FastAPI framework, high performance, easy to learn, fast to code, ready for production
---
**Documentation**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
**Source Code**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. Completion everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (previously known as Swagger) and [JSON Schema](https://json-schema.org/).
* estimation based on tests conducted by an internal development team, building production applications.
## Sponsors
### Keystone Sponsor
### Gold Sponsors
### Silver Sponsors
[Other sponsors](https://fastapi.tiangolo.com/fastapi-people/#sponsors)
## Opinions
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber(ref)
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
## FastAPI Conf
[**FastAPI Conf '26**](https://fastapiconf.com) is happening on **October 28, 2026** in **Amsterdam, NL**. All about FastAPI, right from the source. 🎤
## FastAPI mini documentary
There's a [FastAPI mini documentary](https://www.youtube.com/watch?v=mpR8ngthqiE) released at the end of 2025, you can watch it online:
## **Typer**, the FastAPI of CLIs
If you are building a CLI app to be used in the terminal instead of a web API, check out [**Typer**](https://typer.tiangolo.com/).
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
FastAPI stands on the shoulders of giants:
* [Starlette](https://www.starlette.dev/) for the web parts.
* [Pydantic](https://docs.pydantic.dev/) for the data parts.
## Installation
Create and activate a [virtual environment](https://fastapi.tiangolo.com/virtual-environments/) and then install FastAPI:
**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals.
## Example
### Create it
Create a file `main.py` 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Or use async def...
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about [`async` and `await` in the docs](https://fastapi.tiangolo.com/async/#in-a-hurry).
### Run it
Run the server with:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
About the command fastapi dev...
The command `fastapi dev` reads your `main.py` file automatically, detects the **FastAPI** app in it, and starts a server using [Uvicorn](https://www.uvicorn.dev).
By default, `fastapi dev` will start with auto-reload enabled for local development.
You can read more about it in the [FastAPI CLI docs](https://fastapi.tiangolo.com/fastapi-cli/).
### Check it
Open your browser at [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` operations (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs
Now go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see the automatic interactive API documentation (provided by [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Alternative API docs
And now, go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
You will see the alternative automatic documentation (provided by [ReDoc](https://github.com/Rebilly/ReDoc)):

## Example upgrade
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The `fastapi dev` server should reload automatically.
### Interactive API docs upgrade
Now go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* The interactive API documentation will be automatically updated, including the new body:

* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:

* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:

### Alternative API docs upgrade
And now, go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* The alternative documentation will also reflect the new query parameter and body:

### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* Conversion of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* Conversion of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:

For a more complete example including more features, see the Tutorial - User Guide.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **Dependency Injection** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **GraphQL** integration with [Strawberry](https://strawberry.rocks) and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
### Deploy your app (optional)
You can optionally deploy your FastAPI app to [FastAPI Cloud](https://fastapicloud.com), go and join the waiting list if you haven't. 🚀
If you already have a **FastAPI Cloud** account (we invited you from the waiting list 😉), you can deploy your application with one command.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
That's it! Now you can access your app at that URL. ✨
#### About FastAPI Cloud
**[FastAPI Cloud](https://fastapicloud.com)** is built by the same author and team behind **FastAPI**.
It streamlines the process of **building**, **deploying**, and **accessing** an API with minimal effort.
It brings the same **developer experience** of building apps with FastAPI to **deploying** them to the cloud. 🎉
FastAPI Cloud is the primary sponsor and funding provider for the *FastAPI and friends* open source projects. ✨
#### Deploy to other cloud providers
FastAPI is open source and based on standards. You can deploy FastAPI apps to any cloud provider you choose.
Follow your cloud provider's guides to deploy FastAPI apps with them. 🤓
## Performance
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as [one of the fastest Python frameworks available](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section [Benchmarks](https://fastapi.tiangolo.com/benchmarks/).
## Dependencies
FastAPI depends on Pydantic and Starlette.
### `standard` Dependencies
When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies:
Used by Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - for email validation.
Used by Starlette:
* [`httpx`](https://www.python-httpx.org) - Required if you want to use the `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - Required if you want to use the default template configuration.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - Required if you want to support form "parsing", with `request.form()`.
Used by FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving.
* `fastapi-cli[standard]` - to provide the `fastapi` command.
* This includes `fastapi-cloud-cli`, which allows you to deploy your FastAPI application to [FastAPI Cloud](https://fastapicloud.com).
### Without `standard` Dependencies
If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`.
### Without `fastapi-cloud-cli`
If you want to install FastAPI with the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Additional Optional Dependencies
There are some additional dependencies you might want to install.
Additional optional Pydantic dependencies:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - for settings management.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - for extra types to be used with Pydantic.
Additional optional FastAPI dependencies:
* [`orjson`](https://github.com/ijl/orjson) - Required if you want to use `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - Required if you want to use `UJSONResponse`.
## License
This project is licensed under the terms of the MIT license.
================================================
FILE: SECURITY.md
================================================
# Security Policy
Security is very important for FastAPI and its community. 🔒
Learn more about it below. 👇
## Versions
The latest version of FastAPI is supported.
You are encouraged to [write tests](https://fastapi.tiangolo.com/tutorial/testing/) for your application and update your FastAPI version frequently after ensuring that your tests are passing. This way you will benefit from the latest features, bug fixes, and **security fixes**.
You can learn more about [FastAPI versions and how to pin and upgrade them](https://fastapi.tiangolo.com/deployment/versions/) for your project in the docs.
## Reporting a Vulnerability
If you think you found a vulnerability, and even if you are not sure about it, please report it right away by sending an email to: security@tiangolo.com. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.
I (the author, [@tiangolo](https://x.com/tiangolo)) will review it thoroughly and get back to you.
## Public Discussions
Please restrain from publicly discussing a potential security vulnerability. 🙊
It's better to discuss privately and try to find a solution first, to limit the potential impact as much as possible.
---
Thanks for your help!
The FastAPI community and I thank you for that. 🙇
================================================
FILE: docs/de/docs/_llm-test.md
================================================
# LLM-Testdatei { #llm-test-file }
Dieses Dokument testet, ob das LLM, das die Dokumentation übersetzt, den `general_prompt` in `scripts/translate.py` und den sprachspezifischen Prompt in `docs/{language code}/llm-prompt.md` versteht. Der sprachsspezifische Prompt wird an `general_prompt` angehängt.
Hier hinzugefügte Tests werden von allen Erstellern sprachsspezifischer Prompts gesehen.
So verwenden:
* Einen sprachsspezifischen Prompt haben – `docs/{language code}/llm-prompt.md`.
* Eine frische Übersetzung dieses Dokuments in die gewünschte Zielsprache durchführen (siehe z. B. das Kommando `translate-page` der `translate.py`). Dadurch wird die Übersetzung unter `docs/{language code}/docs/_llm-test.md` erstellt.
* Prüfen Sie, ob in der Übersetzung alles in Ordnung ist.
* Verbessern Sie bei Bedarf Ihren sprachsspezifischen Prompt, den allgemeinen Prompt oder das englische Dokument.
* Beheben Sie anschließend manuell die verbleibenden Probleme in der Übersetzung, sodass es eine gute Übersetzung ist.
* Übersetzen Sie erneut, nachdem die gute Übersetzung vorliegt. Das ideale Ergebnis wäre, dass das LLM an der Übersetzung keine Änderungen mehr vornimmt. Das bedeutet, dass der allgemeine Prompt und Ihr sprachsspezifischer Prompt so gut sind, wie sie sein können (Es wird manchmal ein paar scheinbar zufällige Änderungen machen, der Grund ist, dass [LLMs keine deterministischen Algorithmen sind](https://doublespeak.chat/#/handbook#deterministic-output)).
Die Tests:
## Codeschnipsel { #code-snippets }
//// tab | Test
Dies ist ein Codeschnipsel: `foo`. Und dies ist ein weiteres Codeschnipsel: `bar`. Und noch eins: `baz quux`.
////
//// tab | Info
Der Inhalt von Codeschnipseln sollte unverändert bleiben.
Siehe Abschnitt `### Content of code snippets` im allgemeinen Prompt in `scripts/translate.py`.
////
## Anführungszeichen { #quotes }
//// tab | Test
Gestern schrieb mein Freund: „Wenn man ‚incorrectly‘ korrekt schreibt, hat man es falsch geschrieben“. Worauf ich antwortete: „Korrekt, aber ‚incorrectly‘ ist inkorrekterweise nicht ‚„incorrectly“‘“.
/// note | Hinweis
Das LLM wird dies wahrscheinlich falsch übersetzen. Interessant ist nur, ob es die korrigierte Übersetzung bei einer erneuten Übersetzung beibehält.
///
////
//// tab | Info
Der Promptdesigner kann entscheiden, ob neutrale Anführungszeichen in typografische Anführungszeichen umgewandelt werden sollen. Es ist in Ordnung, sie unverändert zu lassen.
Siehe zum Beispiel den Abschnitt `### Quotes` in `docs/de/llm-prompt.md`.
////
## Anführungszeichen in Codeschnipseln { #quotes-in-code-snippets }
//// tab | Test
`pip install "foo[bar]"`
Beispiele für Stringliterale in Codeschnipseln: `"this"`, `'that'`.
Ein schwieriges Beispiel für Stringliterale in Codeschnipseln: `f"I like {'oranges' if orange else "apples"}"`
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Info
... Allerdings müssen Anführungszeichen in Codeschnipseln unverändert bleiben.
////
## Codeblöcke { #code-blocks }
//// tab | Test
Ein Bash-Codebeispiel ...
```bash
# Eine Begrüßung an das Universum ausgeben
echo "Hello universe"
```
... und ein Konsolen-Codebeispiel ...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
... und noch ein Konsolen-Codebeispiel ...
```console
// Create a directory "Code"
$ mkdir code
// In dieses Verzeichnis wechseln
$ cd code
```
... und ein Python-Codebeispiel ...
```Python
wont_work() # Das wird nicht funktionieren 😱
works(foo="bar") # Das funktioniert 🎉
```
... und das war's.
////
//// tab | Info
Code in Codeblöcken sollte nicht verändert werden, mit Ausnahme von Kommentaren.
Siehe Abschnitt `### Content of code blocks` im allgemeinen Prompt in `scripts/translate.py`.
////
## Tabs und farbige Boxen { #tabs-and-colored-boxes }
//// tab | Test
/// info | Info
Etwas Text
///
/// note | Hinweis
Etwas Text
///
/// note | Technische Details
Etwas Text
///
/// check | Testen
Etwas Text
///
/// tip | Tipp
Etwas Text
///
/// warning | Achtung
Etwas Text
///
/// danger | Gefahr
Etwas Text
///
////
//// tab | Info
Tabs und `Info`/`Note`/`Warning`/usw. Blöcke sollten die Übersetzung ihres Titels nach einem vertikalen Strich (`|`) erhalten.
Siehe die Abschnitte `### Special blocks` und `### Tab blocks` im allgemeinen Prompt in `scripts/translate.py`.
////
## Web- und interne Links { #web-and-internal-links }
//// tab | Test
Der Linktext sollte übersetzt werden, die Linkadresse sollte unverändert bleiben:
* [Link zur Überschrift oben](#code-snippets)
* [Interner Link](index.md#installation)
* [Externer Link](https://sqlmodel.tiangolo.com/)
* [Link zu einem Stil](https://fastapi.tiangolo.com/css/styles.css)
* [Link zu einem Skript](https://fastapi.tiangolo.com/js/logic.js)
* [Link zu einem Bild](https://fastapi.tiangolo.com/img/foo.jpg)
Der Linktext sollte übersetzt werden, die Linkadresse sollte auf die Übersetzung zeigen:
* [FastAPI-Link](https://fastapi.tiangolo.com/de/)
////
//// tab | Info
Links sollten übersetzt werden, aber ihre Adresse soll unverändert bleiben. Eine Ausnahme sind absolute Links zu Seiten der FastAPI-Dokumentation. In diesem Fall sollte auf die Übersetzung verlinkt werden.
Siehe Abschnitt `### Links` im allgemeinen Prompt in `scripts/translate.py`.
////
## HTML-„abbr“-Elemente { #html-abbr-elements }
//// tab | Test
Hier einige Dinge, die in HTML-„abbr“-Elemente gepackt sind (einige sind erfunden):
### Das abbr gibt eine vollständige Phrase { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### Das abbr gibt eine vollständige Phrase und eine Erklärung { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Info
„title“-Attribute von „abbr“-Elementen werden nach bestimmten Anweisungen übersetzt.
Übersetzungen können eigene „abbr“-Elemente hinzufügen, die das LLM nicht entfernen soll. Z. B. um englische Wörter zu erklären.
Siehe Abschnitt `### HTML abbr elements` im allgemeinen Prompt in `scripts/translate.py`.
////
## HTML „dfn“-Elemente { #html-dfn-elements }
* Cluster
* Deep Learning
## Überschriften { #headings }
//// tab | Test
### Eine Webapp entwickeln – ein Tutorial { #develop-a-webapp-a-tutorial }
Hallo.
### Typhinweise und -annotationen { #type-hints-and-annotations }
Hallo wieder.
### Super- und Subklassen { #super-and-subclasses }
Hallo wieder.
////
//// tab | Info
Die einzige strenge Regel für Überschriften ist, dass das LLM den Hash-Teil in geschweiften Klammern unverändert lässt, damit Links nicht kaputtgehen.
Siehe Abschnitt `### Headings` im allgemeinen Prompt in `scripts/translate.py`.
Für einige sprachsspezifische Anweisungen, siehe z. B. den Abschnitt `### Headings` in `docs/de/llm-prompt.md`.
////
## In der Dokumentation verwendete Begriffe { #terms-used-in-the-docs }
//// tab | Test
* Sie
* Ihr
* z. B.
* usw.
* `foo` vom Typ `int`
* `bar` vom Typ `str`
* `baz` vom Typ `list`
* das Tutorial – Benutzerhandbuch
* das Handbuch für fortgeschrittene Benutzer
* die SQLModel-Dokumentation
* die API-Dokumentation
* die automatische Dokumentation
* Data Science
* Deep Learning
* Machine Learning
* Dependency Injection
* HTTP Basic-Authentifizierung
* HTTP Digest
* ISO-Format
* der JSON-Schema-Standard
* das JSON-Schema
* die Schema-Definition
* Password Flow
* Mobile
* deprecatet
* designt
* ungültig
* on the fly
* Standard
* Default
* Groß-/Kleinschreibung ist relevant
* Groß-/Kleinschreibung ist nicht relevant
* die Anwendung bereitstellen
* die Seite ausliefern
* die App
* die Anwendung
* der Request
* die Response
* die Error-Response
* die Pfadoperation
* der Pfadoperation-Dekorator
* die Pfadoperation-Funktion
* der Body
* der Requestbody
* der Responsebody
* der JSON-Body
* der Formularbody
* der Dateibody
* der Funktionskörper
* der Parameter
* der Body-Parameter
* der Pfad-Parameter
* der Query-Parameter
* der Cookie-Parameter
* der Header-Parameter
* der Formular-Parameter
* der Funktionsparameter
* das Event
* das Startup-Event
* das Hochfahren des Servers
* das Shutdown-Event
* das Lifespan-Event
* der Handler
* der Eventhandler
* der Exceptionhandler
* handhaben
* das Modell
* das Pydantic-Modell
* das Datenmodell
* das Datenbankmodell
* das Formularmodell
* das Modellobjekt
* die Klasse
* die Basisklasse
* die Elternklasse
* die Subklasse
* die Kindklasse
* die Geschwisterklasse
* die Klassenmethode
* der Header
* die Header
* der Autorisierungsheader
* der `Authorization`-Header
* der Forwarded-Header
* das Dependency-Injection-System
* die Dependency
* das Dependable
* der Dependant
* I/O-lastig
* CPU-lastig
* Nebenläufigkeit
* Parallelität
* Multiprocessing
* die Umgebungsvariable
* die Umgebungsvariable
* der `PATH`
* die `PATH`-Umgebungsvariable
* die Authentifizierung
* der Authentifizierungsanbieter
* die Autorisierung
* das Anmeldeformular
* der Autorisierungsanbieter
* der Benutzer authentisiert sich
* das System authentifiziert den Benutzer
* Das CLI
* Das Kommandozeileninterface
* der Server
* der Client
* der Cloudanbieter
* der Clouddienst
* die Entwicklung
* die Entwicklungsphasen
* das Dict
* das Dictionary
* die Enumeration
* das Enum
* das Enum-Member
* der Encoder
* der Decoder
* kodieren
* dekodieren
* die Exception
* werfen
* der Ausdruck
* die Anweisung
* das Frontend
* das Backend
* die GitHub-Diskussion
* das GitHub-Issue
* die Leistung
* die Leistungsoptimierung
* der Rückgabetyp
* der Rückgabewert
* die Sicherheit
* das Sicherheitsschema
* der Task
* der Hintergrundtask
* die Taskfunktion
* das Template
* die Template-Engine
* die Typannotation
* der Typhinweis
* der Serverworker
* der Uvicorn-Worker
* der Gunicorn-Worker
* der Workerprozess
* die Workerklasse
* die Workload
* das Deployment
* deployen
* das SDK
* das Software Development Kit
* der `APIRouter`
* die `requirements.txt`
* das Bearer-Token
* der Breaking Change
* der Bug
* der Button
* das Callable
* der Code
* der Commit
* der Contextmanager
* die Coroutine
* die Datenbanksession
* die Festplatte
* die Domain
* die Engine
* das Fake-X
* die HTTP-GET-Methode
* das Item
* die Bibliothek
* der Lifespan
* der Lock
* die Middleware
* die Mobile-Anwendung
* das Modul
* das Mounten
* das Netzwerk
* das Origin
* Die Überschreibung
* die Payload
* der Prozessor
* die Property
* der Proxy
* der Pull Request
* die Query
* der RAM
* der entfernte Rechner
* der Statuscode
* der String
* der Tag
* das Webframework
* die Wildcard
* zurückgeben
* validieren
////
//// tab | Info
Dies ist eine nicht vollständige und nicht normative Liste von (meist) technischen Begriffen, die in der Dokumentation vorkommen. Sie kann dem Promptdesigner helfen herauszufinden, bei welchen Begriffen das LLM Unterstützung braucht. Zum Beispiel, wenn es eine gute Übersetzung immer wieder auf eine suboptimale Übersetzung zurücksetzt. Oder wenn es Probleme hat, einen Begriff in Ihrer Sprache zu konjugieren/deklinieren.
Siehe z. B. den Abschnitt `### List of English terms and their preferred German translations` in `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/de/docs/about/index.md
================================================
# Über { #about }
Über FastAPI, sein Design, seine Inspiration und mehr. 🤓
================================================
FILE: docs/de/docs/advanced/additional-responses.md
================================================
# Zusätzliche Responses in OpenAPI { #additional-responses-in-openapi }
/// warning | Achtung
Dies ist ein eher fortgeschrittenes Thema.
Wenn Sie mit **FastAPI** beginnen, benötigen Sie dies möglicherweise nicht.
///
Sie können zusätzliche Responses mit zusätzlichen Statuscodes, Medientypen, Beschreibungen, usw. deklarieren.
Diese zusätzlichen Responses werden in das OpenAPI-Schema aufgenommen, sodass sie auch in der API-Dokumentation erscheinen.
Für diese zusätzlichen Responses müssen Sie jedoch sicherstellen, dass Sie eine `Response`, wie etwa `JSONResponse`, direkt zurückgeben, mit Ihrem Statuscode und Inhalt.
## Zusätzliche Response mit `model` { #additional-response-with-model }
Sie können Ihren *Pfadoperation-Dekoratoren* einen Parameter `responses` übergeben.
Der nimmt ein `dict` entgegen, die Schlüssel sind Statuscodes für jede Response, wie etwa `200`, und die Werte sind andere `dict`s mit den Informationen für jede Response.
Jedes dieser Response-`dict`s kann einen Schlüssel `model` haben, welcher ein Pydantic-Modell enthält, genau wie `response_model`.
**FastAPI** nimmt dieses Modell, generiert dessen JSON-Schema und fügt es an der richtigen Stelle in OpenAPI ein.
Um beispielsweise eine weitere Response mit dem Statuscode `404` und einem Pydantic-Modell `Message` zu deklarieren, können Sie schreiben:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Hinweis
Beachten Sie, dass Sie die `JSONResponse` direkt zurückgeben müssen.
///
/// info | Info
Der `model`-Schlüssel ist nicht Teil von OpenAPI.
**FastAPI** nimmt das Pydantic-Modell von dort, generiert das JSON-Schema und fügt es an der richtigen Stelle ein.
Die richtige Stelle ist:
* Im Schlüssel `content`, der als Wert ein weiteres JSON-Objekt (`dict`) hat, welches Folgendes enthält:
* Ein Schlüssel mit dem Medientyp, z. B. `application/json`, der als Wert ein weiteres JSON-Objekt hat, welches Folgendes enthält:
* Ein Schlüssel `schema`, der als Wert das JSON-Schema aus dem Modell hat, hier ist die richtige Stelle.
* **FastAPI** fügt hier eine Referenz auf die globalen JSON-Schemas an einer anderen Stelle in Ihrer OpenAPI hinzu, anstatt es direkt einzubinden. Auf diese Weise können andere Anwendungen und Clients diese JSON-Schemas direkt verwenden, bessere Tools zur Codegenerierung bereitstellen, usw.
///
Die generierten Responses in der OpenAPI für diese *Pfadoperation* lauten:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Die Schemas werden von einer anderen Stelle innerhalb des OpenAPI-Schemas referenziert:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Zusätzliche Medientypen für die Haupt-Response { #additional-media-types-for-the-main-response }
Sie können denselben `responses`-Parameter verwenden, um verschiedene Medientypen für dieselbe Haupt-Response hinzuzufügen.
Sie können beispielsweise einen zusätzlichen Medientyp `image/png` hinzufügen und damit deklarieren, dass Ihre *Pfadoperation* ein JSON-Objekt (mit dem Medientyp `application/json`) oder ein PNG-Bild zurückgeben kann:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Hinweis
Beachten Sie, dass Sie das Bild direkt mit einer `FileResponse` zurückgeben müssen.
///
/// info | Info
Sofern Sie in Ihrem Parameter `responses` nicht explizit einen anderen Medientyp angeben, geht FastAPI davon aus, dass die Response denselben Medientyp wie die Haupt-Response-Klasse hat (Standardmäßig `application/json`).
Wenn Sie jedoch eine benutzerdefinierte Response-Klasse mit `None` als Medientyp angegeben haben, verwendet FastAPI `application/json` für jede zusätzliche Response, die über ein zugehöriges Modell verfügt.
///
## Informationen kombinieren { #combining-information }
Sie können auch Response-Informationen von mehreren Stellen kombinieren, einschließlich der Parameter `response_model`, `status_code` und `responses`.
Sie können ein `response_model` deklarieren, indem Sie den Standardstatuscode `200` (oder bei Bedarf einen benutzerdefinierten) verwenden und dann zusätzliche Informationen für dieselbe Response in `responses` direkt im OpenAPI-Schema deklarieren.
**FastAPI** behält die zusätzlichen Informationen aus `responses` und kombiniert sie mit dem JSON-Schema aus Ihrem Modell.
Sie können beispielsweise eine Response mit dem Statuscode `404` deklarieren, die ein Pydantic-Modell verwendet und über eine benutzerdefinierte Beschreibung (`description`) verfügt.
Und eine Response mit dem Statuscode `200`, die Ihr `response_model` verwendet, aber ein benutzerdefiniertes Beispiel (`example`) enthält:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Es wird alles kombiniert und in Ihre OpenAPI eingebunden und in der API-Dokumentation angezeigt:
## Vordefinierte und benutzerdefinierte Responses kombinieren { #combine-predefined-responses-and-custom-ones }
Möglicherweise möchten Sie einige vordefinierte Responses haben, die für viele *Pfadoperationen* gelten, Sie möchten diese jedoch mit benutzerdefinierten Responses kombinieren, die für jede *Pfadoperation* erforderlich sind.
In diesen Fällen können Sie die Python-Technik zum „Entpacken“ eines `dict`s mit `**dict_to_unpack` verwenden:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Hier wird `new_dict` alle Schlüssel-Wert-Paare von `old_dict` plus das neue Schlüssel-Wert-Paar enthalten:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Mit dieser Technik können Sie einige vordefinierte Responses in Ihren *Pfadoperationen* wiederverwenden und sie mit zusätzlichen benutzerdefinierten Responses kombinieren.
Zum Beispiel:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Weitere Informationen zu OpenAPI-Responses { #more-information-about-openapi-responses }
Um zu sehen, was genau Sie in die Responses aufnehmen können, können Sie die folgenden Abschnitte in der OpenAPI-Spezifikation überprüfen:
* [OpenAPI Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), enthält das `Response Object`.
* [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), Sie können alles davon direkt in jede Response innerhalb Ihres `responses`-Parameter einfügen. Einschließlich `description`, `headers`, `content` (darin deklarieren Sie verschiedene Medientypen und JSON-Schemas) und `links`.
================================================
FILE: docs/de/docs/advanced/additional-status-codes.md
================================================
# Zusätzliche Statuscodes { #additional-status-codes }
Standardmäßig liefert **FastAPI** die Responses als `JSONResponse` zurück und fügt den Inhalt, den Sie aus Ihrer *Pfadoperation* zurückgeben, in diese `JSONResponse` ein.
Es wird der Default-Statuscode oder derjenige verwendet, den Sie in Ihrer *Pfadoperation* festgelegt haben.
## Zusätzliche Statuscodes { #additional-status-codes_1 }
Wenn Sie neben dem Hauptstatuscode weitere Statuscodes zurückgeben möchten, können Sie dies tun, indem Sie direkt eine `Response` zurückgeben, wie etwa eine `JSONResponse`, und den zusätzlichen Statuscode direkt festlegen.
Angenommen, Sie möchten eine *Pfadoperation* haben, die das Aktualisieren von Artikeln ermöglicht und bei Erfolg den HTTP-Statuscode 200 „OK“ zurückgibt.
Sie möchten aber auch, dass sie neue Artikel akzeptiert. Und wenn die Artikel vorher nicht vorhanden waren, werden diese Artikel erstellt und der HTTP-Statuscode 201 „Created“ zurückgegeben.
Um dies zu erreichen, importieren Sie `JSONResponse`, und geben Sie Ihren Inhalt direkt zurück, indem Sie den gewünschten `status_code` setzen:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Achtung
Wenn Sie eine `Response` direkt zurückgeben, wie im obigen Beispiel, wird sie direkt zurückgegeben.
Sie wird nicht mit einem Modell usw. serialisiert.
Stellen Sie sicher, dass sie die gewünschten Daten enthält und dass die Werte gültiges JSON sind (wenn Sie `JSONResponse` verwenden).
///
/// note | Technische Details
Sie können auch `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette. Dasselbe gilt für `status`.
///
## OpenAPI- und API-Dokumentation { #openapi-and-api-docs }
Wenn Sie zusätzliche Statuscodes und Responses direkt zurückgeben, werden diese nicht in das OpenAPI-Schema (die API-Dokumentation) aufgenommen, da FastAPI keine Möglichkeit hat, im Voraus zu wissen, was Sie zurückgeben werden.
Sie können das jedoch in Ihrem Code dokumentieren, indem Sie Folgendes verwenden: [Zusätzliche Responses](additional-responses.md).
================================================
FILE: docs/de/docs/advanced/advanced-dependencies.md
================================================
# Fortgeschrittene Abhängigkeiten { #advanced-dependencies }
## Parametrisierte Abhängigkeiten { #parameterized-dependencies }
Alle Abhängigkeiten, die wir bisher gesehen haben, waren festgelegte Funktionen oder Klassen.
Es kann jedoch Fälle geben, in denen Sie Parameter für eine Abhängigkeit festlegen möchten, ohne viele verschiedene Funktionen oder Klassen zu deklarieren.
Stellen wir uns vor, wir möchten eine Abhängigkeit haben, die prüft, ob ein Query-Parameter `q` einen vordefinierten Inhalt hat.
Aber wir wollen diesen vordefinierten Inhalt per Parameter festlegen können.
## Eine „aufrufbare“ Instanz { #a-callable-instance }
In Python gibt es eine Möglichkeit, eine Instanz einer Klasse „aufrufbar“ zu machen.
Nicht die Klasse selbst (die bereits aufrufbar ist), sondern eine Instanz dieser Klasse.
Dazu deklarieren wir eine Methode `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
In diesem Fall ist dieses `__call__` das, was **FastAPI** verwendet, um nach zusätzlichen Parametern und Unterabhängigkeiten zu suchen, und das ist es auch, was später aufgerufen wird, um einen Wert an den Parameter in Ihrer *Pfadoperation-Funktion* zu übergeben.
## Die Instanz parametrisieren { #parameterize-the-instance }
Und jetzt können wir `__init__` verwenden, um die Parameter der Instanz zu deklarieren, die wir zum „Parametrisieren“ der Abhängigkeit verwenden können:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
In diesem Fall wird **FastAPI** `__init__` nie berühren oder sich darum kümmern, wir werden es direkt in unserem Code verwenden.
## Eine Instanz erstellen { #create-an-instance }
Wir könnten eine Instanz dieser Klasse erstellen mit:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Und auf diese Weise können wir unsere Abhängigkeit „parametrisieren“, die jetzt `"bar"` enthält, als das Attribut `checker.fixed_content`.
## Die Instanz als Abhängigkeit verwenden { #use-the-instance-as-a-dependency }
Dann könnten wir diesen `checker` in einem `Depends(checker)` anstelle von `Depends(FixedContentQueryChecker)` verwenden, da die Abhängigkeit die Instanz `checker` und nicht die Klasse selbst ist.
Und beim Auflösen der Abhängigkeit ruft **FastAPI** diesen `checker` wie folgt auf:
```Python
checker(q="somequery")
```
... und übergibt, was immer das als Wert dieser Abhängigkeit in unserer *Pfadoperation-Funktion* zurückgibt, als den Parameter `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Tipp
Das alles mag gekünstelt wirken. Und es ist möglicherweise noch nicht ganz klar, welchen Nutzen das hat.
Diese Beispiele sind bewusst einfach gehalten, zeigen aber, wie alles funktioniert.
In den Kapiteln zum Thema Sicherheit gibt es Hilfsfunktionen, die auf die gleiche Weise implementiert werden.
Wenn Sie das hier alles verstanden haben, wissen Sie bereits, wie diese Sicherheits-Hilfswerkzeuge unter der Haube funktionieren.
///
## Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Achtung
Sie benötigen diese technischen Details höchstwahrscheinlich nicht.
Diese Details sind hauptsächlich nützlich, wenn Sie eine FastAPI-Anwendung haben, die älter als 0.121.0 ist, und Sie auf Probleme mit Abhängigkeiten mit `yield` stoßen.
///
Abhängigkeiten mit `yield` haben sich im Laufe der Zeit weiterentwickelt, um verschiedene Anwendungsfälle abzudecken und einige Probleme zu beheben, hier ist eine Zusammenfassung der Änderungen.
### Abhängigkeiten mit `yield` und `scope` { #dependencies-with-yield-and-scope }
In Version 0.121.0 hat FastAPI Unterstützung für `Depends(scope="function")` für Abhängigkeiten mit `yield` hinzugefügt.
Mit `Depends(scope="function")` wird der Exit-Code nach `yield` direkt nach dem Ende der *Pfadoperation-Funktion* ausgeführt, bevor die Response an den Client gesendet wird.
Und bei Verwendung von `Depends(scope="request")` (dem Default) wird der Exit-Code nach `yield` ausgeführt, nachdem die Response gesendet wurde.
Mehr dazu finden Sie in der Dokumentation zu [Abhängigkeiten mit `yield` – Frühes Beenden und `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Abhängigkeiten mit `yield` und `StreamingResponse`, Technische Details { #dependencies-with-yield-and-streamingresponse-technical-details }
Vor FastAPI 0.118.0 wurde bei Verwendung einer Abhängigkeit mit `yield` der Exit-Code nach der *Pfadoperation-Funktion* ausgeführt, aber unmittelbar bevor die Response gesendet wurde.
Die Absicht war, Ressourcen nicht länger als nötig zu halten, während darauf gewartet wird, dass die Response durchs Netzwerk reist.
Diese Änderung bedeutete auch, dass bei Rückgabe einer `StreamingResponse` der Exit-Code der Abhängigkeit mit `yield` bereits ausgeführt worden wäre.
Wenn Sie beispielsweise eine Datenbanksession in einer Abhängigkeit mit `yield` hatten, konnte die `StreamingResponse` diese Session während des Streamens von Daten nicht verwenden, weil die Session im Exit-Code nach `yield` bereits geschlossen worden wäre.
Dieses Verhalten wurde in 0.118.0 zurückgenommen, sodass der Exit-Code nach `yield` ausgeführt wird, nachdem die Response gesendet wurde.
/// info | Info
Wie Sie unten sehen werden, ähnelt dies sehr dem Verhalten vor Version 0.106.0, jedoch mit mehreren Verbesserungen und Bugfixes für Sonderfälle.
///
#### Anwendungsfälle mit frühem Exit-Code { #use-cases-with-early-exit-code }
Es gibt einige Anwendungsfälle mit spezifischen Bedingungen, die vom alten Verhalten profitieren könnten, den Exit-Code von Abhängigkeiten mit `yield` vor dem Senden der Response auszuführen.
Stellen Sie sich zum Beispiel vor, Sie haben Code, der in einer Abhängigkeit mit `yield` eine Datenbanksession verwendet, nur um einen Benutzer zu verifizieren, die Datenbanksession wird aber in der *Pfadoperation-Funktion* nie wieder verwendet, sondern nur in der Abhängigkeit, und die Response benötigt lange, um gesendet zu werden, wie eine `StreamingResponse`, die Daten langsam sendet, aus irgendeinem Grund aber die Datenbank nicht verwendet.
In diesem Fall würde die Datenbanksession gehalten, bis das Senden der Response abgeschlossen ist, aber wenn Sie sie nicht verwenden, wäre es nicht notwendig, sie zu halten.
So könnte es aussehen:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Der Exit-Code, das automatische Schließen der `Session` in:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
... würde ausgeführt, nachdem die Response das langsame Senden der Daten beendet:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Da `generate_stream()` die Datenbanksession jedoch nicht verwendet, ist es nicht wirklich notwendig, die Session während des Sendens der Response offen zu halten.
Wenn Sie diesen spezifischen Anwendungsfall mit SQLModel (oder SQLAlchemy) haben, könnten Sie die Session explizit schließen, nachdem Sie sie nicht mehr benötigen:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Auf diese Weise würde die Session die Datenbankverbindung freigeben, sodass andere Requests sie verwenden könnten.
Wenn Sie einen anderen Anwendungsfall haben, der ein frühes Beenden aus einer Abhängigkeit mit `yield` benötigt, erstellen Sie bitte eine [GitHub-Diskussion-Frage](https://github.com/fastapi/fastapi/discussions/new?category=questions) mit Ihrem spezifischen Anwendungsfall und warum Sie von einem frühen Schließen für Abhängigkeiten mit `yield` profitieren würden.
Wenn es überzeugende Anwendungsfälle für ein frühes Schließen bei Abhängigkeiten mit `yield` gibt, würde ich erwägen, eine neue Möglichkeit hinzuzufügen, um ein frühes Schließen optional zu aktivieren.
### Abhängigkeiten mit `yield` und `except`, Technische Details { #dependencies-with-yield-and-except-technical-details }
Vor FastAPI 0.110.0 war es so, dass wenn Sie eine Abhängigkeit mit `yield` verwendet und dann in dieser Abhängigkeit mit `except` eine Exception abgefangen haben und die Exception nicht erneut geworfen haben, die Exception automatisch an beliebige Exceptionhandler oder den Handler für interne Serverfehler weitergereicht/weitergeworfen wurde.
Dies wurde in Version 0.110.0 geändert, um unbehandelten Speicherverbrauch durch weitergeleitete Exceptions ohne Handler (interne Serverfehler) zu beheben und um es mit dem Verhalten von normalem Python-Code konsistent zu machen.
### Hintergrundtasks und Abhängigkeiten mit `yield`, Technische Details { #background-tasks-and-dependencies-with-yield-technical-details }
Vor FastAPI 0.106.0 war das Werfen von Exceptions nach `yield` nicht möglich, der Exit-Code in Abhängigkeiten mit `yield` wurde ausgeführt, nachdem die Response gesendet wurde, sodass [Exceptionhandler](../tutorial/handling-errors.md#install-custom-exception-handlers) bereits ausgeführt worden wären.
Dies war so designt, hauptsächlich um die Verwendung derselben von Abhängigkeiten „geyieldeten“ Objekte in Hintergrundtasks zu ermöglichen, da der Exit-Code erst ausgeführt wurde, nachdem die Hintergrundtasks abgeschlossen waren.
Dies wurde in FastAPI 0.106.0 geändert mit der Absicht, keine Ressourcen zu halten, während darauf gewartet wird, dass die Response durchs Netzwerk reist.
/// tip | Tipp
Zusätzlich ist ein Hintergrundtask normalerweise ein unabhängiger Logikblock, der separat gehandhabt werden sollte, mit eigenen Ressourcen (z. B. eigener Datenbankverbindung).
So haben Sie wahrscheinlich saubereren Code.
///
Wenn Sie sich bisher auf dieses Verhalten verlassen haben, sollten Sie jetzt die Ressourcen für Hintergrundtasks innerhalb des Hintergrundtasks selbst erstellen und intern nur Daten verwenden, die nicht von den Ressourcen von Abhängigkeiten mit `yield` abhängen.
Anstatt beispielsweise dieselbe Datenbanksession zu verwenden, würden Sie innerhalb des Hintergrundtasks eine neue Datenbanksession erstellen und die Objekte aus der Datenbank mithilfe dieser neuen Session beziehen. Und anstatt das Objekt aus der Datenbank als Parameter an die Hintergrundtask-Funktion zu übergeben, würden Sie die ID dieses Objekts übergeben und das Objekt dann innerhalb der Hintergrundtask-Funktion erneut beziehen.
================================================
FILE: docs/de/docs/advanced/advanced-python-types.md
================================================
# Fortgeschrittene Python-Typen { #advanced-python-types }
Hier sind einige zusätzliche Ideen, die beim Arbeiten mit Python-Typen nützlich sein könnten.
## `Union` oder `Optional` verwenden { #using-union-or-optional }
Wenn Ihr Code aus irgendeinem Grund nicht `|` verwenden kann, z. B. wenn es nicht in einer Typannotation ist, sondern in etwas wie `response_model=`, können Sie anstelle des senkrechten Strichs (`|`) `Union` aus `typing` verwenden.
Zum Beispiel könnten Sie deklarieren, dass etwas ein `str` oder `None` sein könnte:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` hat außerdem eine Abkürzung, um zu deklarieren, dass etwas `None` sein könnte, mit `Optional`.
Hier ist ein Tipp aus meiner sehr **subjektiven** Perspektive:
* 🚨 Vermeiden Sie die Verwendung von `Optional[SomeType]`
* Verwenden Sie stattdessen ✨ **`Union[SomeType, None]`** ✨.
Beides ist äquivalent und unter der Haube identisch, aber ich würde `Union` statt `Optional` empfehlen, weil das Wort „**optional**“ implizieren könnte, dass der Wert optional ist; tatsächlich bedeutet es jedoch „es kann `None` sein“, selbst wenn es nicht optional ist und weiterhin erforderlich bleibt.
Ich finde, `Union[SomeType, None]` ist expliziter in dem, was es bedeutet.
Es geht nur um Wörter und Namen. Aber diese Wörter können beeinflussen, wie Sie und Ihr Team über den Code denken.
Als Beispiel nehmen wir diese Funktion:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
Der Parameter `name` ist als `Optional[str]` definiert, aber er ist **nicht optional**, Sie können die Funktion nicht ohne den Parameter aufrufen:
```Python
say_hi() # Oh nein, das löst einen Fehler aus! 😱
```
Der Parameter `name` ist **weiterhin erforderlich** (nicht *optional*), weil er keinen Defaultwert hat. Dennoch akzeptiert `name` den Wert `None`:
```Python
say_hi(name=None) # Das funktioniert, None ist gültig 🎉
```
Die gute Nachricht ist: In den meisten Fällen können Sie einfach `|` verwenden, um Unions von Typen zu definieren:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Sie müssen sich also normalerweise keine Gedanken über Namen wie `Optional` und `Union` machen. 😎
================================================
FILE: docs/de/docs/advanced/async-tests.md
================================================
# Asynchrone Tests { #async-tests }
Sie haben bereits gesehen, wie Sie Ihre **FastAPI**-Anwendungen mit dem bereitgestellten `TestClient` testen. Bisher haben Sie nur gesehen, wie man synchrone Tests schreibt, ohne `async`-Funktionen zu verwenden.
Die Möglichkeit, in Ihren Tests asynchrone Funktionen zu verwenden, könnte beispielsweise nützlich sein, wenn Sie Ihre Datenbank asynchron abfragen. Stellen Sie sich vor, Sie möchten das Senden von Requests an Ihre FastAPI-Anwendung testen und dann überprüfen, ob Ihr Backend die richtigen Daten erfolgreich in die Datenbank geschrieben hat, während Sie eine asynchrone Datenbankbibliothek verwenden.
Schauen wir uns an, wie wir das machen können.
## pytest.mark.anyio { #pytest-mark-anyio }
Wenn wir in unseren Tests asynchrone Funktionen aufrufen möchten, müssen unsere Testfunktionen asynchron sein. AnyIO stellt hierfür ein nettes Plugin zur Verfügung, mit dem wir festlegen können, dass einige Testfunktionen asynchron aufgerufen werden sollen.
## HTTPX { #httpx }
Auch wenn Ihre **FastAPI**-Anwendung normale `def`-Funktionen anstelle von `async def` verwendet, handelt es sich darunter immer noch um eine `async`-Anwendung.
Der `TestClient` betreibt unter der Haube etwas Magie, um die asynchrone FastAPI-Anwendung in Ihren normalen `def`-Testfunktionen, mithilfe von Standard-Pytest aufzurufen. Aber diese Magie funktioniert nicht mehr, wenn wir sie in asynchronen Funktionen verwenden. Durch die asynchrone Ausführung unserer Tests können wir den `TestClient` nicht mehr in unseren Testfunktionen verwenden.
Der `TestClient` basiert auf [HTTPX](https://www.python-httpx.org) und glücklicherweise können wir es direkt verwenden, um die API zu testen.
## Beispiel { #example }
Betrachten wir als einfaches Beispiel eine Dateistruktur ähnlich der in [Größere Anwendungen](../tutorial/bigger-applications.md) und [Testen](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Die Datei `main.py` hätte als Inhalt:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
Die Datei `test_main.py` hätte die Tests für `main.py`, das könnte jetzt so aussehen:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Es ausführen { #run-it }
Sie können Ihre Tests wie gewohnt ausführen mit:
```console
$ pytest
---> 100%
```
## Im Detail { #in-detail }
Der Marker `@pytest.mark.anyio` teilt pytest mit, dass diese Testfunktion asynchron aufgerufen werden soll:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Tipp
Beachten Sie, dass die Testfunktion jetzt `async def` ist und nicht nur `def` wie zuvor, wenn Sie den `TestClient` verwenden.
///
Dann können wir einen `AsyncClient` mit der App erstellen und mit `await` asynchrone Requests an ihn senden.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Das ist das Äquivalent zu:
```Python
response = client.get('/')
```
... welches wir verwendet haben, um unsere Requests mit dem `TestClient` zu machen.
/// tip | Tipp
Beachten Sie, dass wir async/await mit dem neuen `AsyncClient` verwenden – der Request ist asynchron.
///
/// warning | Achtung
Falls Ihre Anwendung auf Lifespan-Events angewiesen ist, der `AsyncClient` löst diese Events nicht aus. Um sicherzustellen, dass sie ausgelöst werden, verwenden Sie `LifespanManager` von [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Andere asynchrone Funktionsaufrufe { #other-asynchronous-function-calls }
Da die Testfunktion jetzt asynchron ist, können Sie in Ihren Tests neben dem Senden von Requests an Ihre FastAPI-Anwendung jetzt auch andere `async`-Funktionen aufrufen (und `await`en), genau so, wie Sie diese an anderer Stelle in Ihrem Code aufrufen würden.
/// tip | Tipp
Wenn Sie einen `RuntimeError: Task attached to a different loop` erhalten, wenn Sie asynchrone Funktionsaufrufe in Ihre Tests integrieren (z. B. bei Verwendung von [MongoDBs MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), dann denken Sie daran, Objekte zu instanziieren, die einen Event Loop nur innerhalb asynchroner Funktionen benötigen, z. B. einen `@app.on_event("startup")`-Callback.
///
================================================
FILE: docs/de/docs/advanced/behind-a-proxy.md
================================================
# Hinter einem Proxy { #behind-a-proxy }
In vielen Situationen würden Sie einen **Proxy** wie Traefik oder Nginx vor Ihrer FastAPI-App verwenden.
Diese Proxys könnten HTTPS-Zertifikate und andere Dinge handhaben.
## Proxy-Forwarded-Header { #proxy-forwarded-headers }
Ein **Proxy** vor Ihrer Anwendung würde normalerweise einige Header on-the-fly setzen, bevor er die Requests an den **Server** sendet, um den Server wissen zu lassen, dass der Request vom Proxy **weitergeleitet** wurde, einschließlich der ursprünglichen (öffentlichen) URL, inklusive der Domain, dass HTTPS verwendet wird, usw.
Das **Server**-Programm (z. B. **Uvicorn** via **FastAPI CLI**) ist in der Lage, diese Header zu interpretieren und diese Information dann an Ihre Anwendung weiterzugeben.
Aber aus Sicherheitsgründen, da der Server nicht weiß, dass er hinter einem vertrauenswürdigen Proxy läuft, wird er diese Header nicht interpretieren.
/// note | Technische Details
Die Proxy-Header sind:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Proxy-Forwarded-Header aktivieren { #enable-proxy-forwarded-headers }
Sie können FastAPI CLI mit der *CLI-Option* `--forwarded-allow-ips` starten und die IP-Adressen übergeben, denen vertraut werden soll, um diese Forwarded-Header zu lesen.
Wenn Sie es auf `--forwarded-allow-ips="*"` setzen, würde es allen eingehenden IPs vertrauen.
Wenn Ihr **Server** hinter einem vertrauenswürdigen **Proxy** sitzt und nur der Proxy mit ihm spricht, würde dies dazu führen, dass er die IP dieses **Proxys** akzeptiert, was auch immer sie ist.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Weiterleitungen mit HTTPS { #redirects-with-https }
Angenommen, Sie definieren eine *Pfadoperation* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Wenn der Client versucht, zu `/items` zu gehen, würde er standardmäßig zu `/items/` umgeleitet.
Aber bevor Sie die *CLI-Option* `--forwarded-allow-ips` setzen, könnte er zu `http://localhost:8000/items/` umleiten.
Aber möglicherweise wird Ihre Anwendung unter `https://mysuperapp.com` gehostet, und die Weiterleitung sollte zu `https://mysuperapp.com/items/` erfolgen.
Durch Setzen von `--proxy-headers` kann FastAPI jetzt an den richtigen Ort umleiten. 😎
```
https://mysuperapp.com/items/
```
/// tip | Tipp
Wenn Sie mehr über HTTPS erfahren möchten, lesen Sie den Leitfaden [Über HTTPS](../deployment/https.md).
///
### Wie Proxy-Forwarded-Header funktionieren { #how-proxy-forwarded-headers-work }
Hier ist eine visuelle Darstellung, wie der **Proxy** weitergeleitete Header zwischen dem Client und dem **Anwendungsserver** hinzufügt:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Loadbalancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS-Request Host: mysuperapp.com Pfad: /items
Note over Proxy: Proxy fügt Forwarded-Header hinzu
Proxy->>Server: HTTP-Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Pfad: /items
Note over Server: Server interpretiert die Header (wenn --forwarded-allow-ips gesetzt ist)
Server->>Proxy: HTTP-Response mit correkten HTTPS-URLs
Proxy->>Client: HTTPS-Response
```
Der **Proxy** fängt den ursprünglichen Client-Request ab und fügt die speziellen *Forwarded*-Header (`X-Forwarded-*`) hinzu, bevor er den Request an den **Anwendungsserver** weitergibt.
Diese Header bewahren Informationen über den ursprünglichen Request, die sonst verloren gingen:
* **X-Forwarded-For**: Die ursprüngliche IP-Adresse des Clients
* **X-Forwarded-Proto**: Das ursprüngliche Protokoll (`https`)
* **X-Forwarded-Host**: Der ursprüngliche Host (`mysuperapp.com`)
Wenn **FastAPI CLI** mit `--forwarded-allow-ips` konfiguriert ist, vertraut es diesen Headern und verwendet sie, z. B. um die korrekten URLs in Weiterleitungen zu erzeugen.
## Proxy mit einem abgetrennten Pfadpräfix { #proxy-with-a-stripped-path-prefix }
Sie könnten einen Proxy haben, der Ihrer Anwendung ein Pfadpräfix hinzufügt.
In diesen Fällen können Sie `root_path` verwenden, um Ihre Anwendung zu konfigurieren.
Der `root_path` ist ein Mechanismus, der von der ASGI-Spezifikation bereitgestellt wird (auf der FastAPI via Starlette aufbaut).
Der `root_path` wird verwendet, um diese speziellen Fälle zu handhaben.
Und er wird auch intern beim Mounten von Unteranwendungen verwendet.
Ein Proxy mit einem abgetrennten Pfadpräfix bedeutet in diesem Fall, dass Sie einen Pfad unter `/app` in Ihrem Code deklarieren könnten, dann aber, eine Ebene darüber, den Proxy hinzufügen, der Ihre **FastAPI**-Anwendung unter einem Pfad wie `/api/v1` platziert.
In diesem Fall würde der ursprüngliche Pfad `/app` tatsächlich unter `/api/v1/app` bereitgestellt.
Auch wenn Ihr gesamter Code unter der Annahme geschrieben ist, dass es nur `/app` gibt.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Und der Proxy würde das **Pfadpräfix** on-the-fly **„entfernen“**, bevor er den Request an den Anwendungsserver (wahrscheinlich Uvicorn via FastAPI CLI) übermittelt, dafür sorgend, dass Ihre Anwendung davon überzeugt ist, dass sie unter `/app` bereitgestellt wird, sodass Sie nicht Ihren gesamten Code dahingehend aktualisieren müssen, das Präfix `/api/v1` zu verwenden.
Bis hierher würde alles wie gewohnt funktionieren.
Wenn Sie dann jedoch die Benutzeroberfläche der integrierten Dokumentation (das Frontend) öffnen, wird angenommen, dass sich das OpenAPI-Schema unter `/openapi.json` anstelle von `/api/v1/openapi.json` befindet.
Also würde das Frontend (das im Browser läuft) versuchen, `/openapi.json` zu erreichen und wäre nicht in der Lage, das OpenAPI-Schema abzurufen.
Da wir für unsere Anwendung einen Proxy mit dem Pfadpräfix `/api/v1` haben, muss das Frontend das OpenAPI-Schema unter `/api/v1/openapi.json` abrufen.
```mermaid
graph LR
browser("Browser")
proxy["Proxy auf http://0.0.0.0:9999/api/v1/app"]
server["Server auf http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Tipp
Die IP `0.0.0.0` wird üblicherweise verwendet, um anzudeuten, dass das Programm alle auf diesem Computer/Server verfügbaren IPs abhört.
///
Die Benutzeroberfläche der Dokumentation würde benötigen, dass das OpenAPI-Schema deklariert, dass sich dieser API-`server` unter `/api/v1` (hinter dem Proxy) befindet. Zum Beispiel:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Hier mehr Einstellungen
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Hier mehr Einstellungen
}
}
```
In diesem Beispiel könnte der „Proxy“ etwa **Traefik** sein. Und der Server wäre etwas wie FastAPI CLI mit **Uvicorn**, auf dem Ihre FastAPI-Anwendung ausgeführt wird.
### Bereitstellung des `root_path` { #providing-the-root-path }
Um dies zu erreichen, können Sie die Kommandozeilenoption `--root-path` wie folgt verwenden:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Falls Sie Hypercorn verwenden, das hat auch die Option `--root-path`.
/// note | Technische Details
Die ASGI-Spezifikation definiert einen `root_path` für diesen Anwendungsfall.
Und die Kommandozeilenoption `--root-path` stellt diesen `root_path` bereit.
///
### Testen des aktuellen `root_path` { #checking-the-current-root-path }
Sie können den aktuellen `root_path` abrufen, der von Ihrer Anwendung für jeden Request verwendet wird. Er ist Teil des `scope`-Dictionarys (das ist Teil der ASGI-Spezifikation).
Hier fügen wir ihn, nur zu Demonstrationszwecken, in die Nachricht ein.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Wenn Sie Uvicorn dann starten mit:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
wäre die Response etwa:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Festlegen des `root_path` in der FastAPI-Anwendung { #setting-the-root-path-in-the-fastapi-app }
Falls Sie keine Möglichkeit haben, eine Kommandozeilenoption wie `--root-path` oder ähnlich zu übergeben, können Sie, alternativ dazu, beim Erstellen Ihrer FastAPI-Anwendung den Parameter `root_path` setzen:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Die Übergabe des `root_path` an `FastAPI` wäre das Äquivalent zur Übergabe der `--root-path`-Kommandozeilenoption an Uvicorn oder Hypercorn.
### Über `root_path` { #about-root-path }
Beachten Sie, dass der Server (Uvicorn) diesen `root_path` für nichts anderes verwendet als für die Weitergabe an die Anwendung.
Aber wenn Sie mit Ihrem Browser auf [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) gehen, sehen Sie die normale Response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Es wird also nicht erwartet, dass unter `http://127.0.0.1:8000/api/v1/app` darauf zugegriffen wird.
Uvicorn erwartet, dass der Proxy unter `http://127.0.0.1:8000/app` auf Uvicorn zugreift, und dann liegt es in der Verantwortung des Proxys, das zusätzliche `/api/v1`-Präfix darüber hinzuzufügen.
## Über Proxys mit einem abgetrennten Pfadpräfix { #about-proxies-with-a-stripped-path-prefix }
Bedenken Sie, dass ein Proxy mit abgetrenntem Pfadpräfix nur eine von vielen Konfigurationsmöglichkeiten ist.
Wahrscheinlich wird in vielen Fällen die Standardeinstellung sein, dass der Proxy kein abgetrenntes Pfadpräfix hat.
In einem solchen Fall (ohne ein abgetrenntes Pfadpräfix) würde der Proxy auf etwas wie `https://myawesomeapp.com` lauschen, und wenn der Browser dann zu `https://myawesomeapp.com/api/v1/app` wechselt, und Ihr Server (z. B. Uvicorn) auf `http://127.0.0.1:8000` lauscht, würde der Proxy (ohne ein abgetrenntes Pfadpräfix) über denselben Pfad auf Uvicorn zugreifen: `http://127.0.0.1:8000/api/v1/app`.
## Lokal testen mit Traefik { #testing-locally-with-traefik }
Sie können das Experiment mit einem abgetrennten Pfadpräfix einfach lokal ausführen, indem Sie [Traefik](https://docs.traefik.io/) verwenden.
[Laden Sie Traefik herunter](https://github.com/containous/traefik/releases), es ist eine einzelne Binärdatei, Sie können die komprimierte Datei extrahieren und sie direkt vom Terminal aus ausführen.
Dann erstellen Sie eine Datei `traefik.toml` mit:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Dadurch wird Traefik angewiesen, Port 9999 abzuhören und eine andere Datei `routes.toml` zu verwenden.
/// tip | Tipp
Wir verwenden Port 9999 anstelle des Standard-HTTP-Ports 80, damit Sie ihn nicht mit Administratorrechten (`sudo`) ausführen müssen.
///
Erstellen Sie nun die andere Datei `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Diese Datei konfiguriert Traefik, das Pfadpräfix `/api/v1` zu verwenden.
Und dann leitet Traefik seine Requests an Ihren Uvicorn weiter, der unter `http://127.0.0.1:8000` läuft.
Starten Sie nun Traefik:
Und jetzt starten Sie Ihre Anwendung mit Uvicorn, indem Sie die Option `--root-path` verwenden:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Die Responses testen { #check-the-responses }
Wenn Sie nun zur URL mit dem Port für Uvicorn gehen: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), sehen Sie die normale Response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Tipp
Beachten Sie, dass, obwohl Sie unter `http://127.0.0.1:8000/app` darauf zugreifen, als `root_path` angezeigt wird `/api/v1`, welches aus der Option `--root-path` stammt.
///
Öffnen Sie nun die URL mit dem Port für Traefik, einschließlich des Pfadpräfixes: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Wir bekommen die gleiche Response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Diesmal jedoch unter der URL mit dem vom Proxy bereitgestellten Präfixpfad: `/api/v1`.
Die Idee hier ist natürlich, dass jeder über den Proxy auf die Anwendung zugreifen soll, daher ist die Version mit dem Pfadpräfix `/api/v1` die „korrekte“.
Und die von Uvicorn direkt bereitgestellte Version ohne Pfadpräfix (`http://127.0.0.1:8000/app`) wäre ausschließlich für den Zugriff durch den _Proxy_ (Traefik) bestimmt.
Dies demonstriert, wie der Proxy (Traefik) das Pfadpräfix verwendet und wie der Server (Uvicorn) den `root_path` aus der Option `--root-path` verwendet.
### Es in der Dokumentationsoberfläche testen { #check-the-docs-ui }
Jetzt folgt der spaßige Teil. ✨
Der „offizielle“ Weg, auf die Anwendung zuzugreifen, wäre über den Proxy mit dem von uns definierten Pfadpräfix. Wenn Sie also die von Uvicorn direkt bereitgestellte Dokumentationsoberfläche ohne das Pfadpräfix in der URL ausprobieren, wird es erwartungsgemäß nicht funktionieren, da erwartet wird, dass der Zugriff über den Proxy erfolgt.
Sie können das unter [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) sehen:
Wenn wir jedoch unter der „offiziellen“ URL, über den Proxy mit Port `9999`, unter `/api/v1/docs`, auf die Dokumentationsoberfläche zugreifen, funktioniert es ordnungsgemäß! 🎉
Sie können das unter [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) testen:
Genau so, wie wir es wollten. ✔️
Dies liegt daran, dass FastAPI diesen `root_path` verwendet, um den Default-`server` in OpenAPI mit der von `root_path` bereitgestellten URL zu erstellen.
## Zusätzliche Server { #additional-servers }
/// warning | Achtung
Dies ist ein fortgeschrittener Anwendungsfall. Überspringen Sie das gerne.
///
Standardmäßig erstellt **FastAPI** einen `server` im OpenAPI-Schema mit der URL für den `root_path`.
Sie können aber auch andere alternative `servers` bereitstellen, beispielsweise wenn Sie möchten, dass *dieselbe* Dokumentationsoberfläche mit einer Staging- und Produktionsumgebung interagiert.
Wenn Sie eine benutzerdefinierte Liste von Servern (`servers`) übergeben und es einen `root_path` gibt (da Ihre API hinter einem Proxy läuft), fügt **FastAPI** einen „Server“ mit diesem `root_path` am Anfang der Liste ein.
Zum Beispiel:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Erzeugt ein OpenAPI-Schema, wie:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Hier mehr Einstellungen
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Hier mehr Einstellungen
}
}
```
/// tip | Tipp
Beachten Sie den automatisch generierten Server mit dem `URL`-Wert `/api/v1`, welcher vom `root_path` stammt.
///
In der Dokumentationsoberfläche unter [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) würde es so aussehen:
/// tip | Tipp
Die Dokumentationsoberfläche interagiert mit dem von Ihnen ausgewählten Server.
///
/// note | Technische Details
Die Eigenschaft `servers` in der OpenAPI-Spezifikation ist optional.
Wenn Sie den Parameter `servers` nicht angeben und `root_path` den Wert `/` hat, wird die Eigenschaft `servers` im generierten OpenAPI-Schema standardmäßig vollständig weggelassen, was dem Äquivalent eines einzelnen Servers mit einem `url`-Wert von `/` entspricht.
///
### Den automatischen Server von `root_path` deaktivieren { #disable-automatic-server-from-root-path }
Wenn Sie nicht möchten, dass **FastAPI** einen automatischen Server inkludiert, welcher `root_path` verwendet, können Sie den Parameter `root_path_in_servers=False` verwenden:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
Dann wird er nicht in das OpenAPI-Schema aufgenommen.
## Mounten einer Unteranwendung { #mounting-a-sub-application }
Wenn Sie gleichzeitig eine Unteranwendung mounten (wie beschrieben in [Unteranwendungen – Mounts](sub-applications.md)) und einen Proxy mit `root_path` verwenden wollen, können Sie das normal tun, wie Sie es erwarten würden.
FastAPI verwendet intern den `root_path` auf intelligente Weise, sodass es einfach funktioniert. ✨
================================================
FILE: docs/de/docs/advanced/custom-response.md
================================================
# Benutzerdefinierte Response – HTML, Stream, Datei, andere { #custom-response-html-stream-file-others }
Standardmäßig gibt **FastAPI** JSON-Responses zurück.
Sie können dies überschreiben, indem Sie direkt eine `Response` zurückgeben, wie in [Eine Response direkt zurückgeben](response-directly.md) gezeigt.
Wenn Sie jedoch direkt eine `Response` (oder eine Unterklasse wie `JSONResponse`) zurückgeben, werden die Daten nicht automatisch konvertiert (selbst wenn Sie ein `response_model` deklarieren), und die Dokumentation wird nicht automatisch generiert (zum Beispiel einschließlich des spezifischen „Medientyps“ im HTTP-Header `Content-Type` als Teil der generierten OpenAPI).
Sie können jedoch auch die `Response`, die Sie verwenden möchten (z. B. jede `Response`-Unterklasse), im *Pfadoperation-Dekorator* mit dem `response_class`-Parameter deklarieren.
Der Inhalt, den Sie von Ihrer *Pfadoperation-Funktion* zurückgeben, wird in diese `Response` eingefügt.
/// note | Hinweis
Wenn Sie eine Response-Klasse ohne Medientyp verwenden, erwartet FastAPI, dass Ihre Response keinen Inhalt hat, und dokumentiert daher das Format der Response nicht in deren generierter OpenAPI-Dokumentation.
///
## JSON-Responses { #json-responses }
Standardmäßig gibt FastAPI JSON-Responses zurück.
Wenn Sie ein [Responsemodell](../tutorial/response-model.md) deklarieren, verwendet FastAPI Pydantic, um die Daten zu JSON zu serialisieren.
Wenn Sie kein Responsemodell deklarieren, verwendet FastAPI den `jsonable_encoder`, wie in [JSON-kompatibler Encoder](../tutorial/encoder.md) erklärt, und packt das Ergebnis in eine `JSONResponse`.
Wenn Sie eine `response_class` mit einem JSON-Medientyp (`application/json`) deklarieren, wie es bei `JSONResponse` der Fall ist, werden die von Ihnen zurückgegebenen Daten automatisch mit jedem Pydantic-`response_model` (das Sie im *Pfadoperation-Dekorator* deklariert haben) konvertiert (und gefiltert). Aber die Daten werden nicht mit Pydantic zu JSON-Bytes serialisiert, stattdessen werden sie mit dem `jsonable_encoder` konvertiert und anschließend an die `JSONResponse`-Klasse übergeben, die sie dann mit der Standard-JSON-Bibliothek in Python in Bytes serialisiert.
### JSON-Leistung { #json-performance }
Kurz gesagt: Wenn Sie die maximale Leistung möchten, verwenden Sie ein [Responsemodell](../tutorial/response-model.md) und deklarieren Sie keine `response_class` im *Pfadoperation-Dekorator*.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML-Response { #html-response }
Um eine Response mit HTML direkt von **FastAPI** zurückzugeben, verwenden Sie `HTMLResponse`.
* Importieren Sie `HTMLResponse`.
* Übergeben Sie `HTMLResponse` als den Parameter `response_class` Ihres *Pfadoperation-Dekorators*.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Info
Der Parameter `response_class` wird auch verwendet, um den „Medientyp“ der Response zu definieren.
In diesem Fall wird der HTTP-Header `Content-Type` auf `text/html` gesetzt.
Und er wird als solcher in OpenAPI dokumentiert.
///
### Eine `Response` zurückgeben { #return-a-response }
Wie in [Eine Response direkt zurückgeben](response-directly.md) gezeigt, können Sie die Response auch direkt in Ihrer *Pfadoperation* überschreiben, indem Sie diese zurückgeben.
Das gleiche Beispiel von oben, das eine `HTMLResponse` zurückgibt, könnte so aussehen:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Achtung
Eine `Response`, die direkt von Ihrer *Pfadoperation-Funktion* zurückgegeben wird, wird in OpenAPI nicht dokumentiert (zum Beispiel wird der `Content-Type` nicht dokumentiert) und ist in der automatischen interaktiven Dokumentation nicht sichtbar.
///
/// info | Info
Natürlich stammen der eigentliche `Content-Type`-Header, der Statuscode, usw., aus dem `Response`-Objekt, das Sie zurückgegeben haben.
///
### In OpenAPI dokumentieren und `Response` überschreiben { #document-in-openapi-and-override-response }
Wenn Sie die Response innerhalb der Funktion überschreiben und gleichzeitig den „Medientyp“ in OpenAPI dokumentieren möchten, können Sie den `response_class`-Parameter verwenden UND ein `Response`-Objekt zurückgeben.
Die `response_class` wird dann nur zur Dokumentation der OpenAPI-*Pfadoperation* verwendet, Ihre `Response` wird jedoch unverändert verwendet.
#### Eine `HTMLResponse` direkt zurückgeben { #return-an-htmlresponse-directly }
Es könnte zum Beispiel so etwas sein:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
In diesem Beispiel generiert die Funktion `generate_html_response()` bereits eine `Response` und gibt sie zurück, anstatt das HTML in einem `str` zurückzugeben.
Indem Sie das Ergebnis des Aufrufs von `generate_html_response()` zurückgeben, geben Sie bereits eine `Response` zurück, die das Standardverhalten von **FastAPI** überschreibt.
Aber da Sie die `HTMLResponse` auch in der `response_class` übergeben haben, weiß **FastAPI**, dass sie in OpenAPI und der interaktiven Dokumentation als HTML mit `text/html` zu dokumentieren ist:
## Verfügbare Responses { #available-responses }
Hier sind einige der verfügbaren Responses.
Bedenken Sie, dass Sie `Response` verwenden können, um alles andere zurückzugeben, oder sogar eine benutzerdefinierte Unterklasse zu erstellen.
/// note | Technische Details
Sie können auch `from starlette.responses import HTMLResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
///
### `Response` { #response }
Die Hauptklasse `Response`, alle anderen Responses erben von ihr.
Sie können sie direkt zurückgeben.
Sie akzeptiert die folgenden Parameter:
* `content` – Ein `str` oder `bytes`.
* `status_code` – Ein `int`-HTTP-Statuscode.
* `headers` – Ein `dict` von Strings.
* `media_type` – Ein `str`, der den Medientyp angibt. Z. B. `"text/html"`.
FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem `media_type` basiert, und für Texttypen einen Zeichensatz (charset) anfügen.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben gelesen haben.
### `PlainTextResponse` { #plaintextresponse }
Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Nimmt einige Daten entgegen und gibt eine `application/json`-codierte Response zurück.
Dies ist die Standard-Response, die in **FastAPI** verwendet wird, wie Sie oben gelesen haben.
/// note | Technische Details
Wenn Sie jedoch ein Responsemodell oder einen Rückgabetyp deklarieren, wird dieser direkt verwendet, um die Daten zu JSON zu serialisieren, und eine Response mit dem richtigen Medientyp für JSON wird direkt zurückgegeben, ohne die `JSONResponse`-Klasse zu verwenden.
Dies ist der ideale Weg, um die beste Leistung zu erzielen.
///
### `RedirectResponse` { #redirectresponse }
Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig den Statuscode 307 – Temporäre Weiterleitung (Temporary Redirect).
Sie können eine `RedirectResponse` direkt zurückgeben:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Oder Sie können sie im Parameter `response_class` verwenden:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
In diesem Fall ist der verwendete `status_code` der Standardcode für die `RedirectResponse`, also `307`.
---
Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator (eine Funktion mit `yield`) und streamt den Responsebody.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Technische Details
Ein `async`-Task kann nur abgebrochen werden, wenn er ein `await` erreicht. Wenn es kein `await` gibt, kann der Generator (Funktion mit `yield`) nicht ordnungsgemäß abgebrochen werden und könnte weiterlaufen, selbst nachdem der Abbruch angefordert wurde.
Da dieses kleine Beispiel keine `await`-Anweisungen benötigt, fügen wir ein `await anyio.sleep(0)` hinzu, um dem Event Loop die Chance zu geben, den Abbruch zu verarbeiten.
Dies wäre bei großen oder unendlichen Streams noch wichtiger.
///
/// tip | Tipp
Anstatt eine `StreamingResponse` direkt zurückzugeben, sollten Sie wahrscheinlich dem Stil in [Daten streamen](./stream-data.md) folgen. Das ist wesentlich bequemer und behandelt den Abbruch im Hintergrund für Sie.
Wenn Sie JSON Lines streamen, folgen Sie dem Tutorial [JSON Lines streamen](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Streamt eine Datei asynchron als Response.
Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die anderen Response-Typen:
* `path` – Der Dateipfad zur Datei, die gestreamt werden soll.
* `headers` – Alle benutzerdefinierten Header, die inkludiert werden sollen, als Dictionary.
* `media_type` – Ein String, der den Medientyp angibt. Wenn nicht gesetzt, wird der Dateiname oder Pfad verwendet, um auf einen Medientyp zu schließen.
* `filename` – Wenn gesetzt, wird das in der `Content-Disposition` der Response eingefügt.
Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Sie können auch den Parameter `response_class` verwenden:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
## Benutzerdefinierte Response-Klasse { #custom-response-class }
Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von `Response` erbt und diese verwendet.
Nehmen wir zum Beispiel an, dass Sie [`orjson`](https://github.com/ijl/orjson) mit einigen Einstellungen verwenden möchten.
Sie möchten etwa, dass Ihre Response eingerücktes und formatiertes JSON zurückgibt. Dafür möchten Sie die orjson-Option `orjson.OPT_INDENT_2` verwenden.
Sie könnten eine `CustomORJSONResponse` erstellen. Das Wichtigste, was Sie tun müssen, ist, eine `Response.render(content)`-Methode zu erstellen, die den Inhalt als `bytes` zurückgibt:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Anstatt Folgendes zurückzugeben:
```json
{"message": "Hello World"}
```
... wird diese Response Folgendes zurückgeben:
```json
{
"message": "Hello World"
}
```
Natürlich werden Sie wahrscheinlich viel bessere Möglichkeiten finden, Vorteil daraus zu ziehen, als JSON zu formatieren. 😉
### `orjson` oder Responsemodell { #orjson-or-response-model }
Wenn es Ihnen um Leistung geht, sind Sie wahrscheinlich mit einem [Responsemodell](../tutorial/response-model.md) besser beraten als mit einer `orjson`-Response.
Mit einem Responsemodell verwendet FastAPI Pydantic, um die Daten ohne Zwischenschritte zu JSON zu serialisieren, also ohne sie z. B. erst mit `jsonable_encoder` zu konvertieren, was sonst der Fall wäre.
Und unter der Haube verwendet Pydantic dieselben Rust-Mechanismen wie `orjson`, um nach JSON zu serialisieren. Sie erhalten mit einem Responsemodell also ohnehin die beste Leistung.
## Standard-Response-Klasse { #default-response-class }
Beim Erstellen einer **FastAPI**-Klasseninstanz oder eines `APIRouter`s können Sie angeben, welche Response-Klasse standardmäßig verwendet werden soll.
Der Parameter, der das definiert, ist `default_response_class`.
Im folgenden Beispiel verwendet **FastAPI** standardmäßig `HTMLResponse` in allen *Pfadoperationen*, anstelle von JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Tipp
Sie können dennoch weiterhin `response_class` in *Pfadoperationen* überschreiben, wie bisher.
///
## Zusätzliche Dokumentation { #additional-documentation }
Sie können auch den Medientyp und viele andere Details in OpenAPI mit `responses` deklarieren: [Zusätzliche Responses in OpenAPI](additional-responses.md).
================================================
FILE: docs/de/docs/advanced/dataclasses.md
================================================
# Datenklassen verwenden { #using-dataclasses }
FastAPI basiert auf **Pydantic**, und ich habe Ihnen gezeigt, wie Sie Pydantic-Modelle verwenden können, um Requests und Responses zu deklarieren.
Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von [`dataclasses`](https://docs.python.org/3/library/dataclasses.html):
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Das ist dank **Pydantic** ebenfalls möglich, da es [„`dataclasses` intern unterstützt“](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Auch wenn im obigen Code Pydantic nicht explizit vorkommt, verwendet FastAPI Pydantic, um diese Standard-Datenklassen in Pydantics eigene Variante von Datenklassen zu konvertieren.
Und natürlich wird das gleiche unterstützt:
* Datenvalidierung
* Datenserialisierung
* Datendokumentation, usw.
Das funktioniert genauso wie mit Pydantic-Modellen. Und tatsächlich wird es unter der Haube mittels Pydantic auf die gleiche Weise bewerkstelligt.
/// info | Info
Bedenken Sie, dass Datenklassen nicht alles können, was Pydantic-Modelle können.
Daher müssen Sie möglicherweise weiterhin Pydantic-Modelle verwenden.
Wenn Sie jedoch eine Menge Datenklassen herumliegen haben, ist dies ein guter Trick, um sie für eine Web-API mithilfe von FastAPI zu verwenden. 🤓
///
## Datenklassen in `response_model` { #dataclasses-in-response-model }
Sie können `dataclasses` auch im Parameter `response_model` verwenden:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert.
Auf diese Weise wird deren Schema in der Benutzeroberfläche der API-Dokumentation angezeigt:
## Datenklassen in verschachtelten Datenstrukturen { #dataclasses-in-nested-data-structures }
Sie können `dataclasses` auch mit anderen Typannotationen kombinieren, um verschachtelte Datenstrukturen zu erstellen.
In einigen Fällen müssen Sie möglicherweise immer noch Pydantics Version von `dataclasses` verwenden. Zum Beispiel, wenn Sie Fehler in der automatisch generierten API-Dokumentation haben.
In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.dataclasses` ersetzen, was einen direkten Ersatz darstellt:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Wir importieren `field` weiterhin von Standard-`dataclasses`.
2. `pydantic.dataclasses` ist ein direkter Ersatz für `dataclasses`.
3. Die Datenklasse `Author` enthält eine Liste von `Item`-Datenklassen.
4. Die Datenklasse `Author` wird im `response_model`-Parameter verwendet.
5. Sie können andere Standard-Typannotationen mit Datenklassen als Requestbody verwenden.
In diesem Fall handelt es sich um eine Liste von `Item`-Datenklassen.
6. Hier geben wir ein Dictionary zurück, das `items` enthält, welches eine Liste von Datenklassen ist.
FastAPI ist weiterhin in der Lage, die Daten nach JSON zu Serialisieren.
7. Hier verwendet das `response_model` als Typannotation eine Liste von `Author`-Datenklassen.
Auch hier können Sie `dataclasses` mit Standard-Typannotationen kombinieren.
8. Beachten Sie, dass diese *Pfadoperation-Funktion* reguläres `def` anstelle von `async def` verwendet.
Wie immer können Sie in FastAPI `def` und `async def` beliebig kombinieren.
Wenn Sie eine Auffrischung darüber benötigen, wann welche Anwendung sinnvoll ist, lesen Sie den Abschnitt „In Eile?“ in der Dokumentation zu [`async` und `await`](../async.md#in-a-hurry).
9. Diese *Pfadoperation-Funktion* gibt keine Datenklassen zurück (obwohl dies möglich wäre), sondern eine Liste von Dictionarys mit internen Daten.
FastAPI verwendet den Parameter `response_model` (der Datenklassen enthält), um die Response zu konvertieren.
Sie können `dataclasses` mit anderen Typannotationen auf vielfältige Weise kombinieren, um komplexe Datenstrukturen zu bilden.
Weitere Einzelheiten finden Sie in den Bemerkungen im Quellcode oben.
## Mehr erfahren { #learn-more }
Sie können `dataclasses` auch mit anderen Pydantic-Modellen kombinieren, von ihnen erben, sie in Ihre eigenen Modelle einbinden, usw.
Weitere Informationen finden Sie in der [Pydantic-Dokumentation zu Datenklassen](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Version { #version }
Dies ist verfügbar seit FastAPI-Version `0.67.0`. 🔖
================================================
FILE: docs/de/docs/advanced/events.md
================================================
# Lifespan-Events { #lifespan-events }
Sie können Logik (Code) definieren, die ausgeführt werden soll, bevor die Anwendung **hochfährt**. Dies bedeutet, dass dieser Code **einmal** ausgeführt wird, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**.
Auf die gleiche Weise können Sie Logik (Code) definieren, die ausgeführt werden soll, wenn die Anwendung **heruntergefahren** wird. In diesem Fall wird dieser Code **einmal** ausgeführt, **nachdem** möglicherweise **viele Requests** bearbeitet wurden.
Da dieser Code ausgeführt wird, bevor die Anwendung **beginnt**, Requests entgegenzunehmen, und unmittelbar, nachdem sie die Bearbeitung von Requests **abgeschlossen hat**, deckt er den gesamten Anwendungs-**Lifespan** ab (das Wort „Lifespan“ wird gleich wichtig sein 😉).
Dies kann sehr nützlich sein, um **Ressourcen** einzurichten, die Sie in der gesamten App verwenden wollen und die von Requests **gemeinsam genutzt** werden und/oder die Sie anschließend **aufräumen** müssen. Zum Beispiel ein Pool von Datenbankverbindungen oder das Laden eines gemeinsam genutzten Modells für maschinelles Lernen.
## Anwendungsfall { #use-case }
Beginnen wir mit einem Beispiel-**Anwendungsfall** und schauen uns dann an, wie wir ihn mit dieser Methode implementieren können.
Stellen wir uns vor, Sie verfügen über einige **Modelle für maschinelles Lernen**, die Sie zur Bearbeitung von Requests verwenden möchten. 🤖
Die gleichen Modelle werden von den Requests gemeinsam genutzt, es handelt sich also nicht um ein Modell pro Request, pro Benutzer, oder ähnliches.
Stellen wir uns vor, dass das Laden des Modells **eine ganze Weile dauern** kann, da viele **Daten von der Festplatte** gelesen werden müssen. Sie möchten das also nicht für jeden Request tun.
Sie könnten das auf der obersten Ebene des Moduls/der Datei machen, aber das würde auch bedeuten, dass **das Modell geladen wird**, selbst wenn Sie nur einen einfachen automatisierten Test ausführen, dann wäre dieser Test **langsam**, weil er warten müsste, bis das Modell geladen ist, bevor er einen davon unabhängigen Teil des Codes ausführen könnte.
Das wollen wir besser machen: Laden wir das Modell, bevor die Requests bearbeitet werden, aber unmittelbar bevor die Anwendung beginnt, Requests zu empfangen, und nicht, während der Code geladen wird.
## Lifespan { #lifespan }
Sie können diese Logik beim *Startup* und *Shutdown* mithilfe des `lifespan`-Parameters der `FastAPI`-App und eines „Kontextmanagers“ definieren (ich zeige Ihnen gleich, was das ist).
Beginnen wir mit einem Beispiel und sehen es uns dann im Detail an.
Wir erstellen eine asynchrone Funktion `lifespan()` mit `yield` wie folgt:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Hier simulieren wir den langsamen *Startup*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das Dictionary mit Modellen für maschinelles Lernen einfügen. Dieser Code wird ausgeführt, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**, während des *Startups*.
Und dann, direkt nach dem `yield`, entladen wir das Modell. Dieser Code wird ausgeführt, **nachdem** die Anwendung **die Bearbeitung von Requests abgeschlossen hat**, direkt vor dem *Shutdown*. Dadurch könnten beispielsweise Ressourcen wie Arbeitsspeicher oder eine GPU freigegeben werden.
/// tip | Tipp
Das `shutdown` würde erfolgen, wenn Sie die Anwendung **stoppen**.
Möglicherweise müssen Sie eine neue Version starten, oder Sie haben es einfach satt, sie auszuführen. 🤷
///
### Lifespan-Funktion { #lifespan-function }
Das Erste, was auffällt, ist, dass wir eine asynchrone Funktion mit `yield` definieren. Das ist sehr ähnlich zu Abhängigkeiten mit `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
Der erste Teil der Funktion, vor dem `yield`, wird ausgeführt **bevor** die Anwendung startet.
Und der Teil nach `yield` wird ausgeführt, **nachdem** die Anwendung beendet ist.
### Asynchroner Kontextmanager { #async-context-manager }
Wie Sie sehen, ist die Funktion mit einem `@asynccontextmanager` versehen.
Dadurch wird die Funktion in einen sogenannten „**asynchronen Kontextmanager**“ umgewandelt.
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Ein **Kontextmanager** in Python ist etwas, das Sie in einer `with`-Anweisung verwenden können, zum Beispiel kann `open()` als Kontextmanager verwendet werden:
```Python
with open("file.txt") as file:
file.read()
```
In neueren Versionen von Python gibt es auch einen **asynchronen Kontextmanager**. Sie würden ihn mit `async with` verwenden:
```Python
async with lifespan(app):
await do_stuff()
```
Wenn Sie wie oben einen Kontextmanager oder einen asynchronen Kontextmanager erstellen, führt dieser vor dem Betreten des `with`-Blocks den Code vor dem `yield` aus, und nach dem Verlassen des `with`-Blocks wird er den Code nach dem `yield` ausführen.
In unserem obigen Codebeispiel verwenden wir ihn nicht direkt, sondern übergeben ihn an FastAPI, damit es ihn verwenden kann.
Der Parameter `lifespan` der `FastAPI`-App benötigt einen **asynchronen Kontextmanager**, wir können ihm also unseren neuen asynchronen Kontextmanager `lifespan` übergeben.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Alternative Events (deprecatet) { #alternative-events-deprecated }
/// warning | Achtung
Der empfohlene Weg, den *Startup* und *Shutdown* zu handhaben, ist die Verwendung des `lifespan`-Parameters der `FastAPI`-App, wie oben beschrieben. Wenn Sie einen `lifespan`-Parameter übergeben, werden die `startup`- und `shutdown`-Eventhandler nicht mehr aufgerufen. Es ist entweder alles `lifespan` oder alles Events, nicht beides.
Sie können diesen Teil wahrscheinlich überspringen.
///
Es gibt eine alternative Möglichkeit, diese Logik zu definieren, sodass sie beim *Startup* und beim *Shutdown* ausgeführt wird.
Sie können Eventhandler (Funktionen) definieren, die ausgeführt werden sollen, bevor die Anwendung hochgefahren wird oder wenn die Anwendung heruntergefahren wird.
Diese Funktionen können mit `async def` oder normalem `def` deklariert werden.
### `startup`-Event { #startup-event }
Um eine Funktion hinzuzufügen, die vor dem Start der Anwendung ausgeführt werden soll, deklarieren Sie diese mit dem Event `startup`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
In diesem Fall initialisiert die Eventhandler-Funktion `startup` die „Datenbank“ der Items (nur ein `dict`) mit einigen Werten.
Sie können mehr als eine Eventhandler-Funktion hinzufügen.
Und Ihre Anwendung empfängt erst dann Requests, wenn alle `startup`-Eventhandler abgeschlossen sind.
### `shutdown`-Event { #shutdown-event }
Um eine Funktion hinzuzufügen, die beim Shutdown der Anwendung ausgeführt werden soll, deklarieren Sie sie mit dem Event `shutdown`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Hier schreibt die `shutdown`-Eventhandler-Funktion eine Textzeile `"Application shutdown"` in eine Datei `log.txt`.
/// info | Info
In der Funktion `open()` bedeutet `mode="a"` „append“ („anhängen“), sodass die Zeile nach dem, was sich in dieser Datei befindet, hinzugefügt wird, ohne den vorherigen Inhalt zu überschreiben.
///
/// tip | Tipp
Beachten Sie, dass wir in diesem Fall eine Standard-Python-Funktion `open()` verwenden, die mit einer Datei interagiert.
Es handelt sich also um I/O (Input/Output), welches „Warten“ erfordert, bis Dinge auf die Festplatte geschrieben werden.
Aber `open()` verwendet nicht `async` und `await`.
Daher deklarieren wir die Eventhandler-Funktion mit Standard-`def` statt mit `async def`.
///
### `startup` und `shutdown` zusammen { #startup-and-shutdown-together }
Es besteht eine hohe Wahrscheinlichkeit, dass die Logik für Ihr *Startup* und *Shutdown* miteinander verknüpft ist. Vielleicht möchten Sie etwas beginnen und es dann beenden, eine Ressource laden und sie dann freigeben usw.
Bei getrennten Funktionen, die keine gemeinsame Logik oder Variablen haben, ist dies schwieriger, da Sie Werte in globalen Variablen speichern oder ähnliche Tricks verwenden müssen.
Aus diesem Grund wird jetzt empfohlen, stattdessen `lifespan` wie oben erläutert zu verwenden.
## Technische Details { #technical-details }
Nur ein technisches Detail für die neugierigen Nerds. 🤓
In der technischen ASGI-Spezifikation ist dies Teil des [Lifespan Protokolls](https://asgi.readthedocs.io/en/latest/specs/lifespan.html) und definiert Events namens `startup` und `shutdown`.
/// info | Info
Weitere Informationen zu Starlettes `lifespan`-Handlern finden Sie in [Starlettes Lifespan-Dokumentation](https://www.starlette.dev/lifespan/).
Einschließlich, wie man Lifespan-Zustand handhabt, der in anderen Bereichen Ihres Codes verwendet werden kann.
///
## Unteranwendungen { #sub-applications }
🚨 Beachten Sie, dass diese Lifespan-Events (Startup und Shutdown) nur für die Hauptanwendung ausgeführt werden, nicht für [Unteranwendungen – Mounts](sub-applications.md).
================================================
FILE: docs/de/docs/advanced/generate-clients.md
================================================
# SDKs generieren { #generating-sdks }
Da **FastAPI** auf der **OpenAPI**-Spezifikation basiert, können dessen APIs in einem standardisierten Format beschrieben werden, das viele Tools verstehen.
Dies vereinfacht es, aktuelle **Dokumentation** und Client-Bibliotheken (**SDKs**) in verschiedenen Sprachen zu generieren sowie **Test-** oder **Automatisierungs-Workflows**, die mit Ihrem Code synchron bleiben.
In diesem Leitfaden erfahren Sie, wie Sie ein **TypeScript-SDK** für Ihr FastAPI-Backend generieren.
## Open Source SDK-Generatoren { #open-source-sdk-generators }
Eine vielseitige Möglichkeit ist der [OpenAPI Generator](https://openapi-generator.tech/), der **viele Programmiersprachen** unterstützt und SDKs aus Ihrer OpenAPI-Spezifikation generieren kann.
Für **TypeScript-Clients** ist [Hey API](https://heyapi.dev/) eine speziell entwickelte Lösung, die ein optimiertes Erlebnis für das TypeScript-Ökosystem bietet.
Weitere SDK-Generatoren finden Sie auf [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Tipp
FastAPI generiert automatisch **OpenAPI 3.1**-Spezifikationen, daher muss jedes von Ihnen verwendete Tool diese Version unterstützen.
///
## SDK-Generatoren von FastAPI-Sponsoren { #sdk-generators-from-fastapi-sponsors }
Dieser Abschnitt hebt **venture-unterstützte** und **firmengestützte** Lösungen hervor, die von Unternehmen entwickelt werden, welche FastAPI sponsern. Diese Produkte bieten **zusätzliche Funktionen** und **Integrationen** zusätzlich zu hochwertig generierten SDKs.
Durch das ✨ [**Sponsoring von FastAPI**](../help-fastapi.md#sponsor-the-author) ✨ helfen diese Unternehmen sicherzustellen, dass das Framework und sein **Ökosystem** gesund und **nachhaltig** bleiben.
Ihr Sponsoring zeigt auch ein starkes Engagement für die FastAPI-**Community** (Sie), was bedeutet, dass sie nicht nur einen **großartigen Service** bieten möchten, sondern auch ein **robustes und florierendes Framework**, FastAPI, unterstützen möchten. 🙇
Zum Beispiel könnten Sie ausprobieren:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Einige dieser Lösungen sind möglicherweise auch Open Source oder bieten kostenlose Tarife an, sodass Sie diese ohne finanzielle Verpflichtung ausprobieren können. Andere kommerzielle SDK-Generatoren sind online verfügbar und können dort gefunden werden. 🤓
## Ein TypeScript-SDK erstellen { #create-a-typescript-sdk }
Beginnen wir mit einer einfachen FastAPI-Anwendung:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, die sie für die Request- und Response-Payload verwenden, indem sie die Modelle `Item` und `ResponseMessage` verwenden.
### API-Dokumentation { #api-docs }
Wenn Sie zu `/docs` gehen, sehen Sie, dass es die **Schemas** für die Daten enthält, die in Requests gesendet und in Responses empfangen werden:
Sie können diese Schemas sehen, da sie mit den Modellen in der App deklariert wurden.
Diese Informationen sind im **OpenAPI-Schema** der Anwendung verfügbar und werden in der API-Dokumentation angezeigt.
Diese Informationen aus den Modellen, die in OpenAPI enthalten sind, können verwendet werden, um **den Client-Code zu generieren**.
### Hey API { #hey-api }
Sobald wir eine FastAPI-App mit den Modellen haben, können wir Hey API verwenden, um einen TypeScript-Client zu generieren. Der schnellste Weg das zu tun, ist über npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Dies generiert ein TypeScript-SDK in `./src/client`.
Sie können lernen, wie man [`@hey-api/openapi-ts` installiert](https://heyapi.dev/openapi-ts/get-started) und über die [erzeugte Ausgabe](https://heyapi.dev/openapi-ts/output) auf deren Website lesen.
### Das SDK verwenden { #using-the-sdk }
Jetzt können Sie den Client-Code importieren und verwenden. Er könnte wie folgt aussehen, beachten Sie, dass Sie eine automatische Vervollständigung für die Methoden erhalten:
Sie werden auch eine automatische Vervollständigung für die zu sendende Payload erhalten:
/// tip | Tipp
Beachten Sie die automatische Vervollständigung für `name` und `price`, die in der FastAPI-Anwendung im `Item`-Modell definiert wurden.
///
Sie erhalten Inline-Fehlerberichte für die von Ihnen gesendeten Daten:
Das Response-Objekt hat auch automatische Vervollständigung:
## FastAPI-Anwendung mit Tags { #fastapi-app-with-tags }
In vielen Fällen wird Ihre FastAPI-App größer sein und Sie werden wahrscheinlich Tags verwenden, um verschiedene Gruppen von *Pfadoperationen* zu separieren.
Zum Beispiel könnten Sie einen Abschnitt für **Items** und einen weiteren Abschnitt für **Users** haben, und diese könnten durch Tags getrennt sein:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Einen TypeScript-Client mit Tags generieren { #generate-a-typescript-client-with-tags }
Wenn Sie einen Client für eine FastAPI-App generieren, die Tags verwendet, wird normalerweise der Client-Code auch anhand der Tags getrennt.
Auf diese Weise können Sie die Dinge für den Client-Code richtig ordnen und gruppieren:
In diesem Fall haben Sie:
* `ItemsService`
* `UsersService`
### Client-Methodennamen { #client-method-names }
Im Moment sehen die generierten Methodennamen wie `createItemItemsPost` nicht sehr sauber aus:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
... das liegt daran, dass der Client-Generator für jede *Pfadoperation* die OpenAPI-interne **Operation-ID** verwendet.
OpenAPI erfordert, dass jede Operation-ID innerhalb aller *Pfadoperationen* einzigartig ist. Daher verwendet FastAPI den **Funktionsnamen**, den **Pfad** und die **HTTP-Methode/-Operation**, um diese Operation-ID zu generieren. Denn so kann sichergestellt werden, dass die Operation-IDs einzigartig sind.
Aber ich zeige Ihnen als Nächstes, wie Sie das verbessern können. 🤓
## Benutzerdefinierte Operation-IDs und bessere Methodennamen { #custom-operation-ids-and-better-method-names }
Sie können die Art und Weise, wie diese Operation-IDs **generiert** werden, **ändern**, um sie einfacher zu machen und **einfachere Methodennamen** in den Clients zu haben.
In diesem Fall müssen Sie auf andere Weise sicherstellen, dass jede Operation-ID **einzigartig** ist.
Zum Beispiel könnten Sie sicherstellen, dass jede *Pfadoperation* einen Tag hat, und dann die Operation-ID basierend auf dem **Tag** und dem *Pfadoperation*-**Namen** (dem Funktionsnamen) generieren.
### Eine benutzerdefinierte Funktion zur Erzeugung einer eindeutigen ID erstellen { #custom-generate-unique-id-function }
FastAPI verwendet eine **eindeutige ID** für jede *Pfadoperation*, die für die **Operation-ID** und auch für die Namen aller benötigten benutzerdefinierten Modelle für Requests oder Responses verwendet wird.
Sie können diese Funktion anpassen. Sie nimmt ein `APIRoute` und gibt einen String zurück.
Hier verwendet sie beispielsweise den ersten Tag (Sie werden wahrscheinlich nur einen Tag haben) und den *Pfadoperation*-Namen (den Funktionsnamen).
Anschließend können Sie diese benutzerdefinierte Funktion als `generate_unique_id_function`-Parameter an **FastAPI** übergeben:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Einen TypeScript-Client mit benutzerdefinierten Operation-IDs generieren { #generate-a-typescript-client-with-custom-operation-ids }
Wenn Sie nun den Client erneut generieren, werden Sie feststellen, dass er über die verbesserten Methodennamen verfügt:
Wie Sie sehen, haben die Methodennamen jetzt den Tag und dann den Funktionsnamen, aber keine Informationen aus dem URL-Pfad und der HTTP-Operation.
### Die OpenAPI-Spezifikation für den Client-Generator vorab modifizieren { #preprocess-the-openapi-specification-for-the-client-generator }
Der generierte Code enthält immer noch einige **verdoppelte Informationen**.
Wir wissen bereits, dass diese Methode mit den **Items** zusammenhängt, weil dieses Wort in `ItemsService` enthalten ist (vom Tag übernommen), aber wir haben den Tag-Namen dennoch im Methodennamen vorangestellt. 😕
Wir werden das wahrscheinlich weiterhin für OpenAPI allgemein beibehalten wollen, da dadurch sichergestellt wird, dass die Operation-IDs **einzigartig** sind.
Aber für den generierten Client könnten wir die OpenAPI-Operation-IDs direkt vor der Generierung der Clients **modifizieren**, um diese Methodennamen schöner und **sauberer** zu machen.
Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den präfixierten Tag entfernen**:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Damit würden die Operation-IDs von Dingen wie `items-get_items` in `get_items` umbenannt, sodass der Client-Generator einfachere Methodennamen generieren kann.
### Einen TypeScript-Client mit der vorverarbeiteten OpenAPI generieren { #generate-a-typescript-client-with-the-preprocessed-openapi }
Da das Endergebnis nun in einer `openapi.json`-Datei vorliegt, müssen Sie Ihren Eingabeort aktualisieren:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Nach der Generierung des neuen Clients haben Sie jetzt **saubere Methodennamen**, mit allen **Autovervollständigungen**, **Inline-Fehlerberichten**, usw.:
## Vorteile { #benefits }
Wenn Sie die automatisch generierten Clients verwenden, erhalten Sie **Autovervollständigung** für:
* Methoden.
* Request-Payloads im Body, Query-Parameter, usw.
* Response-Payloads.
Sie erhalten auch **Inline-Fehlerberichte** für alles.
Und wann immer Sie den Backend-Code aktualisieren und **das Frontend neu generieren**, stehen alle neuen *Pfadoperationen* als Methoden zur Verfügung, die alten werden entfernt und alle anderen Änderungen werden im generierten Code reflektiert. 🤓
Das bedeutet auch, dass, wenn sich etwas ändert, dies automatisch im Client-Code **reflektiert** wird. Und wenn Sie den Client **erstellen**, wird eine Fehlermeldung ausgegeben, wenn die verwendeten Daten **nicht übereinstimmen**.
Sie würden also **viele Fehler sehr früh** im Entwicklungszyklus erkennen, anstatt darauf warten zu müssen, dass die Fehler Ihren Endbenutzern in der Produktion angezeigt werden, und dann zu versuchen, zu debuggen, wo das Problem liegt. ✨
================================================
FILE: docs/de/docs/advanced/index.md
================================================
# Handbuch für fortgeschrittene Benutzer { #advanced-user-guide }
## Zusatzfunktionen { #additional-features }
Das Haupt-[Tutorial – Benutzerhandbuch](../tutorial/index.md) sollte ausreichen, um Ihnen einen Überblick über alle Hauptfunktionen von **FastAPI** zu geben.
In den nächsten Abschnitten sehen Sie weitere Optionen, Konfigurationen und zusätzliche Funktionen.
/// tip | Tipp
Die nächsten Abschnitte sind **nicht unbedingt „fortgeschritten“**.
Und es ist möglich, dass für Ihren Anwendungsfall die Lösung in einem davon liegt.
///
## Das Tutorial zuerst lesen { #read-the-tutorial-first }
Sie können immer noch die meisten Funktionen in **FastAPI** mit den Kenntnissen aus dem Haupt-[Tutorial – Benutzerhandbuch](../tutorial/index.md) nutzen.
Und die nächsten Abschnitte setzen voraus, dass Sie es bereits gelesen haben und dass Sie diese Hauptideen kennen.
================================================
FILE: docs/de/docs/advanced/json-base64-bytes.md
================================================
# JSON mit Bytes als base64 { #json-with-bytes-as-base64 }
Wenn Ihre App JSON-Daten empfangen und senden muss, Sie darin aber Binärdaten einschließen müssen, können Sie diese als base64 kodieren.
## Base64 vs Dateien { #base64-vs-files }
Prüfen Sie zunächst, ob Sie [Request Files](../tutorial/request-files.md) zum Hochladen von Binärdaten und [Benutzerdefinierte Response – FileResponse](./custom-response.md#fileresponse--fileresponse-) zum Senden von Binärdaten verwenden können, anstatt sie in JSON zu kodieren.
JSON kann nur UTF-8-kodierte Strings enthalten, es kann daher keine rohen Bytes enthalten.
Base64 kann Binärdaten in Strings kodieren, dafür werden jedoch mehr Zeichen benötigt als in den ursprünglichen Binärdaten; es ist daher in der Regel weniger effizient als der Umgang mit normalen Dateien.
Verwenden Sie base64 nur, wenn Sie Binärdaten unbedingt in JSON einbetten müssen und dafür keine Dateien verwenden können.
## Pydantic `bytes` { #pydantic-bytes }
Sie können ein Pydantic-Modell mit `bytes`-Feldern deklarieren und dann in der Modellkonfiguration `val_json_bytes` verwenden, um anzugeben, dass zur *Validierung* von eingehenden JSON-Daten base64 genutzt werden soll; im Rahmen dieser Validierung wird der base64-String in Bytes dekodiert.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Wenn Sie die `/docs` aufrufen, zeigt die Dokumentation, dass das Feld `data` base64-kodierte Bytes erwartet:
Sie könnten einen Request wie folgt senden:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Tipp
`aGVsbG8=` ist die base64-Kodierung von `hello`.
///
Anschließend dekodiert Pydantic den base64-String und stellt Ihnen die ursprünglichen Bytes im Feld `data` des Modells bereit.
Sie erhalten eine Response wie:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` für Ausgabedaten { #pydantic-bytes-for-output-data }
Sie können in der Modellkonfiguration für Ausgabedaten auch `bytes`-Felder mit `ser_json_bytes` verwenden; Pydantic wird die Bytes bei der Erzeugung der JSON-Response als base64 *serialisieren*.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` für Eingabe- und Ausgabedaten { #pydantic-bytes-for-input-and-output-data }
Und selbstverständlich können Sie dasselbe Modell so konfigurieren, dass base64 sowohl für Eingaben (*validieren*) mit `val_json_bytes` als auch für Ausgaben (*serialisieren*) mit `ser_json_bytes` verwendet wird, wenn JSON-Daten empfangen und gesendet werden.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/de/docs/advanced/middleware.md
================================================
# Fortgeschrittene Middleware { #advanced-middleware }
Im Haupttutorial haben Sie gelesen, wie Sie Ihrer Anwendung [benutzerdefinierte Middleware](../tutorial/middleware.md) hinzufügen können.
Und dann auch, wie man [CORS mittels der `CORSMiddleware`](../tutorial/cors.md) handhabt.
In diesem Abschnitt werden wir sehen, wie man andere Middlewares verwendet.
## ASGI-Middleware hinzufügen { #adding-asgi-middlewares }
Da **FastAPI** auf Starlette basiert und die ASGI-Spezifikation implementiert, können Sie jede ASGI-Middleware verwenden.
Eine Middleware muss nicht speziell für FastAPI oder Starlette gemacht sein, um zu funktionieren, solange sie der ASGI-Spezifikation genügt.
Im Allgemeinen handelt es sich bei ASGI-Middleware um Klassen, die als erstes Argument eine ASGI-Anwendung erwarten.
In der Dokumentation für ASGI-Middlewares von Drittanbietern wird Ihnen wahrscheinlich gesagt, dass Sie etwa Folgendes tun sollen:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Aber FastAPI (eigentlich Starlette) bietet eine einfachere Möglichkeit, welche sicherstellt, dass die internen Middlewares zur Behandlung von Serverfehlern und benutzerdefinierten Exceptionhandlern ordnungsgemäß funktionieren.
Dazu verwenden Sie `app.add_middleware()` (wie schon im Beispiel für CORS gesehen).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` empfängt eine Middleware-Klasse als erstes Argument und dann alle weiteren Argumente, die an die Middleware übergeben werden sollen.
## Integrierte Middleware { #integrated-middlewares }
**FastAPI** enthält mehrere Middlewares für gängige Anwendungsfälle. Wir werden als Nächstes sehen, wie man sie verwendet.
/// note | Technische Details
Für die nächsten Beispiele könnten Sie auch `from starlette.middleware.something import SomethingMiddleware` verwenden.
**FastAPI** bietet mehrere Middlewares via `fastapi.middleware` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Middlewares kommen aber direkt von Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Erzwingt, dass alle eingehenden Requests entweder `https` oder `wss` sein müssen.
Alle eingehenden Requests an `http` oder `ws` werden stattdessen an das sichere Schema umgeleitet.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Erzwingt, dass alle eingehenden Requests einen korrekt gesetzten `Host`-Header haben, um sich vor HTTP-Host-Header-Angriffen zu schützen.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Die folgenden Argumente werden unterstützt:
* `allowed_hosts` – Eine Liste von Domain-Namen, die als Hostnamen zulässig sein sollten. Wildcard-Domains wie `*.example.com` werden unterstützt, um Subdomains zu matchen. Um jeden Hostnamen zu erlauben, verwenden Sie entweder `allowed_hosts=["*"]` oder lassen Sie diese Middleware weg.
* `www_redirect` – Wenn auf True gesetzt, werden Requests an Nicht-www-Versionen der erlaubten Hosts zu deren www-Gegenstücken umgeleitet. Der Defaultwert ist `True`.
Wenn ein eingehender Request nicht korrekt validiert wird, wird eine `400`-Response gesendet.
## `GZipMiddleware` { #gzipmiddleware }
Verarbeitet GZip-Responses für alle Requests, die „gzip“ im `Accept-Encoding`-Header enthalten.
Diese Middleware verarbeitet sowohl Standard- als auch Streaming-Responses.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Die folgenden Argumente werden unterstützt:
* `minimum_size` – Responses, die kleiner als diese Mindestgröße in Bytes sind, nicht per GZip komprimieren. Der Defaultwert ist `500`.
* `compresslevel` – Wird während der GZip-Kompression verwendet. Es ist ein Ganzzahlwert zwischen 1 und 9. Der Defaultwert ist `9`. Ein niedrigerer Wert resultiert in schnellerer Kompression, aber größeren Dateigrößen, während ein höherer Wert langsamere Kompression, aber kleinere Dateigrößen zur Folge hat.
## Andere Middlewares { #other-middlewares }
Es gibt viele andere ASGI-Middlewares.
Zum Beispiel:
* [Uvicorns `ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Um mehr über weitere verfügbare Middlewares herauszufinden, besuchen Sie [Starlettes Middleware-Dokumentation](https://www.starlette.dev/middleware/) und die [ASGI Awesome List](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/de/docs/advanced/openapi-callbacks.md
================================================
# OpenAPI-Callbacks { #openapi-callbacks }
Sie könnten eine API mit einer *Pfadoperation* erstellen, die einen Request an eine *externe API* auslösen könnte, welche von jemand anderem erstellt wurde (wahrscheinlich derselbe Entwickler, der Ihre API *verwenden* würde).
Der Vorgang, der stattfindet, wenn Ihre API-Anwendung die *externe API* aufruft, wird als „Callback“ bezeichnet. Denn die Software, die der externe Entwickler geschrieben hat, sendet einen Request an Ihre API und dann *ruft Ihre API zurück* (*calls back*) und sendet einen Request an eine *externe API* (die wahrscheinlich vom selben Entwickler erstellt wurde).
In diesem Fall möchten Sie möglicherweise dokumentieren, wie diese externe API aussehen *sollte*. Welche *Pfadoperation* sie haben sollte, welchen Body sie erwarten sollte, welche Response sie zurückgeben sollte, usw.
## Eine Anwendung mit Callbacks { #an-app-with-callbacks }
Sehen wir uns das alles anhand eines Beispiels an.
Stellen Sie sich vor, Sie entwickeln eine Anwendung, mit der Sie Rechnungen erstellen können.
Diese Rechnungen haben eine `id`, einen optionalen `title`, einen `customer` (Kunde) und ein `total` (Gesamtsumme).
Der Benutzer Ihrer API (ein externer Entwickler) erstellt mit einem POST-Request eine Rechnung in Ihrer API.
Dann wird Ihre API (stellen wir uns vor):
* die Rechnung an einen Kunden des externen Entwicklers senden.
* das Geld einsammeln.
* eine Benachrichtigung an den API-Benutzer (den externen Entwickler) zurücksenden.
* Dies erfolgt durch Senden eines POST-Requests (von *Ihrer API*) an eine *externe API*, die von diesem externen Entwickler bereitgestellt wird (das ist der „Callback“).
## Die normale **FastAPI**-Anwendung { #the-normal-fastapi-app }
Sehen wir uns zunächst an, wie die normale API-Anwendung aussehen würde, bevor wir den Callback hinzufügen.
Sie verfügt über eine *Pfadoperation*, die einen `Invoice`-Body empfängt, und einen Query-Parameter `callback_url`, der die URL für den Callback enthält.
Dieser Teil ist ziemlich normal, der größte Teil des Codes ist Ihnen wahrscheinlich bereits bekannt:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Tipp
Der Query-Parameter `callback_url` verwendet einen Pydantic-[Url](https://docs.pydantic.dev/latest/api/networks/)-Typ.
///
Das einzig Neue ist `callbacks=invoices_callback_router.routes` als Argument für den *Pfadoperation-Dekorator*. Wir werden als Nächstes sehen, was das ist.
## Dokumentation des Callbacks { #documenting-the-callback }
Der tatsächliche Callback-Code hängt stark von Ihrer eigenen API-Anwendung ab.
Und er wird wahrscheinlich von Anwendung zu Anwendung sehr unterschiedlich sein.
Es könnten nur eine oder zwei Codezeilen sein, wie zum Beispiel:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Der möglicherweise wichtigste Teil des Callbacks besteht jedoch darin, sicherzustellen, dass Ihr API-Benutzer (der externe Entwickler) die *externe API* korrekt implementiert, gemäß den Daten, die *Ihre API* im Requestbody des Callbacks senden wird, usw.
Als Nächstes fügen wir den Code hinzu, um zu dokumentieren, wie diese *externe API* aussehen sollte, um den Callback von *Ihrer API* zu empfangen.
Diese Dokumentation wird in der Swagger-Oberfläche unter `/docs` in Ihrer API angezeigt und zeigt externen Entwicklern, wie diese die *externe API* erstellen sollten.
In diesem Beispiel wird nicht der Callback selbst implementiert (das könnte nur eine Codezeile sein), sondern nur der Dokumentationsteil.
/// tip | Tipp
Der eigentliche Callback ist nur ein HTTP-Request.
Wenn Sie den Callback selbst implementieren, können Sie beispielsweise [HTTPX](https://www.python-httpx.org) oder [Requests](https://requests.readthedocs.io/) verwenden.
///
## Schreiben des Codes, der den Callback dokumentiert { #write-the-callback-documentation-code }
Dieser Code wird nicht in Ihrer Anwendung ausgeführt, wir benötigen ihn nur, um zu *dokumentieren*, wie diese *externe API* aussehen soll.
Sie wissen jedoch bereits, wie Sie mit **FastAPI** ganz einfach eine automatische Dokumentation für eine API erstellen.
Daher werden wir dasselbe Wissen nutzen, um zu dokumentieren, wie die *externe API* aussehen sollte ... indem wir die *Pfadoperation(en)* erstellen, welche die externe API implementieren soll (die, welche Ihre API aufruft).
/// tip | Tipp
Wenn Sie den Code zum Dokumentieren eines Callbacks schreiben, kann es hilfreich sein, sich vorzustellen, dass Sie dieser *externe Entwickler* sind. Und dass Sie derzeit die *externe API* implementieren, nicht *Ihre API*.
Wenn Sie diese Sichtweise (des *externen Entwicklers*) vorübergehend übernehmen, wird es offensichtlicher, wo die Parameter, das Pydantic-Modell für den Body, die Response, usw. für diese *externe API* hingehören.
///
### Einen Callback-`APIRouter` erstellen { #create-a-callback-apirouter }
Erstellen Sie zunächst einen neuen `APIRouter`, der einen oder mehrere Callbacks enthält.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Die Callback-*Pfadoperation* erstellen { #create-the-callback-path-operation }
Um die Callback-*Pfadoperation* zu erstellen, verwenden Sie denselben `APIRouter`, den Sie oben erstellt haben.
Sie sollte wie eine normale FastAPI-*Pfadoperation* aussehen:
* Sie sollte wahrscheinlich eine Deklaration des Bodys enthalten, die sie erhalten soll, z. B. `body: InvoiceEvent`.
* Und sie könnte auch eine Deklaration der Response enthalten, die zurückgegeben werden soll, z. B. `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Es gibt zwei Hauptunterschiede zu einer normalen *Pfadoperation*:
* Es muss kein tatsächlicher Code vorhanden sein, da Ihre Anwendung diesen Code niemals aufruft. Sie wird nur zur Dokumentation der *externen API* verwendet. Die Funktion könnte also einfach `pass` enthalten.
* Der *Pfad* kann einen [OpenAPI-3-Ausdruck](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) enthalten (mehr dazu weiter unten), wo er Variablen mit Parametern und Teilen des ursprünglichen Requests verwenden kann, der an *Ihre API* gesendet wurde.
### Der Callback-Pfadausdruck { #the-callback-path-expression }
Der Callback-*Pfad* kann einen [OpenAPI-3-Ausdruck](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) enthalten, welcher Teile des ursprünglichen Requests enthalten kann, der an *Ihre API* gesendet wurde.
In diesem Fall ist es der `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Wenn Ihr API-Benutzer (der externe Entwickler) also einen Request an *Ihre API* sendet, via:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
mit einem JSON-Körper:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
dann verarbeitet *Ihre API* die Rechnung und sendet irgendwann später einen Callback-Request an die `callback_url` (die *externe API*):
```
https://www.external.org/events/invoices/2expen51ve
```
mit einem JSON-Body, der etwa Folgendes enthält:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
und sie würde eine Response von dieser *externen API* mit einem JSON-Body wie dem folgenden erwarten:
```JSON
{
"ok": true
}
```
/// tip | Tipp
Beachten Sie, dass die verwendete Callback-URL die URL enthält, die als Query-Parameter in `callback_url` (`https://www.external.org/events`) empfangen wurde, und auch die Rechnungs-`id` aus dem JSON-Body (`2expen51ve`).
///
### Den Callback-Router hinzufügen { #add-the-callback-router }
An diesem Punkt haben Sie die benötigte(n) *Callback-Pfadoperation(en)* (diejenige(n), die der *externe Entwickler* in der *externen API* implementieren sollte) im Callback-Router, den Sie oben erstellt haben.
Verwenden Sie nun den Parameter `callbacks` im *Pfadoperation-Dekorator Ihrer API*, um das Attribut `.routes` (das ist eigentlich nur eine `list`e von Routen/*Pfadoperationen*) dieses Callback-Routers zu übergeben:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Tipp
Beachten Sie, dass Sie nicht den Router selbst (`invoices_callback_router`) an `callback=` übergeben, sondern das Attribut `.routes`, wie in `invoices_callback_router.routes`.
///
### Es in der Dokumentation testen { #check-the-docs }
Jetzt können Sie Ihre Anwendung starten und auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) gehen.
Sie sehen Ihre Dokumentation, einschließlich eines Abschnitts „Callbacks“ für Ihre *Pfadoperation*, der zeigt, wie die *externe API* aussehen sollte:
================================================
FILE: docs/de/docs/advanced/openapi-webhooks.md
================================================
# OpenAPI Webhooks { #openapi-webhooks }
Es gibt Fälle, in denen Sie Ihren API-**Benutzern** mitteilen möchten, dass Ihre App *deren* App mit einigen Daten aufrufen (einen Request senden) könnte, normalerweise um über ein bestimmtes **Event** zu **benachrichtigen**.
Das bedeutet, dass anstelle des normalen Prozesses, bei dem Ihre Benutzer Requests an Ihre API senden, **Ihre API** (oder Ihre App) **Requests an deren System** (an deren API, deren App) senden könnte.
Das wird normalerweise als **Webhook** bezeichnet.
## Webhooks-Schritte { #webhooks-steps }
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Requestbody**.
Sie definieren auch auf irgendeine Weise, in welchen **Momenten** Ihre App diese Requests oder Events senden wird.
Und **Ihre Benutzer** definieren auf irgendeine Weise (zum Beispiel irgendwo in einem Web-Dashboard) die **URL**, an die Ihre App diese Requests senden soll.
Die gesamte **Logik** zur Registrierung der URLs für Webhooks und der Code zum tatsächlichen Senden dieser Requests liegt bei Ihnen. Sie schreiben es so, wie Sie möchten, in **Ihrem eigenen Code**.
## Webhooks mit **FastAPI** und OpenAPI dokumentieren { #documenting-webhooks-with-fastapi-and-openapi }
Mit **FastAPI**, mithilfe von OpenAPI, können Sie die Namen dieser Webhooks, die Arten von HTTP-Operationen, die Ihre App senden kann (z. B. `POST`, `PUT`, usw.) und die **Requestbodys** definieren, die Ihre App senden würde.
Dies kann es Ihren Benutzern viel einfacher machen, **deren APIs zu implementieren**, um Ihre **Webhook**-Requests zu empfangen. Möglicherweise können diese sogar einen Teil ihres eigenen API-Codes automatisch generieren.
/// info | Info
Webhooks sind in OpenAPI 3.1.0 und höher verfügbar und werden von FastAPI `0.99.0` und höher unterstützt.
///
## Eine App mit Webhooks { #an-app-with-webhooks }
Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, das Sie verwenden können, um *Webhooks* zu definieren, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Die von Ihnen definierten Webhooks landen im **OpenAPI**-Schema und der automatischen **Dokumentations-Oberfläche**.
/// info | Info
Das `app.webhooks`-Objekt ist eigentlich nur ein `APIRouter`, derselbe Typ, den Sie verwenden würden, wenn Sie Ihre App mit mehreren Dateien strukturieren.
///
Beachten Sie, dass Sie bei Webhooks tatsächlich keinen *Pfad* (wie `/items/`) deklarieren, der Text, den Sie dort übergeben, ist lediglich eine **Kennzeichnung** des Webhooks (der Name des Events). Zum Beispiel ist in `@app.webhooks.post("new-subscription")` der Webhook-Name `new-subscription`.
Das liegt daran, dass erwartet wird, dass **Ihre Benutzer** den tatsächlichen **URL-Pfad**, an dem sie den Webhook-Request empfangen möchten, auf andere Weise definieren (z. B. über ein Web-Dashboard).
### Die Dokumentation testen { #check-the-docs }
Jetzt können Sie Ihre App starten und auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) gehen.
Sie werden sehen, dass Ihre Dokumentation die normalen *Pfadoperationen* und jetzt auch einige **Webhooks** enthält:
================================================
FILE: docs/de/docs/advanced/path-operation-advanced-configuration.md
================================================
# Fortgeschrittene Konfiguration der Pfadoperation { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | Achtung
Wenn Sie kein „Experte“ für OpenAPI sind, brauchen Sie dies wahrscheinlich nicht.
///
Mit dem Parameter `operation_id` können Sie die OpenAPI `operationId` festlegen, die in Ihrer *Pfadoperation* verwendet werden soll.
Sie müssten sicherstellen, dass sie für jede Operation eindeutig ist.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Verwendung des Namens der *Pfadoperation-Funktion* als operationId { #using-the-path-operation-function-name-as-the-operationid }
Wenn Sie die Funktionsnamen Ihrer API als `operationId`s verwenden möchten, können Sie über alle iterieren und die `operation_id` jeder *Pfadoperation* mit deren `APIRoute.name` überschreiben.
Sie sollten dies tun, nachdem Sie alle Ihre *Pfadoperationen* hinzugefügt haben.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Tipp
Wenn Sie `app.openapi()` manuell aufrufen, sollten Sie vorher die `operationId`s aktualisiert haben.
///
/// warning | Achtung
Wenn Sie dies tun, müssen Sie sicherstellen, dass jede Ihrer *Pfadoperation-Funktionen* einen eindeutigen Namen hat.
Auch wenn diese sich in unterschiedlichen Modulen (Python-Dateien) befinden.
///
## Von OpenAPI ausschließen { #exclude-from-openapi }
Um eine *Pfadoperation* aus dem generierten OpenAPI-Schema (und damit aus den automatischen Dokumentationssystemen) auszuschließen, verwenden Sie den Parameter `include_in_schema` und setzen Sie ihn auf `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Fortgeschrittene Beschreibung mittels Docstring { #advanced-description-from-docstring }
Sie können die verwendeten Zeilen aus dem Docstring einer *Pfadoperation-Funktion* einschränken, die für OpenAPI verwendet werden.
Das Hinzufügen eines `\f` (ein maskiertes „Form Feed“-Zeichen) führt dazu, dass **FastAPI** die für OpenAPI verwendete Ausgabe an dieser Stelle abschneidet.
Sie wird nicht in der Dokumentation angezeigt, aber andere Tools (wie z. B. Sphinx) können den Rest verwenden.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Zusätzliche Responses { #additional-responses }
Sie haben wahrscheinlich gesehen, wie man das `response_model` und den `status_code` für eine *Pfadoperation* deklariert.
Das definiert die Metadaten der Haupt-Response einer *Pfadoperation*.
Sie können auch zusätzliche Responses mit deren Modellen, Statuscodes usw. deklarieren.
Es gibt hier in der Dokumentation ein ganzes Kapitel darüber, Sie können es unter [Zusätzliche Responses in OpenAPI](additional-responses.md) lesen.
## OpenAPI-Extra { #openapi-extra }
Wenn Sie in Ihrer Anwendung eine *Pfadoperation* deklarieren, generiert **FastAPI** automatisch die relevanten Metadaten dieser *Pfadoperation*, die in das OpenAPI-Schema aufgenommen werden sollen.
/// note | Technische Details
In der OpenAPI-Spezifikation wird es das [Operationsobjekt](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object) genannt.
///
Es hat alle Informationen zur *Pfadoperation* und wird zur Erstellung der automatischen Dokumentation verwendet.
Es enthält `tags`, `parameters`, `requestBody`, `responses`, usw.
Dieses *Pfadoperation*-spezifische OpenAPI-Schema wird normalerweise automatisch von **FastAPI** generiert, Sie können es aber auch erweitern.
/// tip | Tipp
Dies ist ein Low-Level-Erweiterungspunkt.
Wenn Sie nur zusätzliche Responses deklarieren müssen, können Sie dies bequemer mit [Zusätzliche Responses in OpenAPI](additional-responses.md) tun.
///
Sie können das OpenAPI-Schema für eine *Pfadoperation* erweitern, indem Sie den Parameter `openapi_extra` verwenden.
### OpenAPI-Erweiterungen { #openapi-extensions }
Dieses `openapi_extra` kann beispielsweise hilfreich sein, um [OpenAPI-Erweiterungen](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) zu deklarieren:
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Wenn Sie die automatische API-Dokumentation öffnen, wird Ihre Erweiterung am Ende der spezifischen *Pfadoperation* angezeigt.
Und wenn Sie die resultierende OpenAPI sehen (unter `/openapi.json` in Ihrer API), sehen Sie Ihre Erweiterung auch als Teil der spezifischen *Pfadoperation*:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Benutzerdefiniertes OpenAPI-*Pfadoperation*-Schema { #custom-openapi-path-operation-schema }
Das Dictionary in `openapi_extra` wird mit dem automatisch generierten OpenAPI-Schema für die *Pfadoperation* zusammengeführt (mittels Deep Merge).
Sie können dem automatisch generierten Schema also zusätzliche Daten hinzufügen.
Sie könnten sich beispielsweise dafür entscheiden, den Request mit Ihrem eigenen Code zu lesen und zu validieren, ohne FastAPIs automatische Funktionen mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
Das könnte man mit `openapi_extra` machen:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON geparst, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
Dennoch können wir das zu erwartende Schema für den Requestbody deklarieren.
### Benutzerdefinierter OpenAPI-Content-Type { #custom-openapi-content-type }
Mit demselben Trick könnten Sie ein Pydantic-Modell verwenden, um das JSON-Schema zu definieren, das dann im benutzerdefinierten Abschnitt des OpenAPI-Schemas für die *Pfadoperation* enthalten ist.
Und Sie könnten dies auch tun, wenn der Datentyp im Request nicht JSON ist.
In der folgenden Anwendung verwenden wir beispielsweise weder FastAPIs integrierte Funktionalität zum Extrahieren des JSON-Schemas aus Pydantic-Modellen noch die automatische Validierung für JSON. Tatsächlich deklarieren wir den Request-Content-Type als YAML und nicht als JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Obwohl wir nicht die standardmäßig integrierte Funktionalität verwenden, verwenden wir dennoch ein Pydantic-Modell, um das JSON-Schema für die Daten, die wir in YAML empfangen möchten, manuell zu generieren.
Dann verwenden wir den Request direkt und extrahieren den Body als `bytes`. Das bedeutet, dass FastAPI nicht einmal versucht, die Request-Payload als JSON zu parsen.
Und dann parsen wir in unserem Code diesen YAML-Inhalt direkt und verwenden dann wieder dasselbe Pydantic-Modell, um den YAML-Inhalt zu validieren:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Tipp
Hier verwenden wir dasselbe Pydantic-Modell wieder.
Aber genauso hätten wir es auch auf andere Weise validieren können.
///
================================================
FILE: docs/de/docs/advanced/response-change-status-code.md
================================================
# Response – Statuscode ändern { #response-change-status-code }
Sie haben wahrscheinlich schon vorher gelesen, dass Sie einen Default-[Response-Statuscode](../tutorial/response-status-code.md) festlegen können.
In manchen Fällen müssen Sie jedoch einen anderen als den Default-Statuscode zurückgeben.
## Anwendungsfall { #use-case }
Stellen Sie sich zum Beispiel vor, Sie möchten standardmäßig den HTTP-Statuscode „OK“ `200` zurückgeben.
Wenn die Daten jedoch nicht vorhanden sind, möchten Sie diese erstellen und den HTTP-Statuscode „CREATED“ `201` zurückgeben.
Sie möchten aber dennoch in der Lage sein, die von Ihnen zurückgegebenen Daten mit einem `response_model` zu filtern und zu konvertieren.
In diesen Fällen können Sie einen `Response`-Parameter verwenden.
## Einen `Response`-Parameter verwenden { #use-a-response-parameter }
Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion* deklarieren (wie Sie es auch für Cookies und Header tun können).
Anschließend können Sie den `status_code` in diesem *vorübergehenden* Response-Objekt festlegen.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Und dann können Sie jedes benötigte Objekt zurückgeben, wie Sie es normalerweise tun würden (ein `dict`, ein Datenbankmodell usw.).
Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filtern und Konvertieren des von Ihnen zurückgegebenen Objekts verwendet.
**FastAPI** verwendet diese *vorübergehende* Response, um den Statuscode (auch Cookies und Header) zu extrahieren und fügt diese in die endgültige Response ein, die den von Ihnen zurückgegebenen Wert enthält, gefiltert nach einem beliebigen `response_model`.
Sie können den Parameter `Response` auch in Abhängigkeiten deklarieren und den Statuscode darin festlegen. Bedenken Sie jedoch, dass der zuletzt gesetzte gewinnt.
================================================
FILE: docs/de/docs/advanced/response-cookies.md
================================================
# Response-Cookies { #response-cookies }
## Einen `Response`-Parameter verwenden { #use-a-response-parameter }
Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion* deklarieren.
Und dann können Sie Cookies in diesem *vorübergehenden* Response-Objekt setzen.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filtern und Konvertieren des von Ihnen zurückgegebenen Objekts verwendet.
**FastAPI** verwendet diese *vorübergehende* Response, um die Cookies (auch Header und Statuscode) zu extrahieren und fügt diese in die endgültige Response ein, die den von Ihnen zurückgegebenen Wert enthält, gefiltert nach einem beliebigen `response_model`.
Sie können den `Response`-Parameter auch in Abhängigkeiten deklarieren und darin Cookies (und Header) setzen.
## Eine `Response` direkt zurückgeben { #return-a-response-directly }
Sie können Cookies auch erstellen, wenn Sie eine `Response` direkt in Ihrem Code zurückgeben.
Dazu können Sie eine Response erstellen, wie unter [Eine Response direkt zurückgeben](response-directly.md) beschrieben.
Setzen Sie dann Cookies darin und geben Sie sie dann zurück:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Tipp
Beachten Sie, dass, wenn Sie eine Response direkt zurückgeben, anstatt den `Response`-Parameter zu verwenden, FastAPI diese direkt zurückgibt.
Sie müssen also sicherstellen, dass Ihre Daten vom richtigen Typ sind. Z. B. sollten diese mit JSON kompatibel sein, wenn Sie eine `JSONResponse` zurückgeben.
Und auch, dass Sie keine Daten senden, die durch ein `response_model` hätten gefiltert werden sollen.
///
### Mehr Informationen { #more-info }
/// note | Technische Details
Sie können auch `from starlette.responses import Response` oder `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
Und da die `Response` häufig zum Setzen von Headern und Cookies verwendet wird, stellt **FastAPI** diese auch unter `fastapi.Response` bereit.
///
Um alle verfügbaren Parameter und Optionen anzuzeigen, sehen Sie sich deren [Dokumentation in Starlette](https://www.starlette.dev/responses/#set-cookie) an.
================================================
FILE: docs/de/docs/advanced/response-directly.md
================================================
# Eine Response direkt zurückgeben { #return-a-response-directly }
Wenn Sie eine **FastAPI** *Pfadoperation* erstellen, können Sie normalerweise beliebige Daten davon zurückgeben: ein `dict`, eine `list`, ein Pydantic-Modell, ein Datenbankmodell, usw.
Wenn Sie ein [Responsemodell](../tutorial/response-model.md) deklarieren, wird FastAPI es verwenden, um die Daten mithilfe von Pydantic nach JSON zu serialisieren.
Wenn Sie kein Responsemodell deklarieren, verwendet FastAPI den `jsonable_encoder`, wie in [JSON-kompatibler Encoder](../tutorial/encoder.md) erläutert, und packt die Daten in eine `JSONResponse`.
Sie könnten auch direkt eine `JSONResponse` erstellen und zurückgeben.
/// tip | Tipp
Normalerweise erzielen Sie eine deutlich bessere Leistung, wenn Sie ein [Responsemodell](../tutorial/response-model.md) verwenden, als wenn Sie direkt eine `JSONResponse` zurückgeben, da die Serialisierung der Daten dabei mit Pydantic in Rust erfolgt.
///
## Eine `Response` zurückgeben { #return-a-response }
Tatsächlich können Sie jede `Response` oder jede Unterklasse davon zurückgeben.
/// info | Info
`JSONResponse` selbst ist eine Unterklasse von `Response`.
///
Und wenn Sie eine `Response` zurückgeben, wird **FastAPI** diese direkt weiterleiten.
Es wird keine Datenkonvertierung mit Pydantic-Modellen durchführen, es wird den Inhalt nicht in irgendeinen Typ konvertieren, usw.
Dadurch haben Sie viel Flexibilität. Sie können jeden Datentyp zurückgeben, jede Datendeklaration oder -validierung überschreiben, usw.
Das bringt Ihnen aber auch viel Verantwortung. Sie müssen sicherstellen, dass die von Ihnen zurückgegebenen Daten korrekt sind, das richtige Format haben, serialisierbar sind, usw.
## Verwendung des `jsonable_encoder` in einer `Response` { #using-the-jsonable-encoder-in-a-response }
Da **FastAPI** keine Änderungen an einer von Ihnen zurückgegebenen `Response` vornimmt, müssen Sie sicherstellen, dass deren Inhalt dafür bereit ist.
Sie können beispielsweise kein Pydantic-Modell in eine `JSONResponse` einfügen, ohne es zuvor in ein `dict` zu konvertieren, bei dem alle Datentypen (wie `datetime`, `UUID`, usw.) in JSON-kompatible Typen konvertiert wurden.
In diesen Fällen können Sie den `jsonable_encoder` verwenden, um Ihre Daten zu konvertieren, bevor Sie sie an eine Response übergeben:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Technische Details
Sie könnten auch `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
///
## Eine benutzerdefinierte `Response` zurückgeben { #returning-a-custom-response }
Das obige Beispiel zeigt alle Teile, die Sie benötigen, ist aber noch nicht sehr nützlich, da Sie das `item` einfach direkt hätten zurückgeben können, und **FastAPI** würde es für Sie in eine `JSONResponse` einfügen, es in ein `dict` konvertieren, usw. All das standardmäßig.
Sehen wir uns nun an, wie Sie damit eine benutzerdefinierte Response zurückgeben können.
Nehmen wir an, Sie möchten eine [XML](https://en.wikipedia.org/wiki/XML)-Response zurückgeben.
Sie könnten Ihren XML-Inhalt als String in eine `Response` einfügen und sie zurückgeben:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Funktionsweise eines Responsemodells { #how-a-response-model-works }
Wenn Sie in einer Pfadoperation ein [Responsemodell - Rückgabetyp](../tutorial/response-model.md) deklarieren, wird **FastAPI** es verwenden, um die Daten mithilfe von Pydantic nach JSON zu serialisieren.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Da dies auf der Rust-Seite geschieht, ist die Leistung deutlich besser, als wenn es mit normalem Python und der Klasse `JSONResponse` erfolgen würde.
Wenn Sie ein `response_model` oder einen Rückgabetyp verwenden, nutzt FastAPI weder den `jsonable_encoder` (was langsamer wäre) zur Konvertierung der Daten noch die Klasse `JSONResponse`.
Stattdessen nimmt es die von Pydantic mithilfe des Responsemodells (oder Rückgabetyps) generierten JSON-Bytes und gibt direkt eine `Response` mit dem richtigen Mediatyp für JSON (`application/json`) zurück.
## Anmerkungen { #notes }
Wenn Sie eine `Response` direkt zurücksenden, werden deren Daten weder validiert, konvertiert (serialisiert), noch automatisch dokumentiert.
Sie können sie aber trotzdem wie unter [Zusätzliche Responses in OpenAPI](additional-responses.md) beschrieben dokumentieren.
In späteren Abschnitten erfahren Sie, wie Sie diese benutzerdefinierten `Response`s verwenden/deklarieren und gleichzeitig über automatische Datenkonvertierung, Dokumentation, usw. verfügen.
================================================
FILE: docs/de/docs/advanced/response-headers.md
================================================
# Response-Header { #response-headers }
## Einen `Response`-Parameter verwenden { #use-a-response-parameter }
Sie können einen Parameter vom Typ `Response` in Ihrer *Pfadoperation-Funktion* deklarieren (wie Sie es auch für Cookies tun können).
Und dann können Sie Header in diesem *vorübergehenden* Response-Objekt festlegen.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
Und wenn Sie ein `response_model` deklariert haben, wird es weiterhin zum Filtern und Konvertieren des von Ihnen zurückgegebenen Objekts verwendet.
**FastAPI** verwendet diese *vorübergehende* Response, um die Header (auch Cookies und Statuscode) zu extrahieren und fügt diese in die endgültige Response ein, die den von Ihnen zurückgegebenen Wert enthält, gefiltert nach einem beliebigen `response_model`.
Sie können den Parameter `Response` auch in Abhängigkeiten deklarieren und darin Header (und Cookies) festlegen.
## Eine `Response` direkt zurückgeben { #return-a-response-directly }
Sie können auch Header hinzufügen, wenn Sie eine `Response` direkt zurückgeben.
Erstellen Sie eine Response wie in [Eine Response direkt zurückgeben](response-directly.md) beschrieben und übergeben Sie die Header als zusätzlichen Parameter:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Technische Details
Sie können auch `from starlette.responses import Response` oder `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
Und da die `Response` häufig zum Setzen von Headern und Cookies verwendet wird, stellt **FastAPI** diese auch unter `fastapi.Response` bereit.
///
## Benutzerdefinierte Header { #custom-headers }
Beachten Sie, dass benutzerdefinierte proprietäre Header [mit dem Präfix `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) hinzugefügt werden können.
Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen können soll, müssen Sie diese zu Ihrer CORS-Konfiguration hinzufügen (weitere Informationen finden Sie unter [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), unter Verwendung des Parameters `expose_headers`, dokumentiert in [Starlettes CORS-Dokumentation](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/de/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
Für die einfachsten Fälle können Sie HTTP Basic Auth verwenden.
Bei HTTP Basic Auth erwartet die Anwendung einen Header, der einen Benutzernamen und ein Passwort enthält.
Wenn sie diesen nicht empfängt, gibt sie den HTTP-Error 401 „Unauthorized“ zurück.
Und gibt einen Header `WWW-Authenticate` mit dem Wert `Basic` und einem optionalen `realm`-Parameter zurück.
Dadurch wird der Browser angewiesen, die integrierte Eingabeaufforderung für einen Benutzernamen und ein Passwort anzuzeigen.
Wenn Sie dann den Benutzernamen und das Passwort eingeben, sendet der Browser diese automatisch im Header.
## Einfaches HTTP Basic Auth { #simple-http-basic-auth }
* Importieren Sie `HTTPBasic` und `HTTPBasicCredentials`.
* Erstellen Sie mit `HTTPBasic` ein „`security`-Schema“.
* Verwenden Sie dieses `security` mit einer Abhängigkeit in Ihrer *Pfadoperation*.
* Diese gibt ein Objekt vom Typ `HTTPBasicCredentials` zurück:
* Es enthält den gesendeten `username` und das gesendete `password`.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Wenn Sie versuchen, die URL zum ersten Mal zu öffnen (oder in der Dokumentation auf den Button „Execute“ zu klicken), wird der Browser Sie nach Ihrem Benutzernamen und Passwort fragen:
## Den Benutzernamen überprüfen { #check-the-username }
Hier ist ein vollständigeres Beispiel.
Verwenden Sie eine Abhängigkeit, um zu überprüfen, ob Benutzername und Passwort korrekt sind.
Verwenden Sie dazu das Python-Standardmodul [`secrets`](https://docs.python.org/3/library/secrets.html), um den Benutzernamen und das Passwort zu überprüfen.
`secrets.compare_digest()` benötigt `bytes` oder einen `str`, welcher nur ASCII-Zeichen (solche der englischen Sprache) enthalten darf, das bedeutet, dass es nicht mit Zeichen wie `á`, wie in `Sebastián`, funktionieren würde.
Um dies zu lösen, konvertieren wir zunächst den `username` und das `password` in UTF-8-codierte `bytes`.
Dann können wir `secrets.compare_digest()` verwenden, um sicherzustellen, dass `credentials.username` `"stanleyjobson"` und `credentials.password` `"swordfish"` ist.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Dies wäre das gleiche wie:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Einen Error zurückgeben
...
```
Aber durch die Verwendung von `secrets.compare_digest()` ist dieser Code sicher vor einer Art von Angriffen, die „Timing-Angriffe“ genannt werden.
### Timing-Angriffe { #timing-attacks }
Aber was ist ein „Timing-Angriff“?
Stellen wir uns vor, dass einige Angreifer versuchen, den Benutzernamen und das Passwort zu erraten.
Und sie senden einen Request mit dem Benutzernamen `johndoe` und dem Passwort `love123`.
Dann würde der Python-Code in Ihrer Anwendung etwa so aussehen:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Aber genau in dem Moment, in dem Python das erste `j` in `johndoe` mit dem ersten `s` in `stanleyjobson` vergleicht, gibt es `False` zurück, da es bereits weiß, dass diese beiden Strings nicht identisch sind, und denkt, „Es besteht keine Notwendigkeit, weitere Berechnungen mit dem Vergleich der restlichen Buchstaben zu verschwenden“. Und Ihre Anwendung wird zurückgeben „Incorrect username or password“.
Doch dann versuchen es die Angreifer mit dem Benutzernamen `stanleyjobsox` und dem Passwort `love123`.
Und Ihr Anwendungscode macht etwa Folgendes:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python muss das gesamte `stanleyjobso` in `stanleyjobsox` und `stanleyjobson` vergleichen, bevor es erkennt, dass beide Zeichenfolgen nicht gleich sind. Daher wird es einige zusätzliche Mikrosekunden dauern, bis die Response „Incorrect username or password“ erfolgt.
#### Die Zeit zum Antworten hilft den Angreifern { #the-time-to-answer-helps-the-attackers }
Wenn die Angreifer zu diesem Zeitpunkt feststellen, dass der Server einige Mikrosekunden länger braucht, um die Response „Incorrect username or password“ zu senden, wissen sie, dass sie _etwas_ richtig gemacht haben, einige der Anfangsbuchstaben waren richtig.
Und dann können sie es noch einmal versuchen, wohl wissend, dass es wahrscheinlich eher etwas mit `stanleyjobsox` als mit `johndoe` zu tun hat.
#### Ein „professioneller“ Angriff { #a-professional-attack }
Natürlich würden die Angreifer das alles nicht von Hand versuchen, sondern ein Programm dafür schreiben, möglicherweise mit Tausenden oder Millionen Tests pro Sekunde. Und würden jeweils nur einen zusätzlichen richtigen Buchstaben erhalten.
Aber so hätten die Angreifer in wenigen Minuten oder Stunden mit der „Hilfe“ unserer Anwendung den richtigen Benutzernamen und das richtige Passwort erraten, indem sie die Zeitspanne zur Hilfe nehmen, die diese zur Beantwortung benötigt.
#### Das Problem beheben mittels `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Aber in unserem Code verwenden wir tatsächlich `secrets.compare_digest()`.
Damit wird, kurz gesagt, der Vergleich von `stanleyjobsox` mit `stanleyjobson` genauso lange dauern wie der Vergleich von `johndoe` mit `stanleyjobson`. Und das Gleiche gilt für das Passwort.
So ist Ihr Anwendungscode, dank der Verwendung von `secrets.compare_digest()`, vor dieser ganzen Klasse von Sicherheitsangriffen geschützt.
### Den Error zurückgeben { #return-the-error }
Nachdem Sie festgestellt haben, dass die Anmeldeinformationen falsch sind, geben Sie eine `HTTPException` mit dem Statuscode 401 zurück (derselbe, der auch zurückgegeben wird, wenn keine Anmeldeinformationen angegeben werden) und fügen den Header `WWW-Authenticate` hinzu, damit der Browser die Anmeldeaufforderung erneut anzeigt:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/de/docs/advanced/security/index.md
================================================
# Fortgeschrittene Sicherheit { #advanced-security }
## Zusatzfunktionen { #additional-features }
Neben den in [Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md) behandelten Funktionen gibt es noch einige zusätzliche Funktionen zur Handhabung der Sicherheit.
/// tip | Tipp
Die nächsten Abschnitte sind **nicht unbedingt „fortgeschritten“**.
Und es ist möglich, dass für Ihren Anwendungsfall die Lösung in einem davon liegt.
///
## Das Tutorial zuerst lesen { #read-the-tutorial-first }
Die nächsten Abschnitte setzen voraus, dass Sie das Haupt-[Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md) bereits gelesen haben.
Sie basieren alle auf den gleichen Konzepten, ermöglichen jedoch einige zusätzliche Funktionalitäten.
================================================
FILE: docs/de/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2-Scopes { #oauth2-scopes }
Sie können OAuth2-Scopes direkt in **FastAPI** verwenden, sie sind nahtlos integriert.
Das ermöglicht es Ihnen, ein feingranuliertes Berechtigungssystem nach dem OAuth2-Standard in Ihre OpenAPI-Anwendung (und deren API-Dokumentation) zu integrieren.
OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, X (Twitter) usw. verwendet wird. Sie verwenden ihn, um Benutzern und Anwendungen spezifische Berechtigungen zu erteilen.
Jedes Mal, wenn Sie sich mit Facebook, Google, GitHub, Microsoft oder X (Twitter) anmelden („log in with“), verwendet die entsprechende Anwendung OAuth2 mit Scopes.
In diesem Abschnitt erfahren Sie, wie Sie Authentifizierung und Autorisierung mit demselben OAuth2, mit Scopes in Ihrer **FastAPI**-Anwendung verwalten.
/// warning | Achtung
Dies ist ein mehr oder weniger fortgeschrittener Abschnitt. Wenn Sie gerade erst anfangen, können Sie ihn überspringen.
Sie benötigen nicht unbedingt OAuth2-Scopes, und Sie können die Authentifizierung und Autorisierung handhaben wie Sie möchten.
Aber OAuth2 mit Scopes kann bequem in Ihre API (mit OpenAPI) und deren API-Dokumentation integriert werden.
Dennoch, verwenden Sie solche Scopes oder andere Sicherheits-/Autorisierungsanforderungen in Ihrem Code so wie Sie es möchten.
In vielen Fällen kann OAuth2 mit Scopes ein Overkill sein.
Aber wenn Sie wissen, dass Sie es brauchen oder neugierig sind, lesen Sie weiter.
///
## OAuth2-Scopes und OpenAPI { #oauth2-scopes-and-openapi }
Die OAuth2-Spezifikation definiert „Scopes“ als eine Liste von durch Leerzeichen getrennten Strings.
Der Inhalt jedes dieser Strings kann ein beliebiges Format haben, sollte jedoch keine Leerzeichen enthalten.
Diese Scopes stellen „Berechtigungen“ dar.
In OpenAPI (z. B. der API-Dokumentation) können Sie „Sicherheitsschemas“ definieren.
Wenn eines dieser Sicherheitsschemas OAuth2 verwendet, können Sie auch Scopes deklarieren und verwenden.
Jeder „Scope“ ist nur ein String (ohne Leerzeichen).
Er wird normalerweise verwendet, um bestimmte Sicherheitsberechtigungen zu deklarieren, zum Beispiel:
* `users:read` oder `users:write` sind gängige Beispiele.
* `instagram_basic` wird von Facebook / Instagram verwendet.
* `https://www.googleapis.com/auth/drive` wird von Google verwendet.
/// info | Info
In OAuth2 ist ein „Scope“ nur ein String, der eine bestimmte erforderliche Berechtigung deklariert.
Es spielt keine Rolle, ob er andere Zeichen wie `:` enthält oder ob es eine URL ist.
Diese Details sind implementierungsspezifisch.
Für OAuth2 sind es einfach nur Strings.
///
## Gesamtübersicht { #global-view }
Sehen wir uns zunächst kurz die Teile an, die sich gegenüber den Beispielen im Haupt-**Tutorial – Benutzerhandbuch** für [OAuth2 mit Password (und Hashing), Bearer mit JWT-Tokens](../../tutorial/security/oauth2-jwt.md) ändern. Diesmal verwenden wir OAuth2-Scopes:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Sehen wir uns diese Änderungen nun Schritt für Schritt an.
## OAuth2-Sicherheitsschema { #oauth2-security-scheme }
Die erste Änderung ist, dass wir jetzt das OAuth2-Sicherheitsschema mit zwei verfügbaren Scopes deklarieren: `me` und `items`.
Der `scopes`-Parameter erhält ein `dict` mit jedem Scope als Schlüssel und dessen Beschreibung als Wert:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Da wir diese Scopes jetzt deklarieren, werden sie in der API-Dokumentation angezeigt, wenn Sie sich einloggen/autorisieren.
Und Sie können auswählen, auf welche Scopes Sie Zugriff haben möchten: `me` und `items`.
Das ist derselbe Mechanismus, der verwendet wird, wenn Sie beim Anmelden mit Facebook, Google, GitHub, usw. Berechtigungen erteilen:
## JWT-Token mit Scopes { #jwt-token-with-scopes }
Ändern Sie nun die Token-*Pfadoperation*, um die angeforderten Scopes zurückzugeben.
Wir verwenden immer noch dasselbe `OAuth2PasswordRequestForm`. Es enthält eine Eigenschaft `scopes` mit einer `list`e von `str`s für jeden Scope, den es im Request erhalten hat.
Und wir geben die Scopes als Teil des JWT-Tokens zurück.
/// danger | Gefahr
Der Einfachheit halber fügen wir hier die empfangenen Scopes direkt zum Token hinzu.
Aus Sicherheitsgründen sollten Sie jedoch sicherstellen, dass Sie in Ihrer Anwendung nur die Scopes hinzufügen, die der Benutzer tatsächlich haben kann, oder die Sie vordefiniert haben.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Scopes in *Pfadoperationen* und Abhängigkeiten deklarieren { #declare-scopes-in-path-operations-and-dependencies }
Jetzt deklarieren wir, dass die *Pfadoperation* für `/users/me/items/` den Scope `items` erfordert.
Dazu importieren und verwenden wir `Security` von `fastapi`.
Sie können `Security` verwenden, um Abhängigkeiten zu deklarieren (genau wie `Depends`), aber `Security` erhält auch einen Parameter `scopes` mit einer Liste von Scopes (Strings).
In diesem Fall übergeben wir eine Abhängigkeitsfunktion `get_current_active_user` an `Security` (genauso wie wir es mit `Depends` tun würden).
Wir übergeben aber auch eine `list`e von Scopes, in diesem Fall mit nur einem Scope: `items` (es könnten mehrere sein).
Und die Abhängigkeitsfunktion `get_current_active_user` kann auch Unterabhängigkeiten deklarieren, nicht nur mit `Depends`, sondern auch mit `Security`. Ihre eigene Unterabhängigkeitsfunktion (`get_current_user`) und weitere Scope-Anforderungen deklarierend.
In diesem Fall erfordert sie den Scope `me` (sie könnte mehr als einen Scope erfordern).
/// note | Hinweis
Sie müssen nicht unbedingt an verschiedenen Stellen verschiedene Scopes hinzufügen.
Wir tun dies hier, um zu demonstrieren, wie **FastAPI** auf verschiedenen Ebenen deklarierte Scopes verarbeitet.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Technische Details
`Security` ist tatsächlich eine Unterklasse von `Depends` und hat nur noch einen zusätzlichen Parameter, den wir später kennenlernen werden.
Durch die Verwendung von `Security` anstelle von `Depends` weiß **FastAPI** jedoch, dass es Sicherheits-Scopes deklarieren, intern verwenden und die API mit OpenAPI dokumentieren kann.
Wenn Sie jedoch `Query`, `Path`, `Depends`, `Security` und andere von `fastapi` importieren, handelt es sich tatsächlich um Funktionen, die spezielle Klassen zurückgeben.
///
## `SecurityScopes` verwenden { #use-securityscopes }
Aktualisieren Sie nun die Abhängigkeit `get_current_user`.
Das ist diejenige, die von den oben genannten Abhängigkeiten verwendet wird.
Hier verwenden wir dasselbe OAuth2-Schema, das wir zuvor erstellt haben, und deklarieren es als Abhängigkeit: `oauth2_scheme`.
Da diese Abhängigkeitsfunktion selbst keine Scope-Anforderungen hat, können wir `Depends` mit `oauth2_scheme` verwenden. Wir müssen `Security` nicht verwenden, wenn wir keine Sicherheits-Scopes angeben müssen.
Wir deklarieren auch einen speziellen Parameter vom Typ `SecurityScopes`, der aus `fastapi.security` importiert wird.
Diese `SecurityScopes`-Klasse ähnelt `Request` (`Request` wurde verwendet, um das Request-Objekt direkt zu erhalten).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Die `scopes` verwenden { #use-the-scopes }
Der Parameter `security_scopes` wird vom Typ `SecurityScopes` sein.
Dieses verfügt über ein Attribut `scopes` mit einer Liste, die alle von ihm selbst benötigten Scopes enthält und ferner alle Abhängigkeiten, die dieses als Unterabhängigkeit verwenden. Sprich, alle „Dependanten“ ... das mag verwirrend klingen, wird aber später noch einmal erklärt.
Das `security_scopes`-Objekt (der Klasse `SecurityScopes`) stellt außerdem ein `scope_str`-Attribut mit einem einzelnen String bereit, der die durch Leerzeichen getrennten Scopes enthält (den werden wir verwenden).
Wir erstellen eine `HTTPException`, die wir später an mehreren Stellen wiederverwenden (`raise`n) können.
In diese Exception fügen wir (falls vorhanden) die erforderlichen Scopes als durch Leerzeichen getrennten String ein (unter Verwendung von `scope_str`). Wir fügen diesen String mit den Scopes in den Header `WWW-Authenticate` ein (das ist Teil der Spezifikation).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Den `username` und das Format der Daten überprüfen { #verify-the-username-and-data-shape }
Wir verifizieren, dass wir einen `username` erhalten, und extrahieren die Scopes.
Und dann validieren wir diese Daten mit dem Pydantic-Modell (wobei wir die `ValidationError`-Exception abfangen), und wenn wir beim Lesen des JWT-Tokens oder beim Validieren der Daten mit Pydantic einen Fehler erhalten, lösen wir die zuvor erstellte `HTTPException` aus.
Dazu aktualisieren wir das Pydantic-Modell `TokenData` mit einem neuen Attribut `scopes`.
Durch die Validierung der Daten mit Pydantic können wir sicherstellen, dass wir beispielsweise präzise eine `list`e von `str`s mit den Scopes und einen `str` mit dem `username` haben.
Anstelle beispielsweise eines `dict`s oder etwas anderem, was später in der Anwendung zu Fehlern führen könnte und darum ein Sicherheitsrisiko darstellt.
Wir verifizieren auch, dass wir einen Benutzer mit diesem Benutzernamen haben, und wenn nicht, lösen wir dieselbe Exception aus, die wir zuvor erstellt haben.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Die `scopes` verifizieren { #verify-the-scopes }
Wir überprüfen nun, ob das empfangene Token alle Scopes enthält, die von dieser Abhängigkeit und deren Verwendern (einschließlich *Pfadoperationen*) gefordert werden. Andernfalls lösen wir eine `HTTPException` aus.
Hierzu verwenden wir `security_scopes.scopes`, das eine `list`e mit allen diesen Scopes als `str` enthält.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Abhängigkeitsbaum und Scopes { #dependency-tree-and-scopes }
Sehen wir uns diesen Abhängigkeitsbaum und die Scopes noch einmal an.
Da die Abhängigkeit `get_current_active_user` von `get_current_user` abhängt, wird der bei `get_current_active_user` deklarierte Scope `"me"` in die Liste der erforderlichen Scopes in `security_scopes.scopes` aufgenommen, das an `get_current_user` übergeben wird.
Die *Pfadoperation* selbst deklariert auch einen Scope, `"items"`, sodass dieser auch in der Liste der `security_scopes.scopes` enthalten ist, die an `get_current_user` übergeben wird.
So sieht die Hierarchie der Abhängigkeiten und Scopes aus:
* Die *Pfadoperation* `read_own_items` hat:
* Erforderliche Scopes `["items"]` mit der Abhängigkeit:
* `get_current_active_user`:
* Die Abhängigkeitsfunktion `get_current_active_user` hat:
* Erforderliche Scopes `["me"]` mit der Abhängigkeit:
* `get_current_user`:
* Die Abhängigkeitsfunktion `get_current_user` hat:
* Selbst keine erforderlichen Scopes.
* Eine Abhängigkeit, die `oauth2_scheme` verwendet.
* Einen `security_scopes`-Parameter vom Typ `SecurityScopes`:
* Dieser `security_scopes`-Parameter hat ein Attribut `scopes` mit einer `list`e, die alle oben deklarierten Scopes enthält, sprich:
* `security_scopes.scopes` enthält `["me", "items"]` für die *Pfadoperation* `read_own_items`.
* `security_scopes.scopes` enthält `["me"]` für die *Pfadoperation* `read_users_me`, da das in der Abhängigkeit `get_current_active_user` deklariert ist.
* `security_scopes.scopes` wird `[]` (nichts) für die *Pfadoperation* `read_system_status` enthalten, da diese keine `Security` mit `scopes` deklariert hat, und deren Abhängigkeit `get_current_user` ebenfalls keinerlei `scopes` deklariert.
/// tip | Tipp
Das Wichtige und „Magische“ hier ist, dass `get_current_user` für jede *Pfadoperation* eine andere Liste von `scopes` hat, die überprüft werden.
Alles hängt von den „Scopes“ ab, die in jeder *Pfadoperation* und jeder Abhängigkeit im Abhängigkeitsbaum für diese bestimmte *Pfadoperation* deklariert wurden.
///
## Weitere Details zu `SecurityScopes` { #more-details-about-securityscopes }
Sie können `SecurityScopes` an jeder Stelle und an mehreren Stellen verwenden, es muss sich nicht in der „Wurzel“-Abhängigkeit befinden.
Es wird immer die Sicherheits-Scopes enthalten, die in den aktuellen `Security`-Abhängigkeiten deklariert sind und in allen Abhängigkeiten für **diese spezifische** *Pfadoperation* und **diesen spezifischen** Abhängigkeitsbaum.
Da die `SecurityScopes` alle von den Verwendern der Abhängigkeiten deklarierten Scopes enthalten, können Sie damit überprüfen, ob ein Token in einer zentralen Abhängigkeitsfunktion über die erforderlichen Scopes verfügt, und dann unterschiedliche Scope-Anforderungen in unterschiedlichen *Pfadoperationen* deklarieren.
Diese werden für jede *Pfadoperation* unabhängig überprüft.
## Es testen { #check-it }
Wenn Sie die API-Dokumentation öffnen, können Sie sich authentisieren und angeben, welche Scopes Sie autorisieren möchten.
Wenn Sie keinen Scope auswählen, werden Sie „authentifiziert“, aber wenn Sie versuchen, auf `/users/me/` oder `/users/me/items/` zuzugreifen, wird eine Fehlermeldung angezeigt, die sagt, dass Sie nicht über genügend Berechtigungen verfügen. Sie können aber auf `/status/` zugreifen.
Und wenn Sie den Scope `me`, aber nicht den Scope `items` auswählen, können Sie auf `/users/me/` zugreifen, aber nicht auf `/users/me/items/`.
Das würde einer Drittanbieteranwendung passieren, die versucht, auf eine dieser *Pfadoperationen* mit einem Token zuzugreifen, das von einem Benutzer bereitgestellt wurde, abhängig davon, wie viele Berechtigungen der Benutzer dieser Anwendung erteilt hat.
## Über Integrationen von Drittanbietern { #about-third-party-integrations }
In diesem Beispiel verwenden wir den OAuth2-Flow „Password“.
Das ist angemessen, wenn wir uns bei unserer eigenen Anwendung anmelden, wahrscheinlich mit unserem eigenen Frontend.
Weil wir darauf vertrauen können, dass es den `username` und das `password` erhält, welche wir kontrollieren.
Wenn Sie jedoch eine OAuth2-Anwendung erstellen, mit der andere eine Verbindung herstellen würden (d.h. wenn Sie einen Authentifizierungsanbieter erstellen, der Facebook, Google, GitHub usw. entspricht), sollten Sie einen der anderen Flows verwenden.
Am häufigsten ist der „Implicit“-Flow.
Am sichersten ist der „Code“-Flow, die Implementierung ist jedoch komplexer, da mehr Schritte erforderlich sind. Da er komplexer ist, schlagen viele Anbieter letztendlich den „Implicit“-Flow vor.
/// note | Hinweis
Es ist üblich, dass jeder Authentifizierungsanbieter seine Flows anders benennt, um sie zu einem Teil seiner Marke zu machen.
Aber am Ende implementieren sie denselben OAuth2-Standard.
///
**FastAPI** enthält Werkzeuge für alle diese OAuth2-Authentifizierungs-Flows in `fastapi.security.oauth2`.
## `Security` in Dekorator-`dependencies` { #security-in-decorator-dependencies }
Auf die gleiche Weise können Sie eine `list`e von `Depends` im Parameter `dependencies` des Dekorators definieren (wie in [Abhängigkeiten in Pfadoperation-Dekoratoren](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md) erläutert), Sie könnten auch dort `Security` mit `scopes` verwenden.
================================================
FILE: docs/de/docs/advanced/settings.md
================================================
# Einstellungen und Umgebungsvariablen { #settings-and-environment-variables }
In vielen Fällen benötigt Ihre Anwendung möglicherweise einige externe Einstellungen oder Konfigurationen, zum Beispiel geheime Schlüssel, Datenbank-Anmeldeinformationen, Anmeldeinformationen für E-Mail-Dienste, usw.
Die meisten dieser Einstellungen sind variabel (können sich ändern), wie z. B. Datenbank-URLs. Und vieles könnten schützenswerte, geheime Daten sein.
Aus diesem Grund werden diese üblicherweise in Umgebungsvariablen bereitgestellt, die von der Anwendung gelesen werden.
/// tip | Tipp
Um Umgebungsvariablen zu verstehen, können Sie [Umgebungsvariablen](../environment-variables.md) lesen.
///
## Typen und Validierung { #types-and-validation }
Diese Umgebungsvariablen können nur Text-Zeichenketten verarbeiten, da sie außerhalb von Python liegen und mit anderen Programmen und dem Rest des Systems (und sogar mit verschiedenen Betriebssystemen wie Linux, Windows, macOS) kompatibel sein müssen.
Das bedeutet, dass jeder in Python aus einer Umgebungsvariablen gelesene Wert ein `str` ist und jede Konvertierung in einen anderen Typ oder jede Validierung im Code erfolgen muss.
## Pydantic `Settings` { #pydantic-settings }
Glücklicherweise bietet Pydantic ein großartiges Werkzeug zur Verarbeitung dieser Einstellungen, die von Umgebungsvariablen stammen, mit [Pydantic: Settings Management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### `pydantic-settings` installieren { #install-pydantic-settings }
Stellen Sie zunächst sicher, dass Sie Ihre [virtuelle Umgebung](../virtual-environments.md) erstellt und aktiviert haben, und installieren Sie dann das Package `pydantic-settings`:
### Das `Settings`-Objekt erstellen { #create-the-settings-object }
Importieren Sie `BaseSettings` aus Pydantic und erstellen Sie eine Unterklasse, ganz ähnlich wie bei einem Pydantic-Modell.
Auf die gleiche Weise wie bei Pydantic-Modellen deklarieren Sie Klassenattribute mit Typannotationen und möglicherweise Defaultwerten.
Sie können dieselben Validierungs-Funktionen und -Tools verwenden, die Sie für Pydantic-Modelle verwenden, z. B. verschiedene Datentypen und zusätzliche Validierungen mit `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Tipp
Für ein schnelles Copy-and-paste verwenden Sie nicht dieses Beispiel, sondern das letzte unten.
///
Wenn Sie dann eine Instanz dieser `Settings`-Klasse erstellen (in diesem Fall als `settings`-Objekt), liest Pydantic die Umgebungsvariablen ohne Berücksichtigung der Groß- und Kleinschreibung. Eine Variable `APP_NAME` in Großbuchstaben wird also als Attribut `app_name` gelesen.
Als Nächstes werden die Daten konvertiert und validiert. Wenn Sie also dieses `settings`-Objekt verwenden, verfügen Sie über Daten mit den von Ihnen deklarierten Typen (z. B. ist `items_per_user` ein `int`).
### `settings` verwenden { #use-the-settings }
Dann können Sie das neue `settings`-Objekt in Ihrer Anwendung verwenden:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Den Server ausführen { #run-the-server }
Als Nächstes würden Sie den Server ausführen und die Konfigurationen als Umgebungsvariablen übergeben. Sie könnten beispielsweise `ADMIN_EMAIL` und `APP_NAME` festlegen mit:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Tipp
Um mehrere Umgebungsvariablen für einen einzelnen Befehl festzulegen, trennen Sie diese einfach durch ein Leerzeichen und fügen Sie alle vor dem Befehl ein.
///
Und dann würde die Einstellung `admin_email` auf „deadpool@example.com“ gesetzt.
Der `app_name` wäre „ChimichangApp“.
Und `items_per_user` würde seinen Defaultwert von `50` behalten.
## Einstellungen in einem anderen Modul { #settings-in-another-module }
Sie könnten diese Einstellungen in eine andere Moduldatei einfügen, wie Sie in [Größere Anwendungen – mehrere Dateien](../tutorial/bigger-applications.md) gesehen haben.
Sie könnten beispielsweise eine Datei `config.py` haben mit:
{* ../../docs_src/settings/app01_py310/config.py *}
Und dann verwenden Sie diese in einer Datei `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Tipp
Sie benötigen außerdem eine Datei `__init__.py`, wie in [Größere Anwendungen – mehrere Dateien](../tutorial/bigger-applications.md) gesehen.
///
## Einstellungen in einer Abhängigkeit { #settings-in-a-dependency }
In manchen Fällen kann es nützlich sein, die Einstellungen mit einer Abhängigkeit bereitzustellen, anstatt ein globales Objekt `settings` zu haben, das überall verwendet wird.
Dies könnte besonders beim Testen nützlich sein, da es sehr einfach ist, eine Abhängigkeit mit Ihren eigenen benutzerdefinierten Einstellungen zu überschreiben.
### Die Konfigurationsdatei { #the-config-file }
Ausgehend vom vorherigen Beispiel könnte Ihre Datei `config.py` so aussehen:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Beachten Sie, dass wir jetzt keine Standardinstanz `settings = Settings()` erstellen.
### Die Haupt-Anwendungsdatei { #the-main-app-file }
Jetzt erstellen wir eine Abhängigkeit, die ein neues `config.Settings()` zurückgibt.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Tipp
Wir werden das `@lru_cache` in Kürze besprechen.
Im Moment nehmen Sie an, dass `get_settings()` eine normale Funktion ist.
///
Und dann können wir das von der *Pfadoperation-Funktion* als Abhängigkeit einfordern und es überall dort verwenden, wo wir es brauchen.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Einstellungen und Tests { #settings-and-testing }
Dann wäre es sehr einfach, beim Testen ein anderes Einstellungsobjekt bereitzustellen, indem man eine Abhängigkeitsüberschreibung für `get_settings` erstellt:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
Bei der Abhängigkeitsüberschreibung legen wir einen neuen Wert für `admin_email` fest, wenn wir das neue `Settings`-Objekt erstellen, und geben dann dieses neue Objekt zurück.
Dann können wir testen, ob das verwendet wird.
## Lesen einer `.env`-Datei { #reading-a-env-file }
Wenn Sie viele Einstellungen haben, die sich möglicherweise oft ändern, vielleicht in verschiedenen Umgebungen, kann es nützlich sein, diese in eine Datei zu schreiben und sie dann daraus zu lesen, als wären sie Umgebungsvariablen.
Diese Praxis ist so weit verbreitet, dass sie einen Namen hat. Diese Umgebungsvariablen werden üblicherweise in einer Datei `.env` abgelegt und die Datei wird „dotenv“ genannt.
/// tip | Tipp
Eine Datei, die mit einem Punkt (`.`) beginnt, ist eine versteckte Datei in Unix-ähnlichen Systemen wie Linux und macOS.
Aber eine dotenv-Datei muss nicht unbedingt genau diesen Dateinamen haben.
///
Pydantic unterstützt das Lesen dieser Dateitypen mithilfe einer externen Bibliothek. Weitere Informationen finden Sie unter [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Tipp
Damit das funktioniert, müssen Sie `pip install python-dotenv` ausführen.
///
### Die `.env`-Datei { #the-env-file }
Sie könnten eine `.env`-Datei haben, mit:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Einstellungen aus `.env` lesen { #read-settings-from-env }
Und dann aktualisieren Sie Ihre `config.py` mit:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Tipp
Das Attribut `model_config` wird nur für die Pydantic-Konfiguration verwendet. Weitere Informationen finden Sie unter [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Hier definieren wir die Konfiguration `env_file` innerhalb Ihrer Pydantic-`Settings`-Klasse und setzen den Wert auf den Dateinamen mit der dotenv-Datei, die wir verwenden möchten.
### Die `Settings` nur einmal laden mittels `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Das Lesen einer Datei von der Festplatte ist normalerweise ein kostspieliger (langsamer) Vorgang, daher möchten Sie ihn wahrscheinlich nur einmal ausführen und dann dasselbe Einstellungsobjekt erneut verwenden, anstatt es für jeden Request zu lesen.
Aber jedes Mal, wenn wir ausführen:
```Python
Settings()
```
würde ein neues `Settings`-Objekt erstellt und bei der Erstellung würde die `.env`-Datei erneut ausgelesen.
Wenn die Abhängigkeitsfunktion wie folgt wäre:
```Python
def get_settings():
return Settings()
```
würden wir dieses Objekt für jeden Request erstellen und die `.env`-Datei für jeden Request lesen. ⚠️
Da wir jedoch den `@lru_cache`-Dekorator oben verwenden, wird das `Settings`-Objekt nur einmal erstellt, nämlich beim ersten Aufruf. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Dann wird bei allen nachfolgenden Aufrufen von `get_settings()`, in den Abhängigkeiten für darauffolgende Requests, dasselbe Objekt zurückgegeben, das beim ersten Aufruf zurückgegeben wurde, anstatt den Code von `get_settings()` erneut auszuführen und ein neues `Settings`-Objekt zu erstellen.
#### Technische Details zu `lru_cache` { #lru-cache-technical-details }
`@lru_cache` ändert die Funktion, die es dekoriert, dahingehend, denselben Wert zurückzugeben, der beim ersten Mal zurückgegeben wurde, anstatt ihn erneut zu berechnen und den Code der Funktion jedes Mal auszuführen.
Die darunter liegende Funktion wird also für jede Argumentkombination einmal ausgeführt. Und dann werden die von jeder dieser Argumentkombinationen zurückgegebenen Werte immer wieder verwendet, wenn die Funktion mit genau derselben Argumentkombination aufgerufen wird.
Wenn Sie beispielsweise eine Funktion haben:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
könnte Ihr Programm so ausgeführt werden:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Funktion ausführen
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: führe Code der Funktion aus
execute ->> code: gib das Resultat zurück
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: gib das gespeicherte Resultat zurück
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: führe Code der Funktion aus
execute ->> code: gib das Resultat zurück
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: führe Code der Funktion aus
execute ->> code: gib das Resultat zurück
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: gib das gespeicherte Resultat zurück
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: gib das gespeicherte Resultat zurück
end
```
Im Fall unserer Abhängigkeit `get_settings()` akzeptiert die Funktion nicht einmal Argumente, sodass sie immer den gleichen Wert zurückgibt.
Auf diese Weise verhält es sich fast so, als wäre es nur eine globale Variable. Da es jedoch eine Abhängigkeitsfunktion verwendet, können wir diese zu Testzwecken problemlos überschreiben.
`@lru_cache` ist Teil von `functools`, welches Teil von Pythons Standardbibliothek ist. Weitere Informationen dazu finden Sie in der [Python Dokumentation für `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Zusammenfassung { #recap }
Mit Pydantic Settings können Sie die Einstellungen oder Konfigurationen für Ihre Anwendung verwalten und dabei die gesamte Leistungsfähigkeit der Pydantic-Modelle nutzen.
* Durch die Verwendung einer Abhängigkeit können Sie das Testen vereinfachen.
* Sie können `.env`-Dateien damit verwenden.
* Durch die Verwendung von `@lru_cache` können Sie vermeiden, die dotenv-Datei bei jedem Request erneut zu lesen, während Sie sie während des Testens überschreiben können.
================================================
FILE: docs/de/docs/advanced/stream-data.md
================================================
# Daten streamen { #stream-data }
Wenn Sie Daten streamen möchten, die als JSON strukturiert werden können, sollten Sie [JSON Lines streamen](../tutorial/stream-json-lines.md).
Wenn Sie jedoch **reine Binärdaten** oder Strings streamen möchten, so können Sie es machen.
/// info | Info
Hinzugefügt in FastAPI 0.134.0.
///
## Anwendungsfälle { #use-cases }
Sie könnten dies verwenden, wenn Sie reine Strings streamen möchten, z. B. direkt aus der Ausgabe eines **AI-LLM**-Dienstes.
Sie könnten es auch nutzen, um **große Binärdateien** zu streamen, wobei Sie jeden Datenchunk beim Lesen streamen, ohne alles auf einmal in den Speicher laden zu müssen.
Sie könnten auf diese Weise auch **Video** oder **Audio** streamen, es könnte sogar beim Verarbeiten erzeugt und gesendet werden.
## Eine `StreamingResponse` mit `yield` { #a-streamingresponse-with-yield }
Wenn Sie in Ihrer Pfadoperation-Funktion ein `response_class=StreamingResponse` deklarieren, können Sie `yield` verwenden, um nacheinander jeden Datenchunk zu senden.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI übergibt jeden Datenchunk unverändert an die `StreamingResponse`, es wird nicht versucht, ihn in JSON oder etwas Ähnliches zu konvertieren.
### Nicht-async-Pfadoperation-Funktionen { #non-async-path-operation-functions }
Sie können auch reguläre `def`-Funktionen (ohne `async`) verwenden und `yield` auf die gleiche Weise einsetzen.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Keine Annotation { #no-annotation }
Sie müssen den Rückgabetyp für das Streamen von Binärdaten nicht wirklich annotieren.
Da FastAPI die Daten nicht mit Pydantic in JSON umzuwandeln oder sie anderweitig zu serialisieren versucht, ist die Typannotation hier nur für Ihren Editor und Tools relevant, sie wird von FastAPI nicht verwendet.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Das bedeutet auch, dass Sie mit `StreamingResponse` die **Freiheit** und **Verantwortung** haben, die Datenbytes genau so zu erzeugen und zu encodieren, wie sie gesendet werden sollen, unabhängig von den Typannotationen. 🤓
### Bytes streamen { #stream-bytes }
Einer der Hauptanwendungsfälle wäre, `bytes` statt Strings zu streamen, das können Sie selbstverständlich tun.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Eine benutzerdefinierte `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
In den obigen Beispielen wurden die Datenbytes gestreamt, aber die Response hatte keinen `Content-Type`-Header, sodass der Client nicht wusste, welchen Datentyp er erhielt.
Sie können eine benutzerdefinierte Unterklasse von `StreamingResponse` erstellen, die den `Content-Type`-Header auf den Typ der gestreamten Daten setzt.
Zum Beispiel können Sie eine `PNGStreamingResponse` erstellen, die den `Content-Type`-Header mit dem Attribut `media_type` auf `image/png` setzt:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Dann können Sie diese neue Klasse mit `response_class=PNGStreamingResponse` in Ihrer Pfadoperation-Funktion verwenden:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Eine Datei simulieren { #simulate-a-file }
In diesem Beispiel simulieren wir eine Datei mit `io.BytesIO`, einem dateiähnlichen Objekt, das nur im Speicher lebt, uns aber dieselbe Schnittstelle nutzen lässt.
Wir können z. B. darüber iterieren, um seinen Inhalt zu konsumieren, so wie bei einer Datei.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Technische Details
Die anderen beiden Variablen, `image_base64` und `binary_image`, sind ein in Base64 encodiertes Bild, dann in Bytes konvertiert, um es anschließend an `io.BytesIO` zu übergeben.
Nur damit es in derselben Datei leben kann, für dieses Beispiel, und Sie es unverändert kopieren und ausführen können. 🥚
///
Mit einem `with`-Block stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion (die Funktion mit `yield`) fertig ist. Also nachdem die Response gesendet wurde.
In diesem speziellen Beispiel wäre das nicht so wichtig, weil es sich um eine unechte In-Memory-Datei (mit `io.BytesIO`) handelt, aber bei einer echten Datei wäre es wichtig sicherzustellen, dass die Datei nach der Arbeit damit geschlossen wird.
### Dateien und Async { #files-and-async }
In den meisten Fällen sind dateiähnliche Objekte standardmäßig nicht mit async und await kompatibel.
Beispielsweise haben sie kein `await file.read()` oder `async for chunk in file`.
Und in vielen Fällen wäre das Lesen eine blockierende Operation (die die Event-Loop blockieren könnte), weil von der Festplatte oder aus dem Netzwerk gelesen wird.
/// info | Info
Das obige Beispiel ist tatsächlich eine Ausnahme, weil sich das `io.BytesIO`-Objekt bereits im Speicher befindet, daher blockiert sein Lesen nichts.
Aber in vielen Fällen würde das Lesen einer Datei oder eines dateiähnlichen Objekts blockieren.
///
Um die Event-Loop nicht zu blockieren, können Sie die Pfadoperation-Funktion einfach mit normalem `def` statt `async def` deklarieren, dadurch führt FastAPI sie in einem Threadpool-Worker aus, um die Haupt-Event-Loop nicht zu blockieren.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Tipp
Wenn Sie blockierenden Code aus einer async-Funktion heraus aufrufen müssen, oder eine async-Funktion aus einer blockierenden Funktion, könnten Sie [Asyncer](https://asyncer.tiangolo.com), eine Schwesterbibliothek zu FastAPI, verwenden.
///
### `yield from` { #yield-from }
Wenn Sie über etwas iterieren, z. B. ein dateiähnliches Objekt, und dann für jedes Element `yield` verwenden, könnten Sie auch `yield from` verwenden, um jedes Element direkt weiterzugeben und die `for`-Schleife zu sparen.
Das ist nichts Spezifisches an FastAPI, das ist einfach Python, aber ein netter Trick. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/de/docs/advanced/strict-content-type.md
================================================
# Strikte Content-Type-Prüfung { #strict-content-type-checking }
Standardmäßig verwendet **FastAPI** eine strikte Prüfung des `Content-Type`-Headers für JSON-Requestbodys. Das bedeutet, dass JSON-Requests einen gültigen `Content-Type`-Header (z. B. `application/json`) enthalten MÜSSEN, damit der Body als JSON geparst wird.
## CSRF-Risiko { #csrf-risk }
Dieses Standardverhalten schützt vor einer Klasse von **Cross-Site Request Forgery (CSRF)**-Angriffen in einem sehr spezifischen Szenario.
Diese Angriffe nutzen aus, dass Browser Skripte Requests senden lassen, ohne einen CORS-Preflight-Check durchzuführen, wenn sie:
* keinen `Content-Type`-Header haben (z. B. mit `fetch()` und einem `Blob`-Body)
* und keine Authentifizierungsdaten senden.
Diese Art von Angriff ist vor allem relevant, wenn:
* die Anwendung lokal läuft (z. B. auf `localhost`) oder in einem internen Netzwerk
* und die Anwendung keine Authentifizierung hat, sondern erwartet, dass jeder Request aus demselben Netzwerk vertrauenswürdig ist.
## Beispielangriff { #example-attack }
Stellen Sie sich vor, Sie bauen eine Möglichkeit, lokal einen KI-Agenten auszuführen.
Er stellt eine API bereit unter
```
http://localhost:8000/v1/agents/multivac
```
Es gibt auch ein Frontend unter
```
http://localhost:8000
```
/// tip | Tipp
Beachten Sie, dass beide denselben Host haben.
///
Dann können Sie über das Frontend den KI-Agenten Dinge in Ihrem Namen erledigen lassen.
Da er **lokal** läuft und nicht im offenen Internet, entscheiden Sie sich, **keine Authentifizierung** einzurichten und vertrauen stattdessen einfach auf den Zugriff im lokalen Netzwerk.
Dann könnte einer Ihrer Benutzer es installieren und lokal ausführen.
Anschließend könnte er eine bösartige Website öffnen, z. B. so etwas wie
```
https://evilhackers.example.com
```
Und diese bösartige Website sendet Requests mit `fetch()` und einem `Blob`-Body an die lokale API unter
```
http://localhost:8000/v1/agents/multivac
```
Obwohl der Host der bösartigen Website und der lokalen App unterschiedlich ist, löst der Browser keinen CORS-Preflight-Request aus, weil:
* sie ohne Authentifizierung läuft, es müssen keine Credentials gesendet werden.
* der Browser annimmt, dass kein JSON gesendet wird (wegen des fehlenden `Content-Type`-Headers).
Dann könnte die bösartige Website den lokalen KI-Agenten dazu bringen, wütende Nachrichten an den Ex-Chef des Benutzers zu schicken ... oder Schlimmeres. 😅
## Offenes Internet { #open-internet }
Wenn Ihre App im offenen Internet läuft, würden Sie nicht „dem Netzwerk vertrauen“ und jedem erlauben, privilegierte Requests ohne Authentifizierung zu senden.
Angreifer könnten einfach ein Skript ausführen, um Requests an Ihre API zu senden, es ist keine Browserinteraktion nötig. Daher sichern Sie wahrscheinlich schon alle privilegierten Endpunkte.
In diesem Fall gilt **dieser Angriff / dieses Risiko nicht für Sie**.
Dieses Risiko und dieser Angriff sind vor allem relevant, wenn die App im **lokalen Netzwerk** läuft und das die **einzige angenommene Schutzmaßnahme** ist.
## Requests ohne Content-Type erlauben { #allowing-requests-without-content-type }
Wenn Sie Clients unterstützen müssen, die keinen `Content-Type`-Header senden, können Sie die strikte Prüfung deaktivieren, indem Sie `strict_content_type=False` setzen:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
Mit dieser Einstellung werden Requests ohne `Content-Type`-Header im Body als JSON geparst. Das entspricht dem Verhalten älterer FastAPI-Versionen.
/// info | Info
Dieses Verhalten und diese Konfiguration wurden in FastAPI 0.132.0 hinzugefügt.
///
================================================
FILE: docs/de/docs/advanced/sub-applications.md
================================================
# Unteranwendungen – Mounts { #sub-applications-mounts }
Wenn Sie zwei unabhängige FastAPI-Anwendungen mit deren eigenen unabhängigen OpenAPI und deren eigenen Dokumentationsoberflächen benötigen, können Sie eine Hauptanwendung haben und dann eine (oder mehrere) Unteranwendung(en) „mounten“.
## Eine **FastAPI**-Anwendung mounten { #mounting-a-fastapi-application }
„Mounten“ („Einhängen“) bedeutet das Hinzufügen einer völlig „unabhängigen“ Anwendung an einem bestimmten Pfad, die sich dann um die Handhabung aller unter diesem Pfad liegenden _Pfadoperationen_ kümmert, welche in dieser Unteranwendung deklariert sind.
### Hauptanwendung { #top-level-application }
Erstellen Sie zunächst die Hauptanwendung **FastAPI** und deren *Pfadoperationen*:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Unteranwendung { #sub-application }
Erstellen Sie dann Ihre Unteranwendung und deren *Pfadoperationen*.
Diese Unteranwendung ist nur eine weitere Standard-FastAPI-Anwendung, aber diese wird „gemountet“:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Die Unteranwendung mounten { #mount-the-sub-application }
Mounten Sie in Ihrer Top-Level-Anwendung `app` die Unteranwendung `subapi`.
In diesem Fall wird sie im Pfad `/subapi` gemountet:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Die automatische API-Dokumentation testen { #check-the-automatic-api-docs }
Führen Sie nun den Befehl `fastapi` aus:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Und öffnen Sie die Dokumentation unter [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Sie sehen die automatische API-Dokumentation für die Hauptanwendung, welche nur deren eigene _Pfadoperationen_ anzeigt:
Öffnen Sie dann die Dokumentation für die Unteranwendung unter [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Sie sehen die automatische API-Dokumentation für die Unteranwendung, welche nur deren eigene _Pfadoperationen_ anzeigt, alle unter dem korrekten Unterpfad-Präfix `/subapi`:
Wenn Sie versuchen, mit einer der beiden Benutzeroberflächen zu interagieren, funktionieren diese ordnungsgemäß, da der Browser mit jeder spezifischen Anwendung oder Unteranwendung kommunizieren kann.
### Technische Details: `root_path` { #technical-details-root-path }
Wenn Sie eine Unteranwendung wie oben beschrieben mounten, kümmert sich FastAPI darum, den Mount-Pfad für die Unteranwendung zu kommunizieren, mithilfe eines Mechanismus aus der ASGI-Spezifikation namens `root_path`.
Auf diese Weise weiß die Unteranwendung, dass sie dieses Pfadpräfix für die Benutzeroberfläche der Dokumentation verwenden soll.
Und die Unteranwendung könnte auch ihre eigenen gemounteten Unteranwendungen haben und alles würde korrekt funktionieren, da FastAPI sich um alle diese `root_path`s automatisch kümmert.
Mehr über den `root_path` und dessen explizite Verwendung erfahren Sie im Abschnitt [Hinter einem Proxy](behind-a-proxy.md).
================================================
FILE: docs/de/docs/advanced/templates.md
================================================
# Templates { #templates }
Sie können jede gewünschte Template-Engine mit **FastAPI** verwenden.
Eine häufige Wahl ist Jinja2, dasselbe, was auch von Flask und anderen Tools verwendet wird.
Es gibt Werkzeuge zur einfachen Konfiguration, die Sie direkt in Ihrer **FastAPI**-Anwendung verwenden können (bereitgestellt von Starlette).
## Abhängigkeiten installieren { #install-dependencies }
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und `jinja2` installieren:
/// info | Info
Es gibt andere Formate und Tools zum Definieren und Installieren von Paketabhängigkeiten.
///
### Den **FastAPI**-Code erstellen { #create-the-fastapi-code }
* Erstellen Sie ein `app`-Verzeichnis und betreten Sie es.
* Erstellen Sie eine leere Datei `__init__.py`.
* Erstellen Sie eine `main.py`-Datei mit:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Erstellen Sie nun im selben Projektverzeichnis eine Datei `Dockerfile` mit:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Beginne mit dem offiziellen Python-Basisimage.
2. Setze das aktuelle Arbeitsverzeichnis auf `/code`.
Hier platzieren wir die Datei `requirements.txt` und das Verzeichnis `app`.
3. Kopiere die Datei mit den Paketanforderungen in das Verzeichnis `/code`.
Kopieren Sie zuerst **nur** die Datei mit den Anforderungen, nicht den Rest des Codes.
Da sich diese Datei **nicht oft ändert**, erkennt Docker das und verwendet den **Cache** für diesen Schritt, wodurch der Cache auch für den nächsten Schritt aktiviert wird.
4. Installiere die Paketabhängigkeiten aus der Anforderungsdatei.
Die Option `--no-cache-dir` weist `pip` an, die heruntergeladenen Pakete nicht lokal zu speichern, da dies nur benötigt wird, sollte `pip` erneut ausgeführt werden, um dieselben Pakete zu installieren, aber das ist beim Arbeiten mit Containern nicht der Fall.
/// note | Hinweis
Das `--no-cache-dir` bezieht sich nur auf `pip`, es hat nichts mit Docker oder Containern zu tun.
///
Die Option `--upgrade` weist `pip` an, die Packages zu aktualisieren, wenn sie bereits installiert sind.
Da der vorherige Schritt des Kopierens der Datei vom **Docker-Cache** erkannt werden konnte, wird dieser Schritt auch **den Docker-Cache verwenden**, sofern verfügbar.
Durch die Verwendung des Caches in diesem Schritt **sparen** Sie viel **Zeit**, wenn Sie das Image während der Entwicklung immer wieder erstellen, anstatt **jedes Mal** alle Abhängigkeiten **herunterzuladen und zu installieren**.
5. Kopiere das Verzeichnis `./app` in das Verzeichnis `/code`.
Da hier der gesamte Code enthalten ist, der sich **am häufigsten ändert**, wird der Docker-**Cache** nicht ohne weiteres für diesen oder andere **folgende Schritte** verwendet.
Daher ist es wichtig, dies **nahe dem Ende** des `Dockerfile`s zu platzieren, um die Erstellungszeiten des Containerimages zu optimieren.
6. Lege den **Befehl** fest, um `fastapi run` zu nutzen, welches Uvicorn darunter verwendet.
`CMD` nimmt eine Liste von Zeichenfolgen entgegen. Jede dieser Zeichenfolgen entspricht dem, was Sie durch Leerzeichen getrennt in die Befehlszeile eingeben würden.
Dieser Befehl wird aus dem **aktuellen Arbeitsverzeichnis** ausgeführt, dem gleichen `/code`-Verzeichnis, das Sie oben mit `WORKDIR /code` festgelegt haben.
/// tip | Tipp
Lernen Sie, was jede Zeile bewirkt, indem Sie auf die Zahlenblasen im Code klicken. 👆
///
/// warning | Achtung
Stellen Sie sicher, dass Sie **immer** die **exec form** der Anweisung `CMD` verwenden, wie unten erläutert.
///
#### `CMD` – Exec Form verwenden { #use-cmd-exec-form }
Die [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) Docker-Anweisung kann in zwei Formen geschrieben werden:
✅ **Exec** form:
```Dockerfile
# ✅ Tun Sie das
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ Tun Sie das nicht
CMD fastapi run app/main.py --port 80
```
Achten Sie darauf, stets die **exec** form zu verwenden, um sicherzustellen, dass FastAPI ordnungsgemäß heruntergefahren wird und [Lifespan-Events](../advanced/events.md) ausgelöst werden.
Sie können mehr darüber in der [Docker-Dokumentation für Shell und Exec Form lesen](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Dies kann insbesondere bei der Verwendung von `docker compose` deutlich spürbar sein. Sehen Sie sich diesen Abschnitt in der Docker Compose-FAQ für technische Details an: [Warum benötigen meine Dienste 10 Sekunden, um neu erstellt oder gestoppt zu werden?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Verzeichnisstruktur { #directory-structure }
Sie sollten jetzt eine Verzeichnisstruktur wie diese haben:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Hinter einem TLS-Terminierungsproxy { #behind-a-tls-termination-proxy }
Wenn Sie Ihren Container hinter einem TLS-Terminierungsproxy (Load Balancer) wie Nginx oder Traefik ausführen, fügen Sie die Option `--proxy-headers` hinzu. Das sagt Uvicorn (durch das FastAPI CLI), den von diesem Proxy gesendeten Headern zu vertrauen und dass die Anwendung hinter HTTPS ausgeführt wird, usw.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Docker-Cache { #docker-cache }
In diesem `Dockerfile` gibt es einen wichtigen Trick: Wir kopieren zuerst die **Datei nur mit den Abhängigkeiten**, nicht den Rest des Codes. Lassen Sie mich Ihnen erklären, warum.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker und andere Tools **erstellen** diese Containerimages **inkrementell**, fügen **eine Ebene über der anderen** hinzu, beginnend am Anfang des `Dockerfile`s und fügen alle durch die einzelnen Anweisungen des `Dockerfile`s erstellten Dateien hinzu.
Docker und ähnliche Tools verwenden beim Erstellen des Images auch einen **internen Cache**. Wenn sich eine Datei seit der letzten Erstellung des Containerimages nicht geändert hat, wird **dieselbe Ebene wiederverwendet**, die beim letzten Mal erstellt wurde, anstatt die Datei erneut zu kopieren und eine neue Ebene von Grund auf zu erstellen.
Das bloße Vermeiden des Kopierens von Dateien führt nicht unbedingt zu einer großen Verbesserung, aber da der Cache für diesen Schritt verwendet wurde, kann **der Cache für den nächsten Schritt verwendet werden**. Beispielsweise könnte der Cache verwendet werden für die Anweisung, welche die Abhängigkeiten installiert mit:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Die Datei mit den Paketanforderungen wird sich **nicht häufig ändern**. Wenn Docker also nur diese Datei kopiert, kann es für diesen Schritt **den Cache verwenden**.
Und dann kann Docker **den Cache für den nächsten Schritt verwenden**, der diese Abhängigkeiten herunterlädt und installiert. Und hier **sparen wir viel Zeit**. ✨ ... und vermeiden die Langeweile beim Warten. 😪😆
Das Herunterladen und Installieren der Paketabhängigkeiten **könnte Minuten dauern**, aber die Verwendung des **Cache** würde höchstens **Sekunden** dauern.
Und da Sie das Containerimage während der Entwicklung immer wieder erstellen würden, um zu überprüfen, ob Ihre Codeänderungen funktionieren, würde dies viel Zeit sparen.
Dann, gegen Ende des `Dockerfile`s, kopieren wir den gesamten Code. Da sich der **am häufigsten ändert**, platzieren wir das am Ende, da fast immer alles nach diesem Schritt nicht mehr in der Lage sein wird, den Cache zu verwenden.
```Dockerfile
COPY ./app /code/app
```
### Das Docker-Image erstellen { #build-the-docker-image }
Nachdem nun alle Dateien vorhanden sind, erstellen wir das Containerimage.
* Gehen Sie zum Projektverzeichnis (dort, wo sich Ihr `Dockerfile` und Ihr `app`-Verzeichnis befindet).
* Erstellen Sie Ihr FastAPI-Image:
/// tip | Tipp
Beachten Sie das `.` am Ende, es entspricht `./` und teilt Docker mit, welches Verzeichnis zum Erstellen des Containerimages verwendet werden soll.
In diesem Fall handelt es sich um dasselbe aktuelle Verzeichnis (`.`).
///
### Den Docker-Container starten { #start-the-docker-container }
* Führen Sie einen Container basierend auf Ihrem Image aus:
## Es testen { #check-it }
Sie sollten es in der URL Ihres Docker-Containers überprüfen können, zum Beispiel: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) oder [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (oder gleichwertig, unter Verwendung Ihres Docker-Hosts).
Sie werden etwas sehen wie:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Interaktive API-Dokumentation { #interactive-api-docs }
Jetzt können Sie auf [http://192.168.99.100/docs](http://192.168.99.100/docs) oder [http://127.0.0.1/docs](http://127.0.0.1/docs) gehen (oder ähnlich, unter Verwendung Ihres Docker-Hosts).
Sie sehen die automatische interaktive API-Dokumentation (bereitgestellt von [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Alternative API-Dokumentation { #alternative-api-docs }
Sie können auch auf [http://192.168.99.100/redoc](http://192.168.99.100/redoc) oder [http://127.0.0.1/redoc](http://127.0.0.1/redoc) gehen (oder ähnlich, unter Verwendung Ihres Docker-Hosts).
Sie sehen die alternative automatische Dokumentation (bereitgestellt von [ReDoc](https://github.com/Rebilly/ReDoc)):

## Ein Docker-Image mit einem Single-File-FastAPI erstellen { #build-a-docker-image-with-a-single-file-fastapi }
Wenn Ihr FastAPI eine einzelne Datei ist, zum Beispiel `main.py` ohne ein `./app`-Verzeichnis, könnte Ihre Dateistruktur wie folgt aussehen:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Dann müssten Sie nur noch die entsprechenden Pfade ändern, um die Datei im `Dockerfile` zu kopieren:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Kopiere die Datei `main.py` direkt in das Verzeichnis `/code` (ohne ein Verzeichnis `./app`).
2. Verwenden Sie `fastapi run`, um Ihre Anwendung in der einzelnen Datei `main.py` bereitzustellen.
Indem Sie die Datei an `fastapi run` übergeben, wird automatisch erkannt, dass es sich um eine einzelne Datei handelt und nicht um den Teil eines Packages, und es wird wissen, wie es zu importieren ist und Ihre FastAPI-App bereitzustellen. 😎
## Deployment-Konzepte { #deployment-concepts }
Lassen Sie uns noch einmal über einige der gleichen [Deployment-Konzepte](concepts.md) in Bezug auf Container sprechen.
Container sind hauptsächlich ein Werkzeug, um den Prozess des **Erstellens und Deployments** einer Anwendung zu vereinfachen, sie erzwingen jedoch keinen bestimmten Ansatz für die Handhabung dieser **Deployment-Konzepte**, und es gibt mehrere mögliche Strategien.
Die **gute Nachricht** ist, dass es mit jeder unterschiedlichen Strategie eine Möglichkeit gibt, alle Deployment-Konzepte abzudecken. 🎉
Sehen wir uns diese **Deployment-Konzepte** im Hinblick auf Container noch einmal an:
* HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Arbeitsspeicher
* Schritte vor dem Start
## HTTPS { #https }
Wenn wir uns nur auf das **Containerimage** für eine FastAPI-Anwendung (und später auf den laufenden **Container**) konzentrieren, würde HTTPS normalerweise **extern** von einem anderen Tool verarbeitet.
Es könnte sich um einen anderen Container handeln, zum Beispiel mit [Traefik](https://traefik.io/), welcher **HTTPS** und **automatischen** Erwerb von **Zertifikaten** handhabt.
/// tip | Tipp
Traefik verfügt über Integrationen mit Docker, Kubernetes und anderen, sodass Sie damit ganz einfach HTTPS für Ihre Container einrichten und konfigurieren können.
///
Alternativ könnte HTTPS von einem Cloud-Anbieter als einer seiner Dienste gehandhabt werden (während die Anwendung weiterhin in einem Container ausgeführt wird).
## Beim Hochfahren ausführen und Neustarts { #running-on-startup-and-restarts }
Normalerweise gibt es ein anderes Tool, das für das **Starten und Ausführen** Ihres Containers zuständig ist.
Es könnte sich um **Docker** direkt, **Docker Compose**, **Kubernetes**, einen **Cloud-Dienst**, usw. handeln.
In den meisten (oder allen) Fällen gibt es eine einfache Option, um die Ausführung des Containers beim Hochfahren und Neustarts bei Fehlern zu ermöglichen. In Docker ist es beispielsweise die Befehlszeilenoption `--restart`.
Ohne die Verwendung von Containern kann es umständlich und schwierig sein, Anwendungen beim Hochfahren auszuführen und neu zu starten. Bei der **Arbeit mit Containern** ist diese Funktionalität jedoch in den meisten Fällen standardmäßig enthalten. ✨
## Replikation – Anzahl der Prozesse { #replication-number-of-processes }
Wenn Sie einen Cluster von Maschinen mit **Kubernetes**, Docker Swarm Mode, Nomad verwenden, oder einem anderen, ähnlich komplexen System zur Verwaltung verteilter Container auf mehreren Maschinen, möchten Sie wahrscheinlich die **Replikation auf Cluster-Ebene abwickeln**, anstatt in jedem Container einen **Prozessmanager** (wie Uvicorn mit Workern) zu verwenden.
Diese verteilten Containerverwaltungssysteme wie Kubernetes verfügen normalerweise über eine integrierte Möglichkeit, die **Replikation von Containern** zu handhaben und gleichzeitig **Load Balancing** für die eingehenden Requests zu unterstützen. Alles auf **Cluster-Ebene**.
In diesen Fällen möchten Sie wahrscheinlich ein **Docker-Image von Grund auf** erstellen, wie [oben erklärt](#dockerfile), Ihre Abhängigkeiten installieren und **einen einzelnen Uvicorn-Prozess** ausführen, anstatt mehrere Uvicorn-Worker zu verwenden.
### Load Balancer { #load-balancer }
Bei der Verwendung von Containern ist normalerweise eine Komponente vorhanden, **die am Hauptport lauscht**. Es könnte sich um einen anderen Container handeln, der auch ein **TLS-Terminierungsproxy** ist, um **HTTPS** zu verarbeiten, oder ein ähnliches Tool.
Da diese Komponente die **Last** an Requests aufnehmen und diese (hoffentlich) **ausgewogen** auf die Worker verteilen würde, wird sie üblicherweise auch **Load Balancer** genannt.
/// tip | Tipp
Die gleiche **TLS-Terminierungsproxy**-Komponente, die für HTTPS verwendet wird, wäre wahrscheinlich auch ein **Load Balancer**.
///
Und wenn Sie mit Containern arbeiten, verfügt das gleiche System, mit dem Sie diese starten und verwalten, bereits über interne Tools, um die **Netzwerkkommunikation** (z. B. HTTP-Requests) von diesem **Load Balancer** (das könnte auch ein **TLS-Terminierungsproxy** sein) zu den Containern mit Ihrer Anwendung weiterzuleiten.
### Ein Load Balancer – mehrere Workercontainer { #one-load-balancer-multiple-worker-containers }
Bei der Arbeit mit **Kubernetes** oder ähnlichen verteilten Containerverwaltungssystemen würde die Verwendung ihrer internen Netzwerkmechanismen es dem einzelnen **Load Balancer**, der den Haupt-**Port** überwacht, ermöglichen, Kommunikation (Requests) an möglicherweise **mehrere Container** weiterzuleiten, in denen Ihre Anwendung ausgeführt wird.
Jeder dieser Container, in denen Ihre Anwendung ausgeführt wird, verfügt normalerweise über **nur einen Prozess** (z. B. einen Uvicorn-Prozess, der Ihre FastAPI-Anwendung ausführt). Es wären alles **identische Container**, die das Gleiche ausführen, welche aber jeweils über einen eigenen Prozess, Speicher, usw. verfügen. Auf diese Weise würden Sie die **Parallelisierung** in **verschiedenen Kernen** der CPU nutzen. Oder sogar in **verschiedenen Maschinen**.
Und das verteilte Containersystem mit dem **Load Balancer** würde **die Requests abwechselnd** an jeden einzelnen Container mit Ihrer Anwendung verteilen. Jeder Request könnte also von einem der mehreren **replizierten Container** verarbeitet werden, in denen Ihre Anwendung ausgeführt wird.
Und normalerweise wäre dieser **Load Balancer** in der Lage, Requests zu verarbeiten, die an *andere* Anwendungen in Ihrem Cluster gerichtet sind (z. B. eine andere Domain oder unter einem anderen URL-Pfad-Präfix), und würde diese Kommunikation an die richtigen Container weiterleiten für *diese andere* Anwendung, die in Ihrem Cluster ausgeführt wird.
### Ein Prozess pro Container { #one-process-per-container }
In einem solchen Szenario möchten Sie wahrscheinlich **einen einzelnen (Uvicorn-)Prozess pro Container** haben, da Sie die Replikation bereits auf Cluster-Ebene durchführen würden.
In diesem Fall möchten Sie also **nicht** mehrere Worker im Container haben, z. B. mit der `--workers` Befehlszeilenoption. Sie möchten nur einen **einzelnen Uvicorn-Prozess** pro Container haben (wahrscheinlich aber mehrere Container).
Ein weiterer Prozessmanager im Container (wie es bei mehreren Workern der Fall wäre) würde nur **unnötige Komplexität** hinzufügen, um welche Sie sich höchstwahrscheinlich bereits mit Ihrem Clustersystem kümmern.
### Container mit mehreren Prozessen und Sonderfälle { #containers-with-multiple-processes-and-special-cases }
Natürlich gibt es **Sonderfälle**, in denen Sie **einen Container** mit mehreren **Uvicorn-Workerprozessen** haben möchten.
In diesen Fällen können Sie die `--workers` Befehlszeilenoption verwenden, um die Anzahl der zu startenden Worker festzulegen:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Hier verwenden wir die `--workers` Befehlszeilenoption, um die Anzahl der Worker auf 4 festzulegen.
Hier sind einige Beispiele, wann das sinnvoll sein könnte:
#### Eine einfache Anwendung { #a-simple-app }
Sie könnten einen Prozessmanager im Container haben wollen, wenn Ihre Anwendung **einfach genug** ist, sodass Sie es auf einem **einzelnen Server** ausführen können, nicht auf einem Cluster.
#### Docker Compose { #docker-compose }
Sie könnten das Deployment auf einem **einzelnen Server** (kein Cluster) mit **Docker Compose** durchführen, sodass Sie keine einfache Möglichkeit hätten, die Replikation von Containern (mit Docker Compose) zu verwalten und gleichzeitig das gemeinsame Netzwerk mit **Load Balancing** zu haben.
Dann möchten Sie vielleicht **einen einzelnen Container** mit einem **Prozessmanager** haben, der darin **mehrere Workerprozesse** startet.
---
Der Hauptpunkt ist, dass **keine** dieser Regeln **in Stein gemeißelt** ist, der man blind folgen muss. Sie können diese Ideen verwenden, um **Ihren eigenen Anwendungsfall zu evaluieren**, zu entscheiden, welcher Ansatz für Ihr System am besten geeignet ist und herauszufinden, wie Sie folgende Konzepte verwalten:
* Sicherheit – HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Arbeitsspeicher
* Schritte vor dem Start
## Arbeitsspeicher { #memory }
Wenn Sie **einen einzelnen Prozess pro Container** ausführen, wird von jedem dieser Container (mehr als einer, wenn sie repliziert werden) eine mehr oder weniger klar definierte, stabile und begrenzte Menge an Arbeitsspeicher verbraucht.
Und dann können Sie dieselben Speichergrenzen und -anforderungen in Ihren Konfigurationen für Ihr Container-Management-System festlegen (z. B. in **Kubernetes**). Auf diese Weise ist es in der Lage, die Container auf den **verfügbaren Maschinen** zu replizieren, wobei die von diesen benötigte Speichermenge und die auf den Maschinen im Cluster verfügbare Menge berücksichtigt werden.
Wenn Ihre Anwendung **einfach** ist, wird dies wahrscheinlich **kein Problem darstellen** und Sie müssen möglicherweise keine festen Speichergrenzen angeben. Wenn Sie jedoch **viel Speicher verbrauchen** (z. B. bei **Modellen für maschinelles Lernen**), sollten Sie überprüfen, wie viel Speicher Sie verbrauchen, und die **Anzahl der Container** anpassen, die in **jeder Maschine** ausgeführt werden (und möglicherweise weitere Maschinen zu Ihrem Cluster hinzufügen).
Wenn Sie **mehrere Prozesse pro Container** ausführen, müssen Sie sicherstellen, dass die Anzahl der gestarteten Prozesse nicht **mehr Speicher verbraucht** als verfügbar ist.
## Schritte vor dem Start und Container { #previous-steps-before-starting-and-containers }
Wenn Sie Container (z. B. Docker, Kubernetes) verwenden, können Sie hauptsächlich zwei Ansätze verwenden.
### Mehrere Container { #multiple-containers }
Wenn Sie **mehrere Container** haben, von denen wahrscheinlich jeder einen **einzelnen Prozess** ausführt (z. B. in einem **Kubernetes**-Cluster), dann möchten Sie wahrscheinlich einen **separaten Container** haben, welcher die Arbeit der **Vorab-Schritte** in einem einzelnen Container, mit einem einzelnen Prozess ausführt, **bevor** die replizierten Workercontainer ausgeführt werden.
/// info | Info
Wenn Sie Kubernetes verwenden, wäre dies wahrscheinlich ein [Init-Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Wenn es in Ihrem Anwendungsfall kein Problem darstellt, diese vorherigen Schritte **mehrmals parallel** auszuführen (z. B. wenn Sie keine Datenbankmigrationen ausführen, sondern nur prüfen, ob die Datenbank bereits bereit ist), können Sie sie auch einfach in jedem Container direkt vor dem Start des Hauptprozesses einfügen.
### Einzelner Container { #single-container }
Wenn Sie ein einfaches Setup mit einem **einzelnen Container** haben, welcher dann mehrere **Workerprozesse** (oder auch nur einen Prozess) startet, können Sie die Vorab-Schritte im selben Container direkt vor dem Starten des Prozesses mit der Anwendung ausführen.
### Docker-Basisimage { #base-docker-image }
Es gab ein offizielles FastAPI-Docker-Image: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Dieses ist jedoch jetzt deprecatet. ⛔️
Sie sollten wahrscheinlich **nicht** dieses Basis-Docker-Image (oder ein anderes ähnliches) verwenden.
Wenn Sie **Kubernetes** (oder andere) verwenden und bereits **Replikation** auf Cluster-Ebene mit mehreren **Containern** eingerichtet haben. In diesen Fällen ist es besser, **ein Image von Grund auf neu zu erstellen**, wie oben beschrieben: [Ein Docker-Image für FastAPI erstellen](#build-a-docker-image-for-fastapi).
Und wenn Sie mehrere Worker benötigen, können Sie einfach die `--workers` Befehlszeilenoption verwenden.
/// note | Technische Details
Das Docker-Image wurde erstellt, als Uvicorn das Verwalten und Neustarten von ausgefallenen Workern noch nicht unterstützte, weshalb es notwendig war, Gunicorn mit Uvicorn zu verwenden, was zu einer erheblichen Komplexität führte, nur damit Gunicorn die Uvicorn-Workerprozesse verwaltet und neu startet.
Aber jetzt, da Uvicorn (und der `fastapi`-Befehl) die Verwendung von `--workers` unterstützen, gibt es keinen Grund, ein Basis-Docker-Image an Stelle eines eigenen (das praktisch denselben Code enthält 😅) zu verwenden.
///
## Deployment des Containerimages { #deploy-the-container-image }
Nachdem Sie ein Containerimage (Docker) haben, gibt es mehrere Möglichkeiten, es bereitzustellen.
Zum Beispiel:
* Mit **Docker Compose** auf einem einzelnen Server
* Mit einem **Kubernetes**-Cluster
* Mit einem Docker Swarm Mode-Cluster
* Mit einem anderen Tool wie Nomad
* Mit einem Cloud-Dienst, der Ihr Containerimage nimmt und es deployt
## Docker-Image mit `uv` { #docker-image-with-uv }
Wenn Sie [uv](https://github.com/astral-sh/uv) verwenden, um Ihr Projekt zu installieren und zu verwalten, können Sie deren [uv-Docker-Leitfaden](https://docs.astral.sh/uv/guides/integration/docker/) befolgen.
## Zusammenfassung { #recap }
Mithilfe von Containersystemen (z. B. mit **Docker** und **Kubernetes**) ist es ziemlich einfach, alle **Deployment-Konzepte** zu handhaben:
* HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Arbeitsspeicher
* Schritte vor dem Start
In den meisten Fällen möchten Sie wahrscheinlich kein Basisimage verwenden und stattdessen **ein Containerimage von Grund auf erstellen**, eines basierend auf dem offiziellen Python-Docker-Image.
Indem Sie auf die **Reihenfolge** der Anweisungen im `Dockerfile` und den **Docker-Cache** achten, können Sie **die Build-Zeiten minimieren**, um Ihre Produktivität zu erhöhen (und Langeweile zu vermeiden). 😎
================================================
FILE: docs/de/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Sie können Ihre FastAPI-App in der [FastAPI Cloud](https://fastapicloud.com) mit **einem einzigen Befehl** deployen – tragen Sie sich in die Warteliste ein, falls noch nicht geschehen. 🚀
## Anmelden { #login }
Stellen Sie sicher, dass Sie bereits ein **FastAPI-Cloud-Konto** haben (wir haben Sie von der Warteliste eingeladen 😉).
Melden Sie sich dann an:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Deployen { #deploy }
Stellen Sie Ihre App jetzt mit **einem einzigen Befehl** bereit:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Das war’s! Jetzt können Sie Ihre App unter dieser URL aufrufen. ✨
## Über FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** wird vom gleichen Autor und Team hinter **FastAPI** entwickelt.
Es vereinfacht den Prozess des **Erstellens**, **Deployens** und **Nutzens** einer API mit minimalem Aufwand.
Es bringt die gleiche **Developer-Experience** beim Erstellen von Apps mit FastAPI auch zum **Deployment** in der Cloud. 🎉
Es kümmert sich außerdem um das meiste, was beim Deployen einer App nötig ist, zum Beispiel:
* HTTPS
* Replikation, mit Autoscaling basierend auf Requests
* usw.
FastAPI Cloud ist Hauptsponsor und Finanzierer der Open-Source-Projekte *FastAPI and friends*. ✨
## Bei anderen Cloudanbietern deployen { #deploy-to-other-cloud-providers }
FastAPI ist Open Source und basiert auf Standards. Sie können FastAPI-Apps bei jedem Cloudanbieter Ihrer Wahl deployen.
Folgen Sie den Anleitungen Ihres Cloudanbieters, um dort FastAPI-Apps zu deployen. 🤓
## Auf den eigenen Server deployen { #deploy-your-own-server }
Ich werde Ihnen später in diesem **Deployment-Leitfaden** auch alle Details zeigen, sodass Sie verstehen, was passiert, was geschehen muss und wie Sie FastAPI-Apps selbst deployen können, auch auf Ihre eigenen Server. 🤓
================================================
FILE: docs/de/docs/deployment/https.md
================================================
# Über HTTPS { #about-https }
Es ist leicht anzunehmen, dass HTTPS etwas ist, was einfach nur „aktiviert“ wird oder nicht.
Aber es ist viel komplexer als das.
/// tip | Tipp
Wenn Sie es eilig haben oder es Ihnen egal ist, fahren Sie mit den nächsten Abschnitten fort, um Schritt-für-Schritt-Anleitungen für die Einrichtung der verschiedenen Technologien zu erhalten.
///
Um **die Grundlagen von HTTPS** aus Sicht des Benutzers zu erlernen, schauen Sie sich [https://howhttps.works/](https://howhttps.works/) an.
Aus **Sicht des Entwicklers** sollten Sie beim Nachdenken über HTTPS Folgendes beachten:
* Für HTTPS muss **der Server** über von einem **Dritten** generierte **„Zertifikate“** verfügen.
* Diese Zertifikate werden tatsächlich vom Dritten **erworben** und nicht „generiert“.
* Zertifikate haben eine **Lebensdauer**.
* Sie **verfallen**.
* Und dann müssen sie vom Dritten **erneuert**, **erneut erworben** werden.
* Die Verschlüsselung der Verbindung erfolgt auf **TCP-Ebene**.
* Das ist eine Schicht **unter HTTP**.
* Die Handhabung von **Zertifikaten und Verschlüsselung** erfolgt also **vor HTTP**.
* **TCP weiß nichts über „Domains“**. Nur über IP-Adressen.
* Die Informationen über die angeforderte **spezifische Domain** befinden sich in den **HTTP-Daten**.
* Die **HTTPS-Zertifikate** „zertifizieren“ eine **bestimmte Domain**, aber das Protokoll und die Verschlüsselung erfolgen auf TCP-Ebene, **ohne zu wissen**, um welche Domain es sich handelt.
* **Standardmäßig** bedeutet das, dass Sie nur **ein HTTPS-Zertifikat pro IP-Adresse** haben können.
* Ganz gleich, wie groß Ihr Server ist oder wie klein die einzelnen Anwendungen darauf sind.
* Hierfür gibt es jedoch eine **Lösung**.
* Es gibt eine **Erweiterung** zum **TLS**-Protokoll (dasjenige, das die Verschlüsselung auf TCP-Ebene, vor HTTP, verwaltet) namens **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* Mit dieser SNI-Erweiterung kann ein einzelner Server (mit einer **einzelnen IP-Adresse**) über **mehrere HTTPS-Zertifikate** verfügen und **mehrere HTTPS-Domains/Anwendungen bereitstellen**.
* Damit das funktioniert, muss eine **einzelne** Komponente (Programm), die auf dem Server ausgeführt wird und welche die **öffentliche IP-Adresse** überwacht, **alle HTTPS-Zertifikate** des Servers haben.
* **Nachdem** eine sichere Verbindung hergestellt wurde, ist das Kommunikationsprotokoll **immer noch HTTP**.
* Die Inhalte sind **verschlüsselt**, auch wenn sie mit dem **HTTP-Protokoll** gesendet werden.
Es ist eine gängige Praxis, **ein Programm/HTTP-Server** auf dem Server (der Maschine, dem Host usw.) laufen zu lassen, welches **alle HTTPS-Aspekte verwaltet**: Empfangen der **verschlüsselten HTTPS-Requests**, Senden der **entschlüsselten HTTP-Requests** an die eigentliche HTTP-Anwendung die auf demselben Server läuft (in diesem Fall die **FastAPI**-Anwendung), entgegennehmen der **HTTP-Response** von der Anwendung, **verschlüsseln derselben** mithilfe des entsprechenden **HTTPS-Zertifikats** und Zurücksenden zum Client über **HTTPS**. Dieser Server wird oft als **[TLS-Terminierungsproxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)** bezeichnet.
Einige der Optionen, die Sie als TLS-Terminierungsproxy verwenden können, sind:
* Traefik (kann auch Zertifikat-Erneuerungen durchführen)
* Caddy (kann auch Zertifikat-Erneuerungen durchführen)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Vor Let's Encrypt wurden diese **HTTPS-Zertifikate** von vertrauenswürdigen Dritten verkauft.
Der Prozess zum Erwerb eines dieser Zertifikate war früher umständlich, erforderte viel Papierarbeit und die Zertifikate waren ziemlich teuer.
Aber dann wurde **[Let's Encrypt](https://letsencrypt.org/)** geschaffen.
Es ist ein Projekt der Linux Foundation. Es stellt **kostenlose HTTPS-Zertifikate** automatisiert zur Verfügung. Diese Zertifikate nutzen standardmäßig die gesamte kryptografische Sicherheit und sind kurzlebig (circa 3 Monate), sodass die **Sicherheit tatsächlich besser ist**, aufgrund der kürzeren Lebensdauer.
Die Domains werden sicher verifiziert und die Zertifikate werden automatisch generiert. Das ermöglicht auch die automatische Erneuerung dieser Zertifikate.
Die Idee besteht darin, den Erwerb und die Erneuerung der Zertifikate zu automatisieren, sodass Sie **sicheres HTTPS, kostenlos und für immer** haben können.
## HTTPS für Entwickler { #https-for-developers }
Hier ist ein Beispiel, wie eine HTTPS-API aussehen könnte, Schritt für Schritt, wobei vor allem die für Entwickler wichtigen Ideen berücksichtigt werden.
### Domainname { #domain-name }
Alles beginnt wahrscheinlich damit, dass Sie einen **Domainnamen erwerben**. Anschließend konfigurieren Sie ihn in einem DNS-Server (wahrscheinlich beim selben Cloudanbieter).
Sie würden wahrscheinlich einen Cloud-Server (eine virtuelle Maschine) oder etwas Ähnliches bekommen, und dieser hätte eine feste **öffentliche IP-Adresse**.
In dem oder den DNS-Server(n) würden Sie einen Eintrag (einen „`A record`“) konfigurieren, um mit **Ihrer Domain** auf die öffentliche **IP-Adresse Ihres Servers** zu verweisen.
Sie würden dies wahrscheinlich nur einmal tun, beim ersten Mal, wenn Sie alles einrichten.
/// tip | Tipp
Dieser Domainnamen-Aspekt liegt weit vor HTTPS, aber da alles von der Domain und der IP-Adresse abhängt, lohnt es sich, das hier zu erwähnen.
///
### DNS { #dns }
Konzentrieren wir uns nun auf alle tatsächlichen HTTPS-Aspekte.
Zuerst würde der Browser mithilfe der **DNS-Server** herausfinden, welches die **IP für die Domain** ist, in diesem Fall `someapp.example.com`.
Die DNS-Server geben dem Browser eine bestimmte **IP-Adresse** zurück. Das wäre die von Ihrem Server verwendete öffentliche IP-Adresse, die Sie in den DNS-Servern konfiguriert haben.
### TLS-Handshake-Start { #tls-handshake-start }
Der Browser kommuniziert dann mit dieser IP-Adresse über **Port 443** (den HTTPS-Port).
Der erste Teil der Kommunikation besteht lediglich darin, die Verbindung zwischen dem Client und dem Server herzustellen und die zu verwendenden kryptografischen Schlüssel usw. zu vereinbaren.
Diese Interaktion zwischen dem Client und dem Server zum Aufbau der TLS-Verbindung wird als **TLS-Handshake** bezeichnet.
### TLS mit SNI-Erweiterung { #tls-with-sni-extension }
**Nur ein Prozess** im Server kann an einem bestimmten **Port** einer bestimmten **IP-Adresse** lauschen. Möglicherweise gibt es andere Prozesse, die an anderen Ports dieselbe IP-Adresse abhören, jedoch nur einen für jede Kombination aus IP-Adresse und Port.
TLS (HTTPS) verwendet standardmäßig den spezifischen Port `443`. Das ist also der Port, den wir brauchen.
Da an diesem Port nur ein Prozess lauschen kann, wäre der Prozess, der dies tun würde, der **TLS-Terminierungsproxy**.
Der TLS-Terminierungsproxy hätte Zugriff auf ein oder mehrere **TLS-Zertifikate** (HTTPS-Zertifikate).
Mithilfe der oben beschriebenen **SNI-Erweiterung** würde der TLS-Terminierungsproxy herausfinden, welches der verfügbaren TLS-Zertifikate (HTTPS) er für diese Verbindung verwenden muss, und zwar das, welches mit der vom Client erwarteten Domain übereinstimmt.
In diesem Fall würde er das Zertifikat für `someapp.example.com` verwenden.
Der Client **vertraut** bereits der Entität, die das TLS-Zertifikat generiert hat (in diesem Fall Let's Encrypt, aber wir werden später mehr darüber erfahren), sodass er **verifizieren** kann, dass das Zertifikat gültig ist.
Mithilfe des Zertifikats entscheiden der Client und der TLS-Terminierungsproxy dann, **wie der Rest der TCP-Kommunikation verschlüsselt werden soll**. Damit ist der **TLS-Handshake** abgeschlossen.
Danach verfügen der Client und der Server über eine **verschlüsselte TCP-Verbindung**, via TLS. Und dann können sie diese Verbindung verwenden, um die eigentliche **HTTP-Kommunikation** zu beginnen.
Und genau das ist **HTTPS**, es ist einfach **HTTP** innerhalb einer **sicheren TLS-Verbindung**, statt einer puren (unverschlüsselten) TCP-Verbindung.
/// tip | Tipp
Beachten Sie, dass die Verschlüsselung der Kommunikation auf der **TCP-Ebene** und nicht auf der HTTP-Ebene erfolgt.
///
### HTTPS-Request { #https-request }
Da Client und Server (sprich, der Browser und der TLS-Terminierungsproxy) nun über eine **verschlüsselte TCP-Verbindung** verfügen, können sie die **HTTP-Kommunikation** starten.
Der Client sendet also einen **HTTPS-Request**. Das ist einfach ein HTTP-Request über eine verschlüsselte TLS-Verbindung.
### Den Request entschlüsseln { #decrypt-the-request }
Der TLS-Terminierungsproxy würde die vereinbarte Verschlüsselung zum **Entschlüsseln des Requests** verwenden und den **einfachen (entschlüsselten) HTTP-Request** an den Prozess weiterleiten, der die Anwendung ausführt (z. B. einen Prozess, bei dem Uvicorn die FastAPI-Anwendung ausführt).
### HTTP-Response { #http-response }
Die Anwendung würde den Request verarbeiten und eine **einfache (unverschlüsselte) HTTP-Response** an den TLS-Terminierungsproxy senden.
### HTTPS-Response { #https-response }
Der TLS-Terminierungsproxy würde dann die Response mithilfe der zuvor vereinbarten Kryptografie (als das Zertifikat für `someapp.example.com` verhandelt wurde) **verschlüsseln** und sie an den Browser zurücksenden.
Als Nächstes überprüft der Browser, ob die Response gültig und mit dem richtigen kryptografischen Schlüssel usw. verschlüsselt ist. Anschließend **entschlüsselt er die Response** und verarbeitet sie.
Der Client (Browser) weiß, dass die Response vom richtigen Server kommt, da dieser die Kryptografie verwendet, die zuvor mit dem **HTTPS-Zertifikat** vereinbart wurde.
### Mehrere Anwendungen { #multiple-applications }
Auf demselben Server (oder denselben Servern) könnten sich **mehrere Anwendungen** befinden, beispielsweise andere API-Programme oder eine Datenbank.
Nur ein Prozess kann diese spezifische IP und den Port verarbeiten (in unserem Beispiel der TLS-Terminierungsproxy), aber die anderen Anwendungen/Prozesse können auch auf dem/den Server(n) ausgeführt werden, solange sie nicht versuchen, dieselbe **Kombination aus öffentlicher IP und Port** zu verwenden.
Auf diese Weise könnte der TLS-Terminierungsproxy HTTPS und Zertifikate für **mehrere Domains**, für mehrere Anwendungen, verarbeiten und die Requests dann jeweils an die richtige Anwendung weiterleiten.
### Verlängerung des Zertifikats { #certificate-renewal }
Irgendwann in der Zukunft würde jedes Zertifikat **ablaufen** (etwa 3 Monate nach dem Erwerb).
Und dann gäbe es ein anderes Programm (in manchen Fällen ist es ein anderes Programm, in manchen Fällen ist es derselbe TLS-Terminierungsproxy), das mit Let's Encrypt kommuniziert und das/die Zertifikat(e) erneuert.
Die **TLS-Zertifikate** sind **einem Domainnamen zugeordnet**, nicht einer IP-Adresse.
Um die Zertifikate zu erneuern, muss das erneuernde Programm der Behörde (Let's Encrypt) **nachweisen**, dass es diese Domain tatsächlich **besitzt und kontrolliert**.
Um dies zu erreichen und den unterschiedlichen Anwendungsanforderungen gerecht zu werden, gibt es mehrere Möglichkeiten. Einige beliebte Methoden sind:
* **Einige DNS-Einträge ändern**.
* Hierfür muss das erneuernde Programm die APIs des DNS-Anbieters unterstützen. Je nachdem, welchen DNS-Anbieter Sie verwenden, kann dies eine Option sein oder auch nicht.
* **Als Server ausführen** (zumindest während des Zertifikatserwerbsvorgangs), auf der öffentlichen IP-Adresse, die der Domain zugeordnet ist.
* Wie oben erwähnt, kann nur ein Prozess eine bestimmte IP und einen bestimmten Port überwachen.
* Das ist einer der Gründe, warum es sehr nützlich ist, wenn derselbe TLS-Terminierungsproxy auch den Zertifikats-Erneuerungsprozess übernimmt.
* Andernfalls müssen Sie möglicherweise den TLS-Terminierungsproxy vorübergehend stoppen, das Programm starten, welches die neuen Zertifikate beschafft, diese dann mit dem TLS-Terminierungsproxy konfigurieren und dann den TLS-Terminierungsproxy neu starten. Das ist nicht ideal, da Ihre Anwendung(en) während der Zeit, in der der TLS-Terminierungsproxy ausgeschaltet ist, nicht erreichbar ist/sind.
Dieser ganze Erneuerungsprozess, während die Anwendung weiterhin bereitgestellt wird, ist einer der Hauptgründe, warum Sie ein **separates System zur Verarbeitung von HTTPS** mit einem TLS-Terminierungsproxy haben möchten, anstatt einfach die TLS-Zertifikate direkt mit dem Anwendungsserver zu verwenden (z. B. Uvicorn).
## Proxy-Forwarded-Header { #proxy-forwarded-headers }
Wenn Sie einen Proxy zur Verarbeitung von HTTPS verwenden, weiß Ihr **Anwendungsserver** (z. B. Uvicorn über das FastAPI CLI) nichts über den HTTPS-Prozess, er kommuniziert per einfachem HTTP mit dem **TLS-Terminierungsproxy**.
Dieser **Proxy** würde normalerweise unmittelbar vor dem Übermitteln der Anfrage an den **Anwendungsserver** einige HTTP-Header dynamisch setzen, um dem Anwendungsserver mitzuteilen, dass der Request vom Proxy **weitergeleitet** wird.
/// note | Technische Details
Die Proxy-Header sind:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Trotzdem, da der **Anwendungsserver** nicht weiß, dass er sich hinter einem vertrauenswürdigen **Proxy** befindet, würde er diesen Headern standardmäßig nicht vertrauen.
Sie können den **Anwendungsserver** jedoch so konfigurieren, dass er den vom **Proxy** gesendeten *Forwarded*-Headern vertraut. Wenn Sie das FastAPI CLI verwenden, können Sie die *CLI-Option* `--forwarded-allow-ips` nutzen, um anzugeben, von welchen IPs er diesen *Forwarded*-Headern vertrauen soll.
Wenn der **Anwendungsserver** beispielsweise nur Kommunikation vom vertrauenswürdigen **Proxy** empfängt, können Sie `--forwarded-allow-ips="*"` setzen, um allen eingehenden IPs zu vertrauen, da er nur Requests von der vom **Proxy** verwendeten IP erhalten wird.
Auf diese Weise kann die Anwendung ihre eigene öffentliche URL, ob sie HTTPS verwendet, die Domain, usw. erkennen.
Das ist z. B. nützlich, um Redirects korrekt zu handhaben.
/// tip | Tipp
Mehr dazu finden Sie in der Dokumentation zu [Hinter einem Proxy – Proxy-Forwarded-Header aktivieren](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Zusammenfassung { #recap }
**HTTPS** zu haben ist sehr wichtig und in den meisten Fällen eine **kritische Anforderung**. Die meiste Arbeit, die Sie als Entwickler in Bezug auf HTTPS aufwenden müssen, besteht lediglich darin, **diese Konzepte zu verstehen** und wie sie funktionieren.
Sobald Sie jedoch die grundlegenden Informationen zu **HTTPS für Entwickler** kennen, können Sie verschiedene Tools problemlos kombinieren und konfigurieren, um alles auf einfache Weise zu verwalten.
In einigen der nächsten Kapitel zeige ich Ihnen einige konkrete Beispiele für die Einrichtung von **HTTPS** für **FastAPI**-Anwendungen. 🔒
================================================
FILE: docs/de/docs/deployment/index.md
================================================
# Deployment { #deployment }
Das Deployment einer **FastAPI**-Anwendung ist relativ einfach.
## Was bedeutet Deployment { #what-does-deployment-mean }
**Deployment** bedeutet, die notwendigen Schritte durchzuführen, um die Anwendung **für die Benutzer verfügbar** zu machen.
Bei einer **Web-API** bedeutet das normalerweise, diese auf einem **entfernten Rechner** zu platzieren, mit einem **Serverprogramm**, welches gute Leistung, Stabilität, usw. bietet, damit Ihre **Benutzer** auf die Anwendung effizient und ohne Unterbrechungen oder Probleme **zugreifen** können.
Das steht im Gegensatz zu den **Entwicklungsphasen**, in denen Sie ständig den Code ändern, kaputt machen, reparieren, den Entwicklungsserver stoppen und neu starten, usw.
## Deployment-Strategien { #deployment-strategies }
Es gibt mehrere Möglichkeiten, dies zu tun, abhängig von Ihrem spezifischen Anwendungsfall und den von Ihnen verwendeten Tools.
Sie könnten mithilfe einer Kombination von Tools selbst **einen Server deployen**, Sie könnten einen **Cloud-Dienst** nutzen, der einen Teil der Arbeit für Sie erledigt, oder andere mögliche Optionen.
Zum Beispiel haben wir, das Team hinter FastAPI, [**FastAPI Cloud**](https://fastapicloud.com) entwickelt, um das Deployment von FastAPI-Apps in der Cloud so reibungslos wie möglich zu gestalten, mit derselben Developer-Experience wie beim Arbeiten mit FastAPI.
Ich zeige Ihnen einige der wichtigsten Konzepte, die Sie beim Deployment einer **FastAPI**-Anwendung wahrscheinlich berücksichtigen sollten (obwohl das meiste davon auch für jede andere Art von Webanwendung gilt).
In den nächsten Abschnitten erfahren Sie mehr über die zu beachtenden Details und über die Techniken, das zu tun. ✨
================================================
FILE: docs/de/docs/deployment/manually.md
================================================
# Einen Server manuell ausführen { #run-a-server-manually }
## Den `fastapi run`-Befehl verwenden { #use-the-fastapi-run-command }
Kurz gesagt, nutzen Sie `fastapi run`, um Ihre FastAPI-Anwendung bereitzustellen:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Das würde in den meisten Fällen funktionieren. 😎
Sie könnten diesen Befehl beispielsweise verwenden, um Ihre **FastAPI**-App in einem Container, auf einem Server usw. zu starten.
## ASGI-Server { #asgi-servers }
Lassen Sie uns ein wenig tiefer in die Details eintauchen.
FastAPI verwendet einen Standard zum Erstellen von Python-Webframeworks und -Servern, der als ASGI bekannt ist. FastAPI ist ein ASGI-Webframework.
Das Wichtigste, was Sie benötigen, um eine **FastAPI**-Anwendung (oder eine andere ASGI-Anwendung) auf einer entfernten Servermaschine auszuführen, ist ein ASGI-Serverprogramm wie **Uvicorn**, der standardmäßig im `fastapi`-Kommando enthalten ist.
Es gibt mehrere Alternativen, einschließlich:
* [Uvicorn](https://www.uvicorn.dev/): ein hochperformanter ASGI-Server.
* [Hypercorn](https://hypercorn.readthedocs.io/): ein ASGI-Server, der unter anderem kompatibel mit HTTP/2 und Trio ist.
* [Daphne](https://github.com/django/daphne): der für Django Channels entwickelte ASGI-Server.
* [Granian](https://github.com/emmett-framework/granian): Ein Rust HTTP-Server für Python-Anwendungen.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit ist eine leichte und vielseitige Laufzeitumgebung für Webanwendungen.
## Servermaschine und Serverprogramm { #server-machine-and-server-program }
Es gibt ein kleines Detail bei den Namen, das Sie beachten sollten. 💡
Das Wort „**Server**“ wird häufig verwendet, um sowohl den entfernten/Cloud-Computer (die physische oder virtuelle Maschine) als auch das Programm zu bezeichnen, das auf dieser Maschine läuft (z. B. Uvicorn).
Denken Sie einfach daran, dass sich „Server“ im Allgemeinen auf eines dieser beiden Dinge beziehen kann.
Wenn man sich auf die entfernte Maschine bezieht, wird sie üblicherweise als **Server**, aber auch als **Maschine**, **VM** (virtuelle Maschine) oder **Knoten** bezeichnet. Diese Begriffe beziehen sich auf irgendeine Art von entfernten Rechner, normalerweise unter Linux, auf dem Sie Programme ausführen.
## Das Serverprogramm installieren { #install-the-server-program }
Wenn Sie FastAPI installieren, wird es mit einem Produktionsserver, Uvicorn, geliefert, und Sie können ihn mit dem `fastapi run` Befehl starten.
Aber Sie können auch ein ASGI-Serverprogramm manuell installieren.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und dann die Serveranwendung installieren.
Zum Beispiel, um Uvicorn zu installieren:
Ein ähnlicher Prozess würde für jedes andere ASGI-Serverprogramm gelten.
/// tip | Tipp
Durch das Hinzufügen von `standard` installiert und verwendet Uvicorn einige empfohlene zusätzliche Abhängigkeiten.
Dazu gehört `uvloop`, der hochperformante Drop-in-Ersatz für `asyncio`, der den großen Nebenläufigkeits-Leistungsschub bietet.
Wenn Sie FastAPI mit etwas wie `pip install "fastapi[standard]"` installieren, erhalten Sie auch `uvicorn[standard]`.
///
## Das Serverprogramm ausführen { #run-the-server-program }
Wenn Sie einen ASGI-Server manuell installiert haben, müssen Sie normalerweise einen Importstring in einem speziellen Format übergeben, damit er Ihre FastAPI-Anwendung importiert:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Hinweis
Der Befehl `uvicorn main:app` bezieht sich auf:
* `main`: die Datei `main.py` (das Python-„Modul“).
* `app`: das Objekt, das innerhalb von `main.py` mit der Zeile `app = FastAPI()` erstellt wurde.
Es ist äquivalent zu:
```Python
from main import app
```
///
Jedes alternative ASGI-Serverprogramm hätte einen ähnlichen Befehl, Sie können in deren jeweiligen Dokumentationen mehr lesen.
/// warning | Achtung
Uvicorn und andere Server unterstützen eine `--reload`-Option, die während der Entwicklung nützlich ist.
Die `--reload`-Option verbraucht viel mehr Ressourcen, ist instabiler, usw.
Sie hilft während der **Entwicklung**, Sie sollten sie jedoch **nicht** in der **Produktion** verwenden.
///
## Deployment-Konzepte { #deployment-concepts }
Diese Beispiele führen das Serverprogramm (z. B. Uvicorn) aus, starten **einen einzelnen Prozess** und überwachen alle IPs (`0.0.0.0`) an einem vordefinierten Port (z. B. `80`).
Das ist die Grundidee. Aber Sie möchten sich wahrscheinlich um einige zusätzliche Dinge kümmern, wie zum Beispiel:
* Sicherheit – HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Speicher
* Schritte vor dem Start
In den nächsten Kapiteln erzähle ich Ihnen mehr über jedes dieser Konzepte, wie Sie über diese nachdenken, und gebe Ihnen einige konkrete Beispiele mit Strategien für den Umgang damit. 🚀
================================================
FILE: docs/de/docs/deployment/server-workers.md
================================================
# Serverworker – Uvicorn mit Workern { #server-workers-uvicorn-with-workers }
Schauen wir uns die Deployment-Konzepte von früher noch einmal an:
* Sicherheit – HTTPS
* Beim Hochfahren ausführen
* Neustarts
* **Replikation (die Anzahl der laufenden Prozesse)**
* Arbeitsspeicher
* Schritte vor dem Start
Bis zu diesem Punkt, in allen Tutorials in der Dokumentation, haben Sie wahrscheinlich ein **Serverprogramm** ausgeführt, zum Beispiel mit dem `fastapi`-Befehl, der Uvicorn startet, und einen **einzelnen Prozess** ausführt.
Wenn Sie Anwendungen deployen, möchten Sie wahrscheinlich eine gewisse **Replikation von Prozessen**, um **mehrere Kerne** zu nutzen und mehr Requests bearbeiten zu können.
Wie Sie im vorherigen Kapitel über [Deployment-Konzepte](concepts.md) gesehen haben, gibt es mehrere Strategien, die Sie anwenden können.
Hier zeige ich Ihnen, wie Sie **Uvicorn** mit **Workerprozessen** verwenden, indem Sie den `fastapi`-Befehl oder den `uvicorn`-Befehl direkt verwenden.
/// info | Info
Wenn Sie Container verwenden, beispielsweise mit Docker oder Kubernetes, erzähle ich Ihnen mehr darüber im nächsten Kapitel: [FastAPI in Containern – Docker](docker.md).
Insbesondere wenn die Anwendung auf **Kubernetes** läuft, werden Sie wahrscheinlich **keine** Worker verwenden wollen, und stattdessen **einen einzelnen Uvicorn-Prozess pro Container** ausführen wollen, aber ich werde Ihnen später in diesem Kapitel mehr darüber erzählen.
///
## Mehrere Worker { #multiple-workers }
Sie können mehrere Worker mit der `--workers`-Befehlszeilenoption starten:
//// tab | `fastapi`
Wenn Sie den `fastapi`-Befehl verwenden:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Wenn Sie den `uvicorn`-Befehl direkt verwenden möchten:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
Die einzige neue Option hier ist `--workers`, die Uvicorn anweist, 4 Workerprozesse zu starten.
Sie können auch sehen, dass die **PID** jedes Prozesses angezeigt wird, `27365` für den übergeordneten Prozess (dies ist der **Prozessmanager**) und eine für jeden Workerprozess: `27368`, `27369`, `27370` und `27367`.
## Deployment-Konzepte { #deployment-concepts }
Hier haben Sie gesehen, wie Sie mehrere **Worker** verwenden, um die Ausführung der Anwendung zu **parallelisieren**, **mehrere Kerne** der CPU zu nutzen und in der Lage zu sein, **mehr Requests** zu bearbeiten.
In der Liste der Deployment-Konzepte von oben würde die Verwendung von Workern hauptsächlich bei der **Replikation** und ein wenig bei **Neustarts** helfen, aber Sie müssen sich trotzdem um die anderen kümmern:
* **Sicherheit – HTTPS**
* **Beim Hochfahren ausführen**
* ***Neustarts***
* Replikation (die Anzahl der laufenden Prozesse)
* **Arbeitsspeicher**
* **Schritte vor dem Start**
## Container und Docker { #containers-and-docker }
Im nächsten Kapitel über [FastAPI in Containern – Docker](docker.md) werde ich einige Strategien erläutern, die Sie für den Umgang mit den anderen **Deployment-Konzepten** verwenden können.
Ich zeige Ihnen, wie Sie **Ihr eigenes Image von Grund auf erstellen**, um einen einzelnen Uvicorn-Prozess auszuführen. Es ist ein einfacher Vorgang und wahrscheinlich das, was Sie tun möchten, wenn Sie ein verteiltes Containerverwaltungssystem wie **Kubernetes** verwenden.
## Zusammenfassung { #recap }
Sie können mehrere Workerprozesse mit der `--workers`-CLI-Option über die `fastapi`- oder `uvicorn`-Befehle nutzen, um **Multikern-CPUs** auszunutzen und **mehrere Prozesse parallel** auszuführen.
Sie könnten diese Tools und Ideen nutzen, wenn Sie **Ihr eigenes Deployment-System** einrichten und sich dabei selbst um die anderen Deployment-Konzepte kümmern.
Schauen Sie sich das nächste Kapitel an, um mehr über **FastAPI** mit Containern (z. B. Docker und Kubernetes) zu erfahren. Sie werden sehen, dass diese Tools auch einfache Möglichkeiten bieten, die anderen **Deployment-Konzepte** zu lösen. ✨
================================================
FILE: docs/de/docs/deployment/versions.md
================================================
# Über FastAPI-Versionen { #about-fastapi-versions }
**FastAPI** wird bereits in vielen Anwendungen und Systemen produktiv eingesetzt. Und die Testabdeckung wird bei 100 % gehalten. Aber seine Entwicklung geht immer noch schnell voran.
Es werden regelmäßig neue Funktionen hinzugefügt, Fehler werden regelmäßig behoben und der Code wird weiterhin kontinuierlich verbessert.
Aus diesem Grund sind die aktuellen Versionen immer noch `0.x.x`, was darauf hindeutet, dass jede Version möglicherweise nicht abwärtskompatible Änderungen haben könnte. Dies folgt den Konventionen der [Semantische Versionierung](https://semver.org/).
Sie können jetzt Produktionsanwendungen mit **FastAPI** erstellen (und das tun Sie wahrscheinlich schon seit einiger Zeit), Sie müssen nur sicherstellen, dass Sie eine Version verwenden, die korrekt mit dem Rest Ihres Codes funktioniert.
## Ihre `fastapi`-Version pinnen { #pin-your-fastapi-version }
Als Erstes sollten Sie die Version von **FastAPI**, die Sie verwenden, an die höchste Version „pinnen“, von der Sie wissen, dass sie für Ihre Anwendung korrekt funktioniert.
Angenommen, Sie verwenden in Ihrer App die Version `0.112.0`.
Wenn Sie eine `requirements.txt`-Datei verwenden, können Sie die Version wie folgt angeben:
```txt
fastapi[standard]==0.112.0
```
Das würde bedeuten, dass Sie genau die Version `0.112.0` verwenden.
Oder Sie können sie auch anpinnen mit:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
Das würde bedeuten, dass Sie eine Version `0.112.0` oder höher verwenden würden, aber kleiner als `0.113.0`, beispielsweise würde eine Version `0.112.2` immer noch akzeptiert.
Wenn Sie zum Verwalten Ihrer Installationen andere Tools wie `uv`, Poetry, Pipenv oder andere verwenden, sie verfügen alle über eine Möglichkeit, bestimmte Versionen für Ihre Packages zu definieren.
## Verfügbare Versionen { #available-versions }
Die verfügbaren Versionen können Sie in den [Versionshinweisen](../release-notes.md) einsehen (z. B. um zu überprüfen, welches die neueste Version ist).
## Über Versionen { #about-versions }
Gemäß den Konventionen zur semantischen Versionierung könnte jede Version unter `1.0.0` potenziell nicht abwärtskompatible Änderungen hinzufügen.
FastAPI folgt auch der Konvention, dass jede „PATCH“-Versionsänderung für Bugfixes und abwärtskompatible Änderungen gedacht ist.
/// tip | Tipp
Der „PATCH“ ist die letzte Zahl, zum Beispiel ist in `0.2.3` die PATCH-Version `3`.
///
Sie sollten also in der Lage sein, eine Version wie folgt anzupinnen:
```txt
fastapi>=0.45.0,<0.46.0
```
Nicht abwärtskompatible Änderungen und neue Funktionen werden in „MINOR“-Versionen hinzugefügt.
/// tip | Tipp
„MINOR“ ist die Zahl in der Mitte, zum Beispiel ist in `0.2.3` die MINOR-Version `2`.
///
## Upgrade der FastAPI-Versionen { #upgrading-the-fastapi-versions }
Sie sollten Tests für Ihre App hinzufügen.
Mit **FastAPI** ist das sehr einfach (dank Starlette), schauen Sie sich die Dokumentation an: [Testen](../tutorial/testing.md)
Nachdem Sie Tests erstellt haben, können Sie die **FastAPI**-Version auf eine neuere Version aktualisieren und sicherstellen, dass Ihr gesamter Code ordnungsgemäß funktioniert, indem Sie Ihre Tests ausführen.
Wenn alles funktioniert oder nachdem Sie die erforderlichen Änderungen vorgenommen haben und alle Ihre Tests bestehen, können Sie Ihr `fastapi` an die neue aktuelle Version pinnen.
## Über Starlette { #about-starlette }
Sie sollten die Version von `starlette` nicht pinnen.
Verschiedene Versionen von **FastAPI** verwenden eine bestimmte neuere Version von Starlette.
Sie können **FastAPI** also einfach die korrekte Starlette-Version verwenden lassen.
## Über Pydantic { #about-pydantic }
Pydantic integriert die Tests für **FastAPI** in seine eigenen Tests, sodass neue Versionen von Pydantic (über `1.0.0`) immer mit FastAPI kompatibel sind.
Sie können Pydantic an jede für Sie geeignete Version über `1.0.0` anpinnen.
Zum Beispiel:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/de/docs/editor-support.md
================================================
# Editor-Unterstützung { #editor-support }
Die offizielle [FastAPI-Erweiterung](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) verbessert Ihren FastAPI-Entwicklungsworkflow mit Pfadoperation-Erkennung und -Navigation sowie FastAPI-Cloud-Deployment und Live-Logstreaming.
Weitere Details zur Erweiterung finden Sie im README im [GitHub-Repository](https://github.com/fastapi/fastapi-vscode).
## Einrichtung und Installation { #setup-and-installation }
Die **FastAPI-Erweiterung** ist sowohl für [VS Code](https://code.visualstudio.com/) als auch für [Cursor](https://www.cursor.com/) verfügbar. Sie kann direkt über das Erweiterungen-Panel in jedem Editor installiert werden, indem Sie nach „FastAPI“ suchen und die von **FastAPI Labs** veröffentlichte Erweiterung auswählen. Die Erweiterung funktioniert auch in browserbasierten Editoren wie [vscode.dev](https://vscode.dev) und [github.dev](https://github.dev).
### Anwendungserkennung { #application-discovery }
Standardmäßig erkennt die Erweiterung FastAPI-Anwendungen in Ihrem Workspace automatisch, indem sie nach Dateien sucht, die `FastAPI()` instanziieren. Falls die automatische Erkennung mit Ihrer Projektstruktur nicht funktioniert, können Sie einen Entry-Point über `[tool.fastapi]` in `pyproject.toml` oder die VS-Code-Einstellung `fastapi.entryPoint` in Modulnotation angeben (z. B. `myapp.main:app`).
## Funktionen { #features }
- Pfadoperation-Explorer – Eine Baumansicht in der Seitenleiste aller *Pfadoperationen* in Ihrer Anwendung. Klicken Sie, um zu einer beliebigen Route- oder Router-Definition zu springen.
- Routensuche – Suchen Sie nach Pfad, Methode oder Namen mit Ctrl + Shift + E (unter macOS: Cmd + Shift + E).
- CodeLens-Navigation – Anklickbare Links oberhalb von Testclient-Aufrufen (z. B. `client.get('/items')`), die zur passenden Pfadoperation springen und so eine schnelle Navigation zwischen Tests und Implementierung ermöglichen.
- Zu FastAPI Cloud deployen – Deployment Ihrer App mit einem Klick auf [FastAPI Cloud](https://fastapicloud.com/).
- Anwendungslogs streamen – Echtzeit-Logstreaming Ihrer auf FastAPI Cloud deployten Anwendung mit Loglevel-Filterung und Textsuche.
Wenn Sie sich mit den Funktionen der Erweiterung vertraut machen möchten, können Sie den Erweiterungs‑Walkthrough aufrufen, indem Sie die Befehlspalette öffnen (Ctrl + Shift + P oder unter macOS: Cmd + Shift + P) und „Welcome: Open walkthrough …“ auswählen und anschließend den Walkthrough „Get started with FastAPI“ wählen.
================================================
FILE: docs/de/docs/environment-variables.md
================================================
# Umgebungsvariablen { #environment-variables }
/// tip | Tipp
Wenn Sie bereits wissen, was „Umgebungsvariablen“ sind und wie man sie verwendet, können Sie dies überspringen.
///
Eine Umgebungsvariable (auch bekannt als „**env var**“) ist eine Variable, die **außerhalb** des Python-Codes im **Betriebssystem** lebt und von Ihrem Python-Code (oder auch von anderen Programmen) gelesen werden kann.
Umgebungsvariablen können nützlich sein, um **Einstellungen** der Anwendung zu handhaben, als Teil der **Installation** von Python usw.
## Umgebungsvariablen erstellen und verwenden { #create-and-use-env-vars }
Sie können Umgebungsvariablen in der **Shell (Terminal)** erstellen und verwenden, ohne Python zu benötigen:
//// tab | Linux, macOS, Windows Bash
```console
// Sie können eine Umgebungsvariable MY_NAME erstellen mit
$ export MY_NAME="Wade Wilson"
// Dann können Sie sie mit anderen Programmen verwenden, etwa
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Erstellen Sie eine Umgebungsvariable MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Verwenden Sie sie mit anderen Programmen, etwa
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Umgebungsvariablen in Python lesen { #read-env-vars-in-python }
Sie können auch Umgebungsvariablen **außerhalb** von Python erstellen, im Terminal (oder mit jeder anderen Methode) und sie dann **in Python** lesen.
Zum Beispiel könnten Sie eine Datei `main.py` haben mit:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Tipp
Das zweite Argument von [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) ist der Defaultwert, der zurückgegeben wird.
Wenn er nicht angegeben wird, ist er standardmäßig `None`. Hier geben wir „World“ als den zu verwendenden Defaultwert an.
///
Dann könnten Sie das Python-Programm aufrufen:
//// tab | Linux, macOS, Windows Bash
```console
// Hier setzen wir die Umgebungsvariable noch nicht
$ python main.py
// Da wir die Umgebungsvariable nicht gesetzt haben, erhalten wir den Defaultwert
Hello World from Python
// Aber wenn wir zuerst eine Umgebungsvariable erstellen
$ export MY_NAME="Wade Wilson"
// Und dann das Programm erneut aufrufen
$ python main.py
// Jetzt kann es die Umgebungsvariable lesen
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Hier setzen wir die Umgebungsvariable noch nicht
$ python main.py
// Da wir die Umgebungsvariable nicht gesetzt haben, erhalten wir den Defaultwert
Hello World from Python
// Aber wenn wir zuerst eine Umgebungsvariable erstellen
$ $Env:MY_NAME = "Wade Wilson"
// Und dann das Programm erneut aufrufen
$ python main.py
// Jetzt kann es die Umgebungsvariable lesen
Hello Wade Wilson from Python
```
////
Da Umgebungsvariablen außerhalb des Codes gesetzt werden können, aber vom Code gelesen werden können und nicht mit den restlichen Dateien gespeichert (in `git` committet) werden müssen, werden sie häufig für Konfigurationen oder **Einstellungen** verwendet.
Sie können auch eine Umgebungsvariable nur für einen **spezifischen Programmaufruf** erstellen, die nur für dieses Programm und nur für dessen Dauer verfügbar ist.
Um dies zu tun, erstellen Sie sie direkt vor dem Programmaufruf, in derselben Zeile:
```console
// Erstellen Sie eine Umgebungsvariable MY_NAME in der Zeile für diesen Programmaufruf
$ MY_NAME="Wade Wilson" python main.py
// Jetzt kann es die Umgebungsvariable lesen
Hello Wade Wilson from Python
// Die Umgebungsvariable existiert danach nicht mehr
$ python main.py
Hello World from Python
```
/// tip | Tipp
Sie können mehr darüber lesen auf [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Typen und Validierung { #types-and-validation }
Diese Umgebungsvariablen können nur **Textstrings** handhaben, da sie extern zu Python sind und kompatibel mit anderen Programmen und dem Rest des Systems (und sogar mit verschiedenen Betriebssystemen, wie Linux, Windows, macOS) sein müssen.
Das bedeutet, dass **jeder Wert**, der in Python von einer Umgebungsvariablen gelesen wird, **ein `str` sein wird**, und jede Konvertierung in einen anderen Typ oder jede Validierung muss im Code vorgenommen werden.
Sie werden mehr darüber lernen, wie man Umgebungsvariablen zur Handhabung von **Anwendungseinstellungen** verwendet, im [Handbuch für fortgeschrittene Benutzer – Einstellungen und Umgebungsvariablen](./advanced/settings.md).
## `PATH`-Umgebungsvariable { #path-environment-variable }
Es gibt eine **spezielle** Umgebungsvariable namens **`PATH`**, die von den Betriebssystemen (Linux, macOS, Windows) verwendet wird, um Programme zu finden, die ausgeführt werden sollen.
Der Wert der Variable `PATH` ist ein langer String, der aus Verzeichnissen besteht, die auf Linux und macOS durch einen Doppelpunkt `:` und auf Windows durch ein Semikolon `;` getrennt sind.
Zum Beispiel könnte die `PATH`-Umgebungsvariable so aussehen:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Das bedeutet, dass das System nach Programmen in den Verzeichnissen suchen sollte:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Das bedeutet, dass das System nach Programmen in den Verzeichnissen suchen sollte:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Wenn Sie einen **Befehl** im Terminal eingeben, **sucht** das Betriebssystem nach dem Programm in **jedem dieser Verzeichnisse**, die in der `PATH`-Umgebungsvariablen aufgeführt sind.
Zum Beispiel, wenn Sie `python` im Terminal eingeben, sucht das Betriebssystem nach einem Programm namens `python` im **ersten Verzeichnis** in dieser Liste.
Wenn es es findet, wird es **benutzt**. Andernfalls sucht es weiter in den **anderen Verzeichnissen**.
### Python installieren und den `PATH` aktualisieren { #installing-python-and-updating-the-path }
Wenn Sie Python installieren, könnten Sie gefragt werden, ob Sie die `PATH`-Umgebungsvariable aktualisieren möchten.
//// tab | Linux, macOS
Angenommen, Sie installieren Python und es landet in einem Verzeichnis `/opt/custompython/bin`.
Wenn Sie erlauben, die `PATH`-Umgebungsvariable zu aktualisieren, fügt der Installer `/opt/custompython/bin` zur `PATH`-Umgebungsvariable hinzu.
Das könnte so aussehen:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Auf diese Weise, wenn Sie `python` im Terminal eingeben, findet das System das Python-Programm in `/opt/custompython/bin` (das letzte Verzeichnis) und verwendet dieses.
////
//// tab | Windows
Angenommen, Sie installieren Python und es landet in einem Verzeichnis `C:\opt\custompython\bin`.
Wenn Sie erlauben, die `PATH`-Umgebungsvariable zu aktualisieren, fügt der Installer `C:\opt\custompython\bin` zur `PATH`-Umgebungsvariable hinzu.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Auf diese Weise, wenn Sie `python` im Terminal eingeben, findet das System das Python-Programm in `C:\opt\custompython\bin` (das letzte Verzeichnis) und verwendet dieses.
////
Also, wenn Sie tippen:
```console
$ python
```
//// tab | Linux, macOS
Das System wird das `python` Programm in `/opt/custompython/bin` **finden** und es ausführen.
Es wäre ungefähr gleichbedeutend mit der Eingabe von:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
Das System wird das `python` Programm in `C:\opt\custompython\bin\python` **finden** und es ausführen.
Es wäre ungefähr gleichbedeutend mit der Eingabe von:
```console
$ C:\opt\custompython\bin\python
```
////
Diese Informationen werden nützlich sein, wenn Sie über [Virtuelle Umgebungen](virtual-environments.md) lernen.
## Fazit { #conclusion }
Mit diesem Wissen sollten Sie ein grundlegendes Verständnis davon haben, was **Umgebungsvariablen** sind und wie man sie in Python verwendet.
Sie können auch mehr darüber in der [Wikipedia zu Umgebungsvariablen](https://en.wikipedia.org/wiki/Environment_variable) lesen.
In vielen Fällen ist es nicht sehr offensichtlich, wie Umgebungsvariablen nützlich und sofort anwendbar sein könnten. Aber sie tauchen immer wieder in vielen verschiedenen Szenarien auf, wenn Sie entwickeln, deshalb ist es gut, darüber Bescheid zu wissen.
Zum Beispiel werden Sie diese Informationen im nächsten Abschnitt über [Virtuelle Umgebungen](virtual-environments.md) benötigen.
================================================
FILE: docs/de/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** ist ein Kommandozeilenprogramm, mit dem Sie Ihre FastAPI-App bereitstellen, Ihr FastAPI-Projekt verwalten und mehr.
Wenn Sie FastAPI installieren (z. B. mit `pip install "fastapi[standard]"`), erhalten Sie ein Kommandozeilenprogramm, das Sie im Terminal ausführen können.
Um Ihre FastAPI-App für die Entwicklung auszuführen, können Sie den Befehl `fastapi dev` verwenden:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Tipp
Für die Produktion würden Sie statt `fastapi dev` `fastapi run` verwenden. 🚀
///
Intern verwendet das **FastAPI CLI** [Uvicorn](https://www.uvicorn.dev), einen leistungsstarken, produktionsreifen, ASGI-Server. 😎
Das `fastapi`-CLI versucht automatisch, die auszuführende FastAPI-App zu erkennen, und geht davon aus, dass es sich um ein Objekt namens `app` in einer Datei `main.py` handelt (oder ein paar weitere Varianten).
Sie können aber auch explizit konfigurieren, welche App verwendet werden soll.
## Den App-`entrypoint` in `pyproject.toml` konfigurieren { #configure-the-app-entrypoint-in-pyproject-toml }
Sie können in einer `pyproject.toml`-Datei konfigurieren, wo sich Ihre App befindet, etwa so:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Dieser `entrypoint` teilt dem Befehl `fastapi` mit, dass die App so importiert werden soll:
```python
from main import app
```
Wenn Ihr Code so strukturiert wäre:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Dann würden Sie den `entrypoint` wie folgt setzen:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
was gleichbedeutend wäre mit:
```python
from backend.main import app
```
### `fastapi dev` mit Pfad { #fastapi-dev-with-path }
Sie können auch den Dateipfad an den Befehl `fastapi dev` übergeben, dann wird das zu verwendende FastAPI-App-Objekt erraten:
```console
$ fastapi dev main.py
```
Aber Sie müssten sich merken, bei jedem Aufruf des `fastapi`-Befehls den korrekten Pfad zu übergeben.
Zusätzlich könnten andere Tools sie nicht finden, z. B. die [VS Code Extension](editor-support.md) oder [FastAPI Cloud](https://fastapicloud.com), daher wird empfohlen, den `entrypoint` in `pyproject.toml` zu verwenden.
## `fastapi dev` { #fastapi-dev }
Das Ausführen von `fastapi dev` startet den Entwicklermodus.
Standardmäßig ist **Autoreload** aktiviert, das den Server automatisch neu lädt, wenn Sie Änderungen an Ihrem Code vornehmen. Dies ist ressourcenintensiv und könnte weniger stabil sein als wenn es deaktiviert ist. Sie sollten es nur für die Entwicklung verwenden. Es horcht auch auf der IP-Adresse `127.0.0.1`, die die IP für Ihre Maschine ist, um nur mit sich selbst zu kommunizieren (`localhost`).
## `fastapi run` { #fastapi-run }
Das Ausführen von `fastapi run` startet FastAPI im Produktionsmodus.
Standardmäßig ist **Autoreload** deaktiviert. Es horcht auch auf der IP-Adresse `0.0.0.0`, was alle verfügbaren IP-Adressen bedeutet, so wird es öffentlich zugänglich für jeden, der mit der Maschine kommunizieren kann. So würden Sie es normalerweise in der Produktion ausführen, beispielsweise in einem Container.
In den meisten Fällen würden (und sollten) Sie einen „Terminierungsproxy“ haben, der HTTPS für Sie verwaltet. Dies hängt davon ab, wie Sie Ihre Anwendung deployen. Ihr Anbieter könnte dies für Sie erledigen, oder Sie müssen es selbst einrichten.
/// tip | Tipp
Sie können mehr darüber in der [Deployment-Dokumentation](deployment/index.md) erfahren.
///
================================================
FILE: docs/de/docs/features.md
================================================
# Merkmale { #features }
## FastAPI Merkmale { #fastapi-features }
**FastAPI** ermöglicht Ihnen Folgendes:
### Basiert auf offenen Standards { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) für die Erstellung von APIs, inklusive Deklarationen von Pfad-Operationen, Parametern, Requestbodys, Sicherheit, usw.
* Automatische Dokumentation der Datenmodelle mit [**JSON Schema**](https://json-schema.org/) (da OpenAPI selbst auf JSON Schema basiert).
* Um diese Standards herum entworfen, nach sorgfältigem Studium. Statt einer nachträglichen Schicht darüber.
* Dies ermöglicht auch automatische **Client-Code-Generierung** in vielen Sprachen.
### Automatische Dokumentation { #automatic-docs }
Interaktive API-Dokumentation und erkundbare Web-Benutzeroberflächen. Da das Framework auf OpenAPI basiert, gibt es mehrere Optionen, zwei sind standardmäßig vorhanden.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), bietet interaktive Erkundung, testen und rufen Sie Ihre API direkt im Webbrowser auf.

* Alternative API-Dokumentation mit [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Nur modernes Python { #just-modern-python }
Alles basiert auf Standard-**Python-Typ**deklarationen (dank Pydantic). Es muss keine neue Syntax gelernt werden, nur standardisiertes modernes Python.
Wenn Sie eine zweiminütige Auffrischung benötigen, wie man Python-Typen verwendet (auch wenn Sie FastAPI nicht benutzen), schauen Sie sich das kurze Tutorial an: [Einführung in Python-Typen](python-types.md).
Sie schreiben Standard-Python mit Typen:
```Python
from datetime import date
from pydantic import BaseModel
# Deklarieren Sie eine Variable als ein str
# und bekommen Sie Editor-Unterstützung innerhalb der Funktion
def main(user_id: str):
return user_id
# Ein Pydantic-Modell
class User(BaseModel):
id: int
name: str
joined: date
```
Das kann nun wie folgt verwendet werden:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Info
`**second_user_data` bedeutet:
Nimm die Schlüssel-Wert-Paare des `second_user_data` Dicts und übergebe sie direkt als Schlüsselwort-Argumente. Äquivalent zu: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Editor Unterstützung { #editor-support }
Das ganze Framework wurde so entworfen, dass es einfach und intuitiv zu benutzen ist; alle Entscheidungen wurden auf mehreren Editoren getestet, sogar vor der Implementierung, um die bestmögliche Entwicklererfahrung zu gewährleisten.
In den Python-Entwickler-Umfragen wird klar, [dass die meist genutzte Funktion die „Autovervollständigung“ ist](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Das gesamte **FastAPI**-Framework ist darauf ausgelegt, das zu erfüllen. Autovervollständigung funktioniert überall.
Sie werden selten noch mal in der Dokumentation nachschauen müssen.
So kann Ihr Editor Sie unterstützen:
* in [Visual Studio Code](https://code.visualstudio.com/):

* in [PyCharm](https://www.jetbrains.com/pycharm/):

Sie bekommen sogar Autovervollständigung an Stellen, an denen Sie dies vorher nicht für möglich gehalten hätten. Zum Beispiel der `price` Schlüssel in einem JSON Datensatz (dieser könnte auch verschachtelt sein), der aus einem Request kommt.
Nie wieder falsche Schlüsselnamen tippen, Hin und Herhüpfen zwischen der Dokumentation, Hoch- und Runterscrollen, um herauszufinden, ob es `username` oder `user_name` war.
### Kompakt { #short }
Es gibt für alles sensible **Defaultwerte**, mit optionaler Konfiguration überall. Alle Parameter können feinjustiert werden, damit sie tun, was Sie benötigen, und die API definieren, die Sie brauchen.
Aber standardmäßig **„funktioniert einfach alles“**.
### Validierung { #validation }
* Validierung für die meisten (oder alle?) Python-**Datentypen**, hierzu gehören:
* JSON Objekte (`dict`).
* JSON Listen (`list`), die den Typ ihrer Elemente definieren.
* Strings (`str`) mit definierter minimaler und maximaler Länge.
* Zahlen (`int`, `float`) mit Mindest- und Maximalwerten, usw.
* Validierung für mehr exotische Typen, wie:
* URL.
* E-Mail.
* UUID.
* ... und andere.
Die gesamte Validierung übernimmt das gut etablierte und robuste **Pydantic**.
### Sicherheit und Authentifizierung { #security-and-authentication }
Sicherheit und Authentifizierung sind integriert. Ohne Kompromisse bei Datenbanken oder Datenmodellen.
Alle in OpenAPI definierten Sicherheitsschemas, inklusive:
* HTTP Basic.
* **OAuth2** (auch mit **JWT Tokens**). Siehe dazu das Tutorial zu [OAuth2 mit JWT](tutorial/security/oauth2-jwt.md).
* API Schlüssel in:
* Headern.
* Query-Parametern.
* Cookies, usw.
Zusätzlich alle Sicherheitsfunktionen von Starlette (inklusive **Session Cookies**).
Alles als wiederverwendbare Tools und Komponenten gebaut, die einfach in Ihre Systeme, Datenspeicher, relationale und nicht-relationale Datenbanken, usw., integriert werden können.
### Dependency Injection { #dependency-injection }
FastAPI enthält ein extrem einfach zu verwendendes, aber extrem mächtiges Dependency Injection System.
* Selbst Abhängigkeiten können Abhängigkeiten haben, woraus eine Hierarchie oder ein **„Graph“ von Abhängigkeiten** entsteht.
* Alles **automatisch gehandhabt** durch das Framework.
* Alle Abhängigkeiten können Daten von Requests anfordern und das Verhalten von **Pfadoperationen** und der automatisierten Dokumentation **modifizieren**.
* **Automatische Validierung** selbst für solche Parameter von *Pfadoperationen*, welche in Abhängigkeiten definiert sind.
* Unterstützung für komplexe Authentifizierungssysteme, **Datenbankverbindungen**, usw.
* **Keine Kompromisse** bei Datenbanken, Frontends, usw., sondern einfache Integration mit allen.
### Unbegrenzte Erweiterungen { #unlimited-plug-ins }
Oder mit anderen Worten, sie werden nicht benötigt. Importieren und nutzen Sie den Code, den Sie brauchen.
Jede Integration wurde so entworfen, dass sie so einfach zu nutzen ist (mit Abhängigkeiten), dass Sie eine Erweiterung für Ihre Anwendung mit nur zwei Zeilen Code erstellen können. Hierbei nutzen Sie die gleiche Struktur und Syntax, wie bei *Pfadoperationen*.
### Getestet { #tested }
* 100 % Testabdeckung.
* 100 % Typen annotiert.
* Verwendet in Produktionsanwendungen.
## Starlette Merkmale { #starlette-features }
**FastAPI** ist vollkommen kompatibel (und basiert auf) [**Starlette**](https://www.starlette.dev/). Das bedeutet, wenn Sie eigenen Starlette Quellcode haben, funktioniert der.
`FastAPI` ist tatsächlich eine Unterklasse von `Starlette`. Wenn Sie also bereits Starlette kennen oder benutzen, das meiste funktioniert genau so.
Mit **FastAPI** bekommen Sie alles von **Starlette** (da FastAPI nur Starlette auf Steroiden ist):
* Schwer beeindruckende Performanz. Es ist [eines der schnellsten Python-Frameworks, auf Augenhöhe mit **NodeJS** und **Go**](https://github.com/encode/starlette#performance).
* **WebSocket**-Unterstützung.
* Hintergrundtasks im selben Prozess.
* Startup- und Shutdown-Events.
* Testclient basierend auf HTTPX.
* **CORS**, GZip, statische Dateien, Responses streamen.
* **Sitzungs- und Cookie**-Unterstützung.
* 100 % Testabdeckung.
* 100 % Typen annotierte Codebasis.
## Pydantic Merkmale { #pydantic-features }
**FastAPI** ist vollkommen kompatibel (und basiert auf) [**Pydantic**](https://docs.pydantic.dev/). Das bedeutet, wenn Sie eigenen Pydantic Quellcode haben, funktioniert der.
Inklusive externer Bibliotheken, die auf Pydantic basieren, wie ORMs, ODMs für Datenbanken.
Daher können Sie in vielen Fällen das Objekt eines Requests **direkt zur Datenbank** schicken, weil alles automatisch validiert wird.
Das gleiche gilt auch für die andere Richtung: Sie können in vielen Fällen das Objekt aus der Datenbank **direkt zum Client** senden.
Mit **FastAPI** bekommen Sie alle Funktionen von **Pydantic** (da FastAPI für die gesamte Datenverarbeitung Pydantic nutzt):
* **Kein Kopfzerbrechen**:
* Keine neue Schemadefinition-Mikrosprache zu lernen.
* Wenn Sie Pythons Typen kennen, wissen Sie, wie man Pydantic verwendet.
* Gutes Zusammenspiel mit Ihrer/Ihrem **IDE/Linter/Gehirn**:
* Weil Pydantics Datenstrukturen einfach nur Instanzen ihrer definierten Klassen sind; Autovervollständigung, Linting, mypy und Ihre Intuition sollten alle einwandfrei mit Ihren validierten Daten funktionieren.
* Validierung von **komplexen Strukturen**:
* Benutzung von hierarchischen Pydantic-Modellen, Python-`typing`s `List` und `Dict`, etc.
* Die Validierer erlauben es, komplexe Datenschemen klar und einfach zu definieren, überprüft und dokumentiert als JSON Schema.
* Sie können tief **verschachtelte JSON** Objekte haben, die alle validiert und annotiert sind.
* **Erweiterbar**:
* Pydantic erlaubt die Definition von eigenen Datentypen oder sie können die Validierung mit einer `validator`-dekorierten Methode im Modell erweitern.
* 100 % Testabdeckung.
================================================
FILE: docs/de/docs/help-fastapi.md
================================================
# FastAPI helfen – Hilfe erhalten { #help-fastapi-get-help }
Mögen Sie **FastAPI**?
Möchten Sie FastAPI, anderen Benutzern und dem Autor helfen?
Oder möchten Sie Hilfe zu **FastAPI** erhalten?
Es gibt sehr einfache Möglichkeiten zu helfen (einige erfordern nur ein oder zwei Klicks).
Und es gibt auch mehrere Möglichkeiten, Hilfe zu bekommen.
## Newsletter abonnieren { #subscribe-to-the-newsletter }
Sie können den (unregelmäßigen) [**FastAPI and friends**-Newsletter](newsletter.md) abonnieren, um über folgende Themen informiert zu bleiben:
* Neuigkeiten über FastAPI und Freunde 🚀
* Anleitungen 📝
* Funktionen ✨
* Breaking Changes 🚨
* Tipps und Tricks ✅
## FastAPI auf X (Twitter) folgen { #follow-fastapi-on-x-twitter }
[Folgen Sie @fastapi auf **X (Twitter)**](https://x.com/fastapi), um die neuesten Nachrichten über **FastAPI** zu erhalten. 🐦
## **FastAPI** auf GitHub einen Stern geben { #star-fastapi-in-github }
Sie können FastAPI auf GitHub „starren“ (klicken Sie auf den Stern-Button oben rechts): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Durch das Hinzufügen eines Sterns können andere Benutzer es leichter finden und sehen, dass es für andere bereits nützlich war.
## Das GitHub-Repository auf Releases überwachen { #watch-the-github-repository-for-releases }
Sie können FastAPI auf GitHub „beobachten“ (klicken Sie auf den „watch“-Button oben rechts): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Dort können Sie „Releases only“ auswählen.
Auf diese Weise erhalten Sie Benachrichtigungen (per E-Mail), wenn es ein neues Release (eine neue Version) von **FastAPI** mit Bugfixes und neuen Funktionen gibt.
## Mit dem Autor vernetzen { #connect-with-the-author }
Sie können sich mit [mir (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), dem Autor, vernetzen.
Sie können:
* [Mir auf **GitHub** folgen](https://github.com/tiangolo).
* Andere Open-Source-Projekte sehen, die ich erstellt habe und die Ihnen helfen könnten.
* Mir folgen, um zu sehen, wenn ich ein neues Open-Source-Projekt erstelle.
* [Mir auf **X (Twitter)** folgen](https://x.com/tiangolo) oder [Mastodon](https://fosstodon.org/@tiangolo).
* Mir mitteilen, wie Sie FastAPI verwenden (ich höre das gerne).
* Mitbekommen, wenn ich Ankündigungen mache oder neue Tools veröffentliche.
* Sie können auch [@fastapi auf X (Twitter) folgen](https://x.com/fastapi) (ein separates Konto).
* [Mir auf **LinkedIn** folgen](https://www.linkedin.com/in/tiangolo/).
* Mitbekommen, wenn ich Ankündigungen mache oder neue Tools veröffentliche (obwohl ich X (Twitter) häufiger verwende 🤷♂).
* Lesen, was ich schreibe (oder mir folgen) auf [**Dev.to**](https://dev.to/tiangolo) oder [**Medium**](https://medium.com/@tiangolo).
* Andere Ideen, Artikel lesen und mehr über die von mir erstellten Tools erfahren.
* Mir folgen, um zu lesen, wenn ich etwas Neues veröffentliche.
## Über **FastAPI** tweeten { #tweet-about-fastapi }
[Tweeten Sie über **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) und teilen Sie mir und anderen mit, warum es Ihnen gefällt. 🎉
Ich höre gerne, wie **FastAPI** verwendet wird, was Ihnen daran gefallen hat, in welchem Projekt/Unternehmen Sie es verwenden, usw.
## Für FastAPI abstimmen { #vote-for-fastapi }
* [Stimmen Sie für **FastAPI** auf Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Stimmen Sie für **FastAPI** auf AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Sagen Sie auf StackShare, dass Sie **FastAPI** verwenden](https://stackshare.io/pypi-fastapi).
## Anderen bei Fragen auf GitHub helfen { #help-others-with-questions-in-github }
Sie können versuchen, anderen bei ihren Fragen zu helfen:
* [GitHub-Diskussionen](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub-Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
In vielen Fällen kennen Sie möglicherweise bereits die Antwort auf diese Fragen. 🤓
Wenn Sie vielen Menschen bei ihren Fragen helfen, werden Sie offizieller [FastAPI-Experte](fastapi-people.md#fastapi-experts). 🎉
Denken Sie daran, der wichtigste Punkt ist: Versuchen Sie, freundlich zu sein. Die Leute bringen ihre Frustrationen mit und fragen in vielen Fällen nicht auf die beste Art und Weise, aber versuchen Sie dennoch so gut wie möglich, freundlich zu sein. 🤗
Die **FastAPI**-Community soll freundlich und einladend sein. Akzeptieren Sie gleichzeitig kein Mobbing oder respektloses Verhalten gegenüber anderen. Wir müssen uns umeinander kümmern.
---
So helfen Sie anderen bei Fragen (in Diskussionen oder Issues):
### Die Frage verstehen { #understand-the-question }
* Prüfen Sie, ob Sie verstehen können, was der **Zweck** und der Anwendungsfall der fragenden Person ist.
* Überprüfen Sie dann, ob die Frage (die überwiegende Mehrheit sind Fragen) **klar** ist.
* In vielen Fällen handelt es sich bei der gestellten Frage um eine Lösung, die der Benutzer sich vorstellt, aber es könnte eine **bessere** Lösung geben. Wenn Sie das Problem und den Anwendungsfall besser verstehen, können Sie eine bessere **Alternativlösung** vorschlagen.
* Wenn Sie die Frage nicht verstehen können, fragen Sie nach weiteren **Details**.
### Das Problem reproduzieren { #reproduce-the-problem }
In den meisten Fällen und bei den meisten Fragen gibt es etwas in Bezug auf den **originalen Code** der Person.
In vielen Fällen wird nur ein Fragment des Codes gepostet, aber das reicht nicht aus, um **das Problem zu reproduzieren**.
* Sie können die Person bitten, ein [minimales, reproduzierbares Beispiel](https://stackoverflow.com/help/minimal-reproducible-example) bereitzustellen, welches Sie **kopieren, einfügen** und lokal ausführen können, um den gleichen Fehler oder das gleiche Verhalten zu sehen, das die Person sieht, oder um ihren Anwendungsfall besser zu verstehen.
* Wenn Sie in Geberlaune sind, können Sie ein solches Beispiel selbst erstellen, nur basierend auf der Beschreibung des Problems. Denken Sie jedoch daran, dass dies viel Zeit in Anspruch nehmen kann und dass es besser sein kann, zunächst um eine Klärung des Problems zu bitten.
### Lösungen vorschlagen { #suggest-solutions }
* Nachdem Sie die Frage verstanden haben, können Sie eine mögliche **Antwort** geben.
* In vielen Fällen ist es besser, das **zugrunde liegende Problem oder den Anwendungsfall** zu verstehen, da es möglicherweise einen besseren Weg zur Lösung gibt als das, was die Person versucht.
### Um Schließung bitten { #ask-to-close }
Wenn die Person antwortet, besteht eine hohe Chance, dass Sie ihr Problem gelöst haben. Herzlichen Glückwunsch, **Sie sind ein Held**! 🦸
* Wenn es tatsächlich das Problem gelöst hat, können Sie sie darum bitten:
* In GitHub-Diskussionen: den Kommentar als **Antwort** zu markieren.
* In GitHub-Issues: Das Issue zu **schließen**.
## Das GitHub-Repository beobachten { #watch-the-github-repository }
Sie können FastAPI auf GitHub „beobachten“ (klicken Sie auf den „watch“-Button oben rechts): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Wenn Sie dann „Watching“ statt „Releases only“ auswählen, erhalten Sie Benachrichtigungen, wenn jemand ein neues Issue eröffnet oder eine neue Frage stellt. Sie können auch spezifizieren, dass Sie nur über neue Issues, Diskussionen, PRs usw. benachrichtigt werden möchten.
Dann können Sie versuchen, bei der Lösung solcher Fragen zu helfen.
## Fragen stellen { #ask-questions }
Sie können im GitHub-Repository [eine neue Frage erstellen](https://github.com/fastapi/fastapi/discussions/new?category=questions), zum Beispiel:
* Stellen Sie eine **Frage** oder bitten Sie um Hilfe mit einem **Problem**.
* Schlagen Sie eine neue **Funktionalität** vor.
**Hinweis**: Wenn Sie das tun, bitte ich Sie, auch anderen zu helfen. 😉
## Pull Requests prüfen { #review-pull-requests }
Sie können mir helfen, Pull Requests von anderen zu überprüfen.
Noch einmal, bitte versuchen Sie Ihr Bestes, freundlich zu sein. 🤗
---
Hier ist, was Sie beachten sollten und wie Sie einen Pull Request überprüfen:
### Das Problem verstehen { #understand-the-problem }
* Stellen Sie zunächst sicher, dass Sie **das Problem verstehen**, welches der Pull Request zu lösen versucht. Möglicherweise gibt es eine längere Diskussion dazu in einer GitHub-Diskussion oder einem GitHub-Issue.
* Es besteht auch eine gute Chance, dass der Pull Request nicht wirklich benötigt wird, da das Problem auf **andere Weise** gelöst werden kann. Dann können Sie das vorschlagen oder danach fragen.
### Keine Panik wegen des Stils { #dont-worry-about-style }
* Machen Sie sich keine Sorgen über Dinge wie den Stil von Commit-Nachrichten. Ich werde den Commit zusammenführen und manuell anpassen.
* Außerdem, keine Sorgen über Stilregeln, es gibt bereits automatisierte Tools, die das überprüfen.
Und wenn es irgendeinen anderen Stil- oder Konsistenzbedarf gibt, werde ich direkt danach fragen oder zusätzliche Commits mit den erforderlichen Änderungen hinzufügen.
### Den Code testen { #check-the-code }
* Prüfen und lesen Sie den Code, fragen Sie sich, ob er Sinn macht, **führen Sie ihn lokal aus** und testen Sie, ob er das Problem tatsächlich löst.
* Schreiben Sie dann einen **Kommentar** und berichten, dass Sie das getan haben. So weiß ich, dass Sie ihn wirklich überprüft haben.
/// info | Info
Leider kann ich PRs, nur weil sie von mehreren gutgeheißen wurden, nicht einfach vertrauen.
Es ist mehrmals passiert, dass es PRs mit drei, fünf oder mehr Zustimmungen gibt, wahrscheinlich weil die Beschreibung ansprechend ist, aber wenn ich die PRs überprüfe, sind sie tatsächlich fehlerhaft, haben einen Bug, oder lösen das Problem nicht, welches sie behaupten, zu lösen. 😅
Daher ist es wirklich wichtig, dass Sie den Code wirklich lesen und ausführen und mir in den Kommentaren mitteilen, dass Sie dies getan haben. 🤓
///
* Wenn der PR in irgendeiner Weise vereinfacht werden kann, können Sie danach fragen, aber es gibt keinen Grund, zu wählerisch zu sein. Es gibt viele subjektive Standpunkte (und ich habe auch meinen eigenen 🙈), also ist es besser, wenn man sich auf die grundlegenden Dinge konzentriert.
### Tests { #tests }
* Helfen Sie mir zu überprüfen, dass der PR **Tests** hat.
* Überprüfen Sie, dass diese Tests vor dem PR **fehlschlagen**. 🚨
* Überprüfen Sie dann, dass diese Tests nach dem PR **bestanden** werden. ✅
* Viele PRs haben keine Tests. Sie können den Autor daran **erinnern**, Tests hinzuzufügen, oder Sie können sogar selbst einige Tests **vorschlagen**. Das ist eines der Dinge, die am meisten Zeit in Anspruch nehmen, und Sie können dabei viel helfen.
* Kommentieren Sie auch hier anschließend, was Sie versucht haben, sodass ich weiß, dass Sie es überprüft haben. 🤓
## Einen Pull Request erstellen { #create-a-pull-request }
Sie können [zum Quellcode mit Pull Requests beitragen](contributing.md), zum Beispiel:
* Um einen Tippfehler zu beheben, den Sie in der Dokumentation gefunden haben.
* Um einen Artikel, ein Video oder einen Podcast über FastAPI zu teilen, den Sie erstellt oder gefunden haben, indem Sie [diese Datei bearbeiten](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Stellen Sie sicher, dass Sie Ihren Link am Anfang des entsprechenden Abschnitts einfügen.
* Um zu helfen, [die Dokumentation in Ihre Sprache zu übersetzen](contributing.md#translations).
* Sie können auch dabei helfen, die von anderen erstellten Übersetzungen zu überprüfen.
* Um neue Dokumentationsabschnitte vorzuschlagen.
* Um ein bestehendes Problem/Bug zu beheben.
* Stellen Sie sicher, dass Sie Tests hinzufügen.
* Um eine neue Funktionalität hinzuzufügen.
* Stellen Sie sicher, dass Sie Tests hinzufügen.
* Stellen Sie sicher, dass Sie Dokumentation hinzufügen, falls das notwendig ist.
## FastAPI pflegen { #help-maintain-fastapi }
Helfen Sie mir, **FastAPI** zu pflegen! 🤓
Es gibt viel zu tun, und das meiste davon können **SIE** tun.
Die Hauptaufgaben, die Sie jetzt erledigen können, sind:
* [Anderen bei Fragen auf GitHub helfen](#help-others-with-questions-in-github) (siehe Abschnitt oben).
* [Pull Requests prüfen](#review-pull-requests) (siehe Abschnitt oben).
Diese beiden Aufgaben sind die Dinge, die **am meisten Zeit verbrauchen**. Das ist die Hauptarbeit bei der Wartung von FastAPI.
Wenn Sie mir dabei helfen können, **helfen Sie mir, FastAPI zu pflegen** und Sie stellen sicher, dass es weiterhin **schneller und besser voranschreitet**. 🚀
## Am Chat teilnehmen { #join-the-chat }
Treten Sie dem 👥 [Discord-Chatserver](https://discord.gg/VQjSZaeJmf) 👥 bei und treffen Sie sich mit anderen Mitgliedern der FastAPI-Community.
/// tip | Tipp
Bei Fragen stellen Sie sie in [GitHub-Diskussionen](https://github.com/fastapi/fastapi/discussions/new?category=questions), dort besteht eine viel größere Chance, dass Sie Hilfe von den [FastAPI-Experten](fastapi-people.md#fastapi-experts) erhalten.
Nutzen Sie den Chat nur für andere allgemeine Gespräche.
///
### Den Chat nicht für Fragen verwenden { #dont-use-the-chat-for-questions }
Bedenken Sie, dass Sie in Chats, die „freie Konversation“ erlauben, leicht Fragen stellen können, die zu allgemein und schwer zu beantworten sind, sodass Sie möglicherweise keine Antworten erhalten.
Auf GitHub hilft Ihnen die Vorlage dabei, die richtige Frage zu stellen, sodass Sie leichter eine gute Antwort erhalten können, oder sogar das Problem selbst lösen, bevor Sie überhaupt fragen. Und auf GitHub kann ich sicherstellen, dass ich immer alles beantworte, auch wenn es einige Zeit dauert. Persönlich kann ich das mit den Chat-Systemen nicht machen. 😅
Unterhaltungen in den Chat-Systemen sind auch nicht so leicht durchsuchbar wie auf GitHub, sodass Fragen und Antworten möglicherweise im Gespräch verloren gehen. Und nur die auf GitHub machen einen [FastAPI-Experten](fastapi-people.md#fastapi-experts), Sie werden also höchstwahrscheinlich mehr Aufmerksamkeit auf GitHub erhalten.
Auf der anderen Seite gibt es Tausende von Benutzern in den Chat-Systemen, sodass die Wahrscheinlichkeit hoch ist, dass Sie dort fast immer jemanden zum Reden finden. 😄
## Den Autor sponsern { #sponsor-the-author }
Wenn Ihr **Produkt/Firma** auf **FastAPI** angewiesen ist oder in Zusammenhang steht und Sie seine Benutzer erreichen möchten, können Sie den Autor (mich) über [GitHub-Sponsoren](https://github.com/sponsors/tiangolo) unterstützen. Je nach Stufe können Sie einige zusätzliche Vorteile erhalten, wie z. B. ein Abzeichen in der Dokumentation. 🎁
---
Danke! 🚀
================================================
FILE: docs/de/docs/history-design-future.md
================================================
# Geschichte, Design und Zukunft { #history-design-and-future }
Vor einiger Zeit fragte [ein **FastAPI**-Benutzer](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> Was ist die Geschichte dieses Projekts? Es scheint aus dem Nichts in ein paar Wochen zu etwas Großartigem geworden zu sein [...]
Hier ist ein wenig über diese Geschichte.
## Alternativen { #alternatives }
Ich habe seit mehreren Jahren APIs mit komplexen Anforderungen (maschinelles Lernen, verteilte Systeme, asynchrone Jobs, NoSQL-Datenbanken, usw.) erstellt und leitete mehrere Entwicklerteams.
Dabei musste ich viele Alternativen untersuchen, testen und nutzen.
Die Geschichte von **FastAPI** ist zu einem großen Teil die Geschichte seiner Vorgänger.
Wie im Abschnitt [Alternativen](alternatives.md) gesagt:
**FastAPI** würde ohne die frühere Arbeit anderer nicht existieren.
Es wurden zuvor viele Tools entwickelt, die als Inspiration für seine Entwicklung dienten.
Ich habe die Schaffung eines neuen Frameworks viele Jahre lang vermieden. Zuerst habe ich versucht, alle von **FastAPI** abgedeckten Funktionen mithilfe vieler verschiedener Frameworks, Plugins und Tools zu lösen.
Aber irgendwann gab es keine andere Möglichkeit, als etwas zu schaffen, das all diese Funktionen bereitstellte, die besten Ideen früherer Tools aufnahm und diese auf die bestmögliche Weise kombinierte, wobei Sprachfunktionen verwendet wurden, die vorher noch nicht einmal verfügbar waren (Python 3.6+ Typhinweise).
## Untersuchung { #investigation }
Durch die Nutzung all dieser vorherigen Alternativen hatte ich die Möglichkeit, von allen zu lernen, Ideen aufzunehmen und sie auf die beste Weise zu kombinieren, die ich für mich und die Entwicklerteams, mit denen ich zusammengearbeitet habe, finden konnte.
Es war beispielsweise klar, dass es idealerweise auf Standard-Python-Typhinweisen basieren sollte.
Der beste Ansatz bestand außerdem darin, bereits bestehende Standards zu nutzen.
Bevor ich also überhaupt angefangen habe, **FastAPI** zu schreiben, habe ich mehrere Monate damit verbracht, die Spezifikationen für OpenAPI, JSON Schema, OAuth2, usw. zu studieren und deren Beziehungen, Überschneidungen und Unterschiede zu verstehen.
## Design { #design }
Dann habe ich einige Zeit damit verbracht, die Entwickler-„API“ zu entwerfen, die ich als Benutzer haben wollte (als Entwickler, welcher FastAPI verwendet).
Ich habe mehrere Ideen in den beliebtesten Python-Editoren getestet: PyCharm, VS Code, Jedi-basierte Editoren.
Laut der letzten [Python-Entwickler-Umfrage](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools) deckt das etwa 80 % der Benutzer ab.
Das bedeutet, dass **FastAPI** speziell mit den Editoren getestet wurde, die von 80 % der Python-Entwickler verwendet werden. Und da die meisten anderen Editoren in der Regel ähnlich funktionieren, sollten alle diese Vorteile für praktisch alle Editoren funktionieren.
Auf diese Weise konnte ich die besten Möglichkeiten finden, die Codeverdoppelung so weit wie möglich zu reduzieren, überall Autovervollständigung, Typ- und Fehlerprüfungen, usw. zu gewährleisten.
Alles auf eine Weise, die allen Entwicklern das beste Entwicklungserlebnis bot.
## Anforderungen { #requirements }
Nachdem ich mehrere Alternativen getestet hatte, entschied ich, dass ich [**Pydantic**](https://docs.pydantic.dev/) wegen seiner Vorteile verwenden würde.
Dann habe ich zu dessen Code beigetragen, um es vollständig mit JSON Schema kompatibel zu machen, und so verschiedene Möglichkeiten zum Definieren von einschränkenden Deklarationen (Constraints) zu unterstützen, und die Editorunterstützung (Typprüfungen, Codevervollständigung) zu verbessern, basierend auf den Tests in mehreren Editoren.
Während der Entwicklung habe ich auch zu [**Starlette**](https://www.starlette.dev/) beigetragen, die andere Schlüsselanforderung.
## Entwicklung { #development }
Als ich mit der Erstellung von **FastAPI** selbst begann, waren die meisten Teile bereits vorhanden, das Design definiert, die Anforderungen und Tools bereit und das Wissen über die Standards und Spezifikationen klar und frisch.
## Zukunft { #future }
Zu diesem Zeitpunkt ist bereits klar, dass **FastAPI** mit seinen Ideen für viele Menschen nützlich ist.
Es wird gegenüber früheren Alternativen gewählt, da es für viele Anwendungsfälle besser geeignet ist.
Viele Entwickler und Teams verlassen sich bei ihren Projekten bereits auf **FastAPI** (einschließlich mir und meinem Team).
Dennoch stehen uns noch viele Verbesserungen und Funktionen bevor.
**FastAPI** hat eine große Zukunft vor sich.
Und [Ihre Hilfe](help-fastapi.md) wird sehr geschätzt.
================================================
FILE: docs/de/docs/how-to/authentication-error-status-code.md
================================================
# Alte 403-Authentifizierungsfehler-Statuscodes verwenden { #use-old-403-authentication-error-status-codes }
Vor FastAPI-Version `0.122.0` verwendeten die integrierten Sicherheits-Utilities den HTTP-Statuscode `403 Forbidden`, wenn sie dem Client nach einer fehlgeschlagenen Authentifizierung einen Fehler zurückgaben.
Ab FastAPI-Version `0.122.0` verwenden sie den passenderen HTTP-Statuscode `401 Unauthorized` und geben in der Response einen sinnvollen `WWW-Authenticate`-Header zurück, gemäß den HTTP-Spezifikationen, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Aber falls Ihre Clients aus irgendeinem Grund vom alten Verhalten abhängen, können Sie darauf zurückgreifen, indem Sie in Ihren Sicherheitsklassen die Methode `make_not_authenticated_error` überschreiben.
Sie können beispielsweise eine Unterklasse von `HTTPBearer` erstellen, die einen Fehler `403 Forbidden` zurückgibt, statt des Default-`401 Unauthorized`-Fehlers:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Tipp
Beachten Sie, dass die Funktion die Exception-Instanz zurückgibt; sie wirft sie nicht. Das Werfen erfolgt im restlichen internen Code.
///
================================================
FILE: docs/de/docs/how-to/conditional-openapi.md
================================================
# Bedingte OpenAPI { #conditional-openapi }
Bei Bedarf können Sie OpenAPI mithilfe von Einstellungen und Umgebungsvariablen abhängig von der Umgebung bedingt konfigurieren und sogar vollständig deaktivieren.
## Über Sicherheit, APIs und Dokumentation { #about-security-apis-and-docs }
Das Verstecken Ihrer Dokumentationsoberflächen in der Produktion *sollte nicht* die Methode sein, Ihre API zu schützen.
Dadurch wird Ihrer API keine zusätzliche Sicherheit hinzugefügt, die *Pfadoperationen* sind weiterhin dort verfügbar, wo sie sich befinden.
Wenn Ihr Code eine Sicherheitslücke aufweist, ist diese weiterhin vorhanden.
Das Verstecken der Dokumentation macht es nur schwieriger zu verstehen, wie mit Ihrer API interagiert werden kann, und könnte es auch schwieriger machen, diese in der Produktion zu debuggen. Man könnte es einfach als eine Form von [Sicherheit durch Verschleierung](https://en.wikipedia.org/wiki/Security_through_obscurity) betrachten.
Wenn Sie Ihre API sichern möchten, gibt es mehrere bessere Dinge, die Sie tun können, zum Beispiel:
* Stellen Sie sicher, dass Sie über gut definierte Pydantic-Modelle für Ihre Requestbodys und Responses verfügen.
* Konfigurieren Sie alle erforderlichen Berechtigungen und Rollen mithilfe von Abhängigkeiten.
* Speichern Sie niemals Klartext-Passwörter, sondern nur Passwort-Hashes.
* Implementieren und verwenden Sie gängige kryptografische Tools wie pwdlib und JWT-Tokens, usw.
* Fügen Sie bei Bedarf detailliertere Berechtigungskontrollen mit OAuth2-Scopes hinzu.
* ... usw.
Dennoch kann es sein, dass Sie einen ganz bestimmten Anwendungsfall haben, bei dem Sie die API-Dokumentation für eine bestimmte Umgebung (z. B. für die Produktion) oder abhängig von Konfigurationen aus Umgebungsvariablen wirklich deaktivieren müssen.
## Bedingte OpenAPI aus Einstellungen und Umgebungsvariablen { #conditional-openapi-from-settings-and-env-vars }
Sie können problemlos dieselben Pydantic-Einstellungen verwenden, um Ihre generierte OpenAPI und die Dokumentationsoberflächen zu konfigurieren.
Zum Beispiel:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Hier deklarieren wir die Einstellung `openapi_url` mit dem gleichen Defaultwert `"/openapi.json"`.
Und dann verwenden wir es beim Erstellen der `FastAPI`-App.
Dann könnten Sie OpenAPI (einschließlich der Dokumentationsoberflächen) deaktivieren, indem Sie die Umgebungsvariable `OPENAPI_URL` auf einen leeren String setzen, wie zum Beispiel:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Wenn Sie dann zu den URLs unter `/openapi.json`, `/docs` oder `/redoc` gehen, erhalten Sie lediglich einen `404 Not Found`-Fehler, wie:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/de/docs/how-to/configure-swagger-ui.md
================================================
# Swagger-Oberfläche konfigurieren { #configure-swagger-ui }
Sie können einige zusätzliche [Parameter der Swagger-Oberfläche](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) konfigurieren.
Um diese zu konfigurieren, übergeben Sie das Argument `swagger_ui_parameters` beim Erstellen des `FastAPI()`-App-Objekts oder an die Funktion `get_swagger_ui_html()`.
`swagger_ui_parameters` empfängt ein Dictionary mit den Konfigurationen, die direkt an die Swagger-Oberfläche übergeben werden.
FastAPI konvertiert die Konfigurationen nach **JSON**, um diese mit JavaScript kompatibel zu machen, da die Swagger-Oberfläche das benötigt.
## Syntaxhervorhebung deaktivieren { #disable-syntax-highlighting }
Sie könnten beispielsweise die Syntaxhervorhebung in der Swagger-Oberfläche deaktivieren.
Ohne Änderung der Einstellungen ist die Syntaxhervorhebung standardmäßig aktiviert:
Sie können sie jedoch deaktivieren, indem Sie `syntaxHighlight` auf `False` setzen:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
... und dann zeigt die Swagger-Oberfläche die Syntaxhervorhebung nicht mehr an:
## Das Theme ändern { #change-the-theme }
Auf die gleiche Weise könnten Sie das Theme der Syntaxhervorhebung mit dem Schlüssel `"syntaxHighlight.theme"` festlegen (beachten Sie, dass er einen Punkt in der Mitte hat):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Obige Konfiguration würde das Theme für die Farbe der Syntaxhervorhebung ändern:
## Defaultparameter der Swagger-Oberfläche ändern { #change-default-swagger-ui-parameters }
FastAPI enthält einige Defaultkonfigurationsparameter, die für die meisten Anwendungsfälle geeignet sind.
Es umfasst die folgenden Defaultkonfigurationen:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Sie können jede davon überschreiben, indem Sie im Argument `swagger_ui_parameters` einen anderen Wert festlegen.
Um beispielsweise `deepLinking` zu deaktivieren, könnten Sie folgende Einstellungen an `swagger_ui_parameters` übergeben:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Andere Parameter der Swagger-Oberfläche { #other-swagger-ui-parameters }
Um alle anderen möglichen Konfigurationen zu sehen, die Sie verwenden können, lesen Sie die offizielle [Dokumentation für die Parameter der Swagger-Oberfläche](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Nur-JavaScript-Einstellungen { #javascript-only-settings }
Die Swagger-Oberfläche erlaubt, dass andere Konfigurationen auch **Nur-JavaScript**-Objekte sein können (z. B. JavaScript-Funktionen).
FastAPI umfasst auch diese Nur-JavaScript-`presets`-Einstellungen:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Dabei handelt es sich um **JavaScript**-Objekte, nicht um Strings, daher können Sie diese nicht direkt vom Python-Code aus übergeben.
Wenn Sie solche JavaScript-Konfigurationen verwenden müssen, können Sie einen der früher genannten Wege verwenden. Überschreiben Sie alle *Pfadoperationen* der Swagger-Oberfläche und schreiben Sie manuell jedes benötigte JavaScript.
================================================
FILE: docs/de/docs/how-to/custom-docs-ui-assets.md
================================================
# Statische Assets der Dokumentationsoberfläche (Selbst-Hosting) { #custom-docs-ui-static-assets-self-hosting }
Die API-Dokumentation verwendet **Swagger UI** und **ReDoc**, und jede dieser Dokumentationen benötigt einige JavaScript- und CSS-Dateien.
Standardmäßig werden diese Dateien von einem CDN bereitgestellt.
Es ist jedoch möglich, das anzupassen, ein bestimmtes CDN festzulegen oder die Dateien selbst bereitzustellen.
## Benutzerdefiniertes CDN für JavaScript und CSS { #custom-cdn-for-javascript-and-css }
Nehmen wir an, Sie möchten ein anderes CDN verwenden, zum Beispiel möchten Sie `https://unpkg.com/` verwenden.
Das kann nützlich sein, wenn Sie beispielsweise in einem Land leben, in dem bestimmte URLs eingeschränkt sind.
### Die automatische Dokumentation deaktivieren { #disable-the-automatic-docs }
Der erste Schritt besteht darin, die automatische Dokumentation zu deaktivieren, da sie standardmäßig das Standard-CDN verwendet.
Um sie zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Die benutzerdefinierte Dokumentation hinzufügen { #include-the-custom-docs }
Jetzt können Sie die *Pfadoperationen* für die benutzerdefinierte Dokumentation erstellen.
Sie können die internen Funktionen von FastAPI wiederverwenden, um die HTML-Seiten für die Dokumentation zu erstellen und ihnen die erforderlichen Argumente zu übergeben:
* `openapi_url`: die URL, unter welcher die HTML-Seite für die Dokumentation das OpenAPI-Schema für Ihre API abrufen kann. Sie können hier das Attribut `app.openapi_url` verwenden.
* `title`: der Titel Ihrer API.
* `oauth2_redirect_url`: Sie können hier `app.swagger_ui_oauth2_redirect_url` verwenden, um die Standardeinstellung zu verwenden.
* `swagger_js_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **JavaScript**-Datei abrufen kann. Dies ist die benutzerdefinierte CDN-URL.
* `swagger_css_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **CSS**-Datei abrufen kann. Dies ist die benutzerdefinierte CDN-URL.
Und ähnlich für ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Tipp
Die *Pfadoperation* für `swagger_ui_redirect` ist ein Hilfsmittel bei der Verwendung von OAuth2.
Wenn Sie Ihre API mit einem OAuth2-Anbieter integrieren, können Sie sich authentifizieren und mit den erworbenen Anmeldeinformationen zur API-Dokumentation zurückkehren. Und mit ihr interagieren, die echte OAuth2-Authentifizierung verwendend.
Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „Umleitungs“-Helfer.
///
### Eine *Pfadoperation* erstellen, um es zu testen { #create-a-path-operation-to-test-it }
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Es testen { #test-it }
Jetzt sollten Sie in der Lage sein, zu Ihrer Dokumentation auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) zu gehen und die Seite neu zu laden, die Assets werden nun vom neuen CDN geladen.
## JavaScript und CSS für die Dokumentation selbst hosten { #self-hosting-javascript-and-css-for-docs }
Das Selbst-Hosting von JavaScript und CSS kann nützlich sein, wenn Sie beispielsweise möchten, dass Ihre Anwendung auch offline, ohne bestehenden Internetzugang oder in einem lokalen Netzwerk weiter funktioniert.
Hier erfahren Sie, wie Sie diese Dateien selbst in derselben FastAPI-App bereitstellen und die Dokumentation für deren Verwendung konfigurieren.
### Projektdateistruktur { #project-file-structure }
Nehmen wir an, die Dateistruktur Ihres Projekts sieht folgendermaßen aus:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Erstellen Sie jetzt ein Verzeichnis zum Speichern dieser statischen Dateien.
Ihre neue Dateistruktur könnte so aussehen:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Die Dateien herunterladen { #download-the-files }
Laden Sie die für die Dokumentation benötigten statischen Dateien herunter und legen Sie diese im Verzeichnis `static/` ab.
Sie können wahrscheinlich mit der rechten Maustaste auf jeden Link klicken und eine Option wie etwa „Link speichern unter ...“ auswählen.
**Swagger UI** verwendet folgende Dateien:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
Und **ReDoc** verwendet diese Datei:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Danach könnte Ihre Dateistruktur wie folgt aussehen:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Die statischen Dateien bereitstellen { #serve-the-static-files }
* Importieren Sie `StaticFiles`.
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Die statischen Dateien testen { #test-the-static-files }
Starten Sie Ihre Anwendung und gehen Sie auf [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Sie sollten eine sehr lange JavaScript-Datei für **ReDoc** sehen.
Sie könnte beginnen mit etwas wie:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Das zeigt, dass Sie statische Dateien aus Ihrer Anwendung bereitstellen können und dass Sie die statischen Dateien für die Dokumentation an der richtigen Stelle platziert haben.
Jetzt können wir die Anwendung so konfigurieren, dass sie diese statischen Dateien für die Dokumentation verwendet.
### Die automatische Dokumentation für statische Dateien deaktivieren { #disable-the-automatic-docs-for-static-files }
Wie bei der Verwendung eines benutzerdefinierten CDN besteht der erste Schritt darin, die automatische Dokumentation zu deaktivieren, da sie standardmäßig das CDN verwendet.
Um sie zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Die benutzerdefinierte Dokumentation für statische Dateien hinzufügen { #include-the-custom-docs-for-static-files }
Und genau wie bei einem benutzerdefinierten CDN können Sie jetzt die *Pfadoperationen* für die benutzerdefinierte Dokumentation erstellen.
Auch hier können Sie die internen Funktionen von FastAPI wiederverwenden, um die HTML-Seiten für die Dokumentation zu erstellen und ihnen die erforderlichen Argumente zu übergeben:
* `openapi_url`: die URL, unter der die HTML-Seite für die Dokumentation das OpenAPI-Schema für Ihre API abrufen kann. Sie können hier das Attribut `app.openapi_url` verwenden.
* `title`: der Titel Ihrer API.
* `oauth2_redirect_url`: Sie können hier `app.swagger_ui_oauth2_redirect_url` verwenden, um die Standardeinstellung zu verwenden.
* `swagger_js_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **JavaScript**-Datei abrufen kann. **Das ist die, welche jetzt von Ihrer eigenen Anwendung bereitgestellt wird**.
* `swagger_css_url`: die URL, unter welcher der HTML-Code für Ihre Swagger-UI-Dokumentation die **CSS**-Datei abrufen kann. **Das ist die, welche jetzt von Ihrer eigenen Anwendung bereitgestellt wird**.
Und ähnlich für ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Tipp
Die *Pfadoperation* für `swagger_ui_redirect` ist ein Hilfsmittel bei der Verwendung von OAuth2.
Wenn Sie Ihre API mit einem OAuth2-Anbieter integrieren, können Sie sich authentifizieren und mit den erworbenen Anmeldeinformationen zur API-Dokumentation zurückkehren. Und mit ihr interagieren, die echte OAuth2-Authentifizierung verwendend.
Swagger UI erledigt das hinter den Kulissen für Sie, benötigt aber diesen „Umleitungs“-Helfer.
///
### Eine *Pfadoperation* erstellen, um statische Dateien zu testen { #create-a-path-operation-to-test-static-files }
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Benutzeroberfläche mit statischen Dateien testen { #test-static-files-ui }
Jetzt sollten Sie in der Lage sein, Ihr WLAN zu trennen, gehen Sie zu Ihrer Dokumentation unter [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) und laden Sie die Seite neu.
Und selbst ohne Internet können Sie die Dokumentation für Ihre API sehen und mit ihr interagieren.
================================================
FILE: docs/de/docs/how-to/custom-request-and-route.md
================================================
# Benutzerdefinierte Request- und APIRoute-Klasse { #custom-request-and-apiroute-class }
In einigen Fällen möchten Sie möglicherweise die von den Klassen `Request` und `APIRoute` verwendete Logik überschreiben.
Das kann insbesondere eine gute Alternative zur Logik in einer Middleware sein.
Wenn Sie beispielsweise den Requestbody lesen oder manipulieren möchten, bevor er von Ihrer Anwendung verarbeitet wird.
/// danger | Gefahr
Dies ist eine „fortgeschrittene“ Funktion.
Wenn Sie gerade erst mit **FastAPI** beginnen, möchten Sie diesen Abschnitt vielleicht überspringen.
///
## Anwendungsfälle { #use-cases }
Einige Anwendungsfälle sind:
* Konvertieren von Nicht-JSON-Requestbodys nach JSON (z. B. [`msgpack`](https://msgpack.org/index.html)).
* Dekomprimierung gzip-komprimierter Requestbodys.
* Automatisches Loggen aller Requestbodys.
## Handhaben von benutzerdefinierten Requestbody-Kodierungen { #handling-custom-request-body-encodings }
Sehen wir uns an, wie Sie eine benutzerdefinierte `Request`-Unterklasse verwenden, um gzip-Requests zu dekomprimieren.
Und eine `APIRoute`-Unterklasse zur Verwendung dieser benutzerdefinierten Requestklasse.
### Eine benutzerdefinierte `GzipRequest`-Klasse erstellen { #create-a-custom-gziprequest-class }
/// tip | Tipp
Dies ist nur ein einfaches Beispiel, um zu demonstrieren, wie es funktioniert. Wenn Sie Gzip-Unterstützung benötigen, können Sie die bereitgestellte [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) verwenden.
///
Zuerst erstellen wir eine `GzipRequest`-Klasse, welche die Methode `Request.body()` überschreibt, um den Body bei Vorhandensein eines entsprechenden Headers zu dekomprimieren.
Wenn der Header kein `gzip` enthält, wird nicht versucht, den Body zu dekomprimieren.
Auf diese Weise kann dieselbe Routenklasse gzip-komprimierte oder unkomprimierte Requests verarbeiten.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Eine benutzerdefinierte `GzipRoute`-Klasse erstellen { #create-a-custom-gziproute-class }
Als Nächstes erstellen wir eine benutzerdefinierte Unterklasse von `fastapi.routing.APIRoute`, welche `GzipRequest` nutzt.
Dieses Mal wird die Methode `APIRoute.get_route_handler()` überschrieben.
Diese Methode gibt eine Funktion zurück. Und diese Funktion empfängt einen Request und gibt eine Response zurück.
Hier verwenden wir sie, um aus dem ursprünglichen Request einen `GzipRequest` zu erstellen.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Technische Details
Ein `Request` hat ein `request.scope`-Attribut, welches einfach ein Python-`dict` ist, welches die mit dem Request verbundenen Metadaten enthält.
Ein `Request` hat auch ein `request.receive`, welches eine Funktion ist, die den Body des Requests empfängt.
Das `scope`-`dict` und die `receive`-Funktion sind beide Teil der ASGI-Spezifikation.
Und diese beiden Dinge, `scope` und `receive`, werden benötigt, um eine neue `Request`-Instanz zu erstellen.
Um mehr über den `Request` zu erfahren, schauen Sie sich [Starlettes Dokumentation zu Requests](https://www.starlette.dev/requests/) an.
///
Das Einzige, was die von `GzipRequest.get_route_handler` zurückgegebene Funktion anders macht, ist die Konvertierung von `Request` in ein `GzipRequest`.
Dabei kümmert sich unser `GzipRequest` um die Dekomprimierung der Daten (falls erforderlich), bevor diese an unsere *Pfadoperationen* weitergegeben werden.
Danach ist die gesamte Verarbeitungslogik dieselbe.
Aufgrund unserer Änderungen in `GzipRequest.body` wird der Requestbody jedoch bei Bedarf automatisch dekomprimiert, wenn er von **FastAPI** geladen wird.
## Zugriff auf den Requestbody in einem Exceptionhandler { #accessing-the-request-body-in-an-exception-handler }
/// tip | Tipp
Um dasselbe Problem zu lösen, ist es wahrscheinlich viel einfacher, den `body` in einem benutzerdefinierten Handler für `RequestValidationError` zu verwenden ([Fehlerbehandlung](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Dieses Beispiel ist jedoch immer noch gültig und zeigt, wie mit den internen Komponenten interagiert wird.
///
Wir können denselben Ansatz auch verwenden, um in einem Exceptionhandler auf den Requestbody zuzugreifen.
Alles, was wir tun müssen, ist, den Request innerhalb eines `try`/`except`-Blocks zu handhaben:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Wenn eine Exception auftritt, befindet sich die `Request`-Instanz weiterhin im Gültigkeitsbereich, sodass wir den Requestbody lesen und bei der Fehlerbehandlung verwenden können:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Benutzerdefinierte `APIRoute`-Klasse in einem Router { #custom-apiroute-class-in-a-router }
Sie können auch den Parameter `route_class` eines `APIRouter` festlegen:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
In diesem Beispiel verwenden die *Pfadoperationen* unter dem `router` die benutzerdefinierte `TimedRoute`-Klasse und haben in der Response einen zusätzlichen `X-Response-Time`-Header mit der Zeit, die zum Generieren der Response benötigt wurde:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/de/docs/how-to/extending-openapi.md
================================================
# OpenAPI erweitern { #extending-openapi }
Es gibt einige Fälle, in denen Sie das generierte OpenAPI-Schema ändern müssen.
In diesem Abschnitt erfahren Sie, wie.
## Der normale Vorgang { #the-normal-process }
Der normale (Standard-)Prozess ist wie folgt.
Eine `FastAPI`-Anwendung (Instanz) verfügt über eine `.openapi()`-Methode, von der erwartet wird, dass sie das OpenAPI-Schema zurückgibt.
Als Teil der Erstellung des Anwendungsobjekts wird eine *Pfadoperation* für `/openapi.json` (oder welcher Wert für den Parameter `openapi_url` gesetzt wurde) registriert.
Diese gibt lediglich eine JSON-Response zurück, mit dem Ergebnis der Methode `.openapi()` der Anwendung.
Standardmäßig überprüft die Methode `.openapi()` die Eigenschaft `.openapi_schema`, um zu sehen, ob diese Inhalt hat, und gibt diesen zurück.
Ist das nicht der Fall, wird der Inhalt mithilfe der Hilfsfunktion unter `fastapi.openapi.utils.get_openapi` generiert.
Diese Funktion `get_openapi()` erhält als Parameter:
* `title`: Der OpenAPI-Titel, der in der Dokumentation angezeigt wird.
* `version`: Die Version Ihrer API, z. B. `2.5.0`.
* `openapi_version`: Die Version der verwendeten OpenAPI-Spezifikation. Standardmäßig die neueste Version: `3.1.0`.
* `summary`: Eine kurze Zusammenfassung der API.
* `description`: Die Beschreibung Ihrer API. Dies kann Markdown enthalten und wird in der Dokumentation angezeigt.
* `routes`: Eine Liste von Routen, dies sind alle registrierten *Pfadoperationen*. Sie stammen von `app.routes`.
/// info | Info
Der Parameter `summary` ist in OpenAPI 3.1.0 und höher verfügbar und wird von FastAPI 0.99.0 und höher unterstützt.
///
## Überschreiben der Standardeinstellungen { #overriding-the-defaults }
Mithilfe der oben genannten Informationen können Sie dieselbe Hilfsfunktion verwenden, um das OpenAPI-Schema zu generieren und jeden benötigten Teil zu überschreiben.
Fügen wir beispielsweise [ReDocs OpenAPI-Erweiterung zum Einbinden eines benutzerdefinierten Logos](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo) hinzu.
### Normales **FastAPI** { #normal-fastapi }
Schreiben Sie zunächst wie gewohnt Ihre ganze **FastAPI**-Anwendung:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Das OpenAPI-Schema generieren { #generate-the-openapi-schema }
Verwenden Sie dann dieselbe Hilfsfunktion, um das OpenAPI-Schema innerhalb einer `custom_openapi()`-Funktion zu generieren:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Das OpenAPI-Schema ändern { #modify-the-openapi-schema }
Jetzt können Sie die ReDoc-Erweiterung hinzufügen und dem `info`-„Objekt“ im OpenAPI-Schema ein benutzerdefiniertes `x-logo` hinzufügen:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Zwischenspeichern des OpenAPI-Schemas { #cache-the-openapi-schema }
Sie können die Eigenschaft `.openapi_schema` als „Cache“ verwenden, um Ihr generiertes Schema zu speichern.
Auf diese Weise muss Ihre Anwendung das Schema nicht jedes Mal generieren, wenn ein Benutzer Ihre API-Dokumentation öffnet.
Es wird nur einmal generiert und dann wird dasselbe zwischengespeicherte Schema für die nächsten Requests verwendet.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Die Methode überschreiben { #override-the-method }
Jetzt können Sie die Methode `.openapi()` durch Ihre neue Funktion ersetzen.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Es testen { #check-it }
Sobald Sie auf [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) gehen, werden Sie sehen, dass Ihr benutzerdefiniertes Logo verwendet wird (in diesem Beispiel das Logo von **FastAPI**):
================================================
FILE: docs/de/docs/how-to/general.md
================================================
# Allgemeines – How-To – Rezepte { #general-how-to-recipes }
Hier finden Sie mehrere Verweise auf andere Stellen in der Dokumentation, für allgemeine oder häufige Fragen.
## Daten filtern – Sicherheit { #filter-data-security }
Um sicherzustellen, dass Sie nicht mehr Daten zurückgeben, als Sie sollten, lesen Sie die Dokumentation unter [Tutorial – Responsemodell – Rückgabetyp](../tutorial/response-model.md).
## Response-Leistung optimieren – Responsemodell – Rückgabetyp { #optimize-response-performance-response-model-return-type }
Um die Leistung beim Zurückgeben von JSON-Daten zu optimieren, verwenden Sie einen Rückgabetyp oder ein Responsemodell. So übernimmt Pydantic die Serialisierung zu JSON auf der Rust-Seite, ohne den Umweg über Python. Mehr dazu in der Dokumentation unter [Tutorial – Responsemodell – Rückgabetyp](../tutorial/response-model.md).
## Dokumentations-Tags – OpenAPI { #documentation-tags-openapi }
Um Tags zu Ihren *Pfadoperationen* hinzuzufügen und diese in der Oberfläche der Dokumentation zu gruppieren, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Tags](../tutorial/path-operation-configuration.md#tags).
## Zusammenfassung und Beschreibung in der Dokumentation – OpenAPI { #documentation-summary-and-description-openapi }
Um Ihren *Pfadoperationen* eine Zusammenfassung und Beschreibung hinzuzufügen und diese in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Zusammenfassung und Beschreibung](../tutorial/path-operation-configuration.md#summary-and-description).
## Beschreibung der Response in der Dokumentation – OpenAPI { #documentation-response-description-openapi }
Um die Beschreibung der Response zu definieren, welche in der Oberfläche der Dokumentation angezeigt wird, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Beschreibung der Response](../tutorial/path-operation-configuration.md#response-description).
## *Pfadoperation* in der Dokumentation deprecaten – OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Um eine *Pfadoperation* zu deprecaten und das in der Oberfläche der Dokumentation anzuzeigen, lesen Sie die Dokumentation unter [Tutorial – Pfadoperation-Konfiguration – Deprecaten](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Daten in etwas JSON-kompatibles konvertieren { #convert-any-data-to-json-compatible }
Um Daten in etwas JSON-kompatibles zu konvertieren, lesen Sie die Dokumentation unter [Tutorial – JSON-kompatibler Encoder](../tutorial/encoder.md).
## OpenAPI-Metadaten – Dokumentation { #openapi-metadata-docs }
Um Metadaten zu Ihrem OpenAPI-Schema hinzuzufügen, einschließlich einer Lizenz, Version, Kontakt, usw., lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md).
## Benutzerdefinierte OpenAPI-URL { #openapi-custom-url }
Um die OpenAPI-URL anzupassen (oder zu entfernen), lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md#openapi-url).
## URLs der OpenAPI-Dokumentationen { #openapi-docs-urls }
Um die URLs zu aktualisieren, die für die automatisch generierten Dokumentations-Oberflächen verwendet werden, lesen Sie die Dokumentation unter [Tutorial – Metadaten und URLs der Dokumentation](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/de/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Da **FastAPI** auf dem **ASGI**-Standard basiert, ist es sehr einfach, jede **GraphQL**-Bibliothek zu integrieren, die auch mit ASGI kompatibel ist.
Sie können normale FastAPI-*Pfadoperationen* mit GraphQL in derselben Anwendung kombinieren.
/// tip | Tipp
**GraphQL** löst einige sehr spezifische Anwendungsfälle.
Es hat **Vorteile** und **Nachteile** im Vergleich zu gängigen **Web-APIs**.
Stellen Sie sicher, dass Sie prüfen, ob die **Vorteile** für Ihren Anwendungsfall die **Nachteile** ausgleichen. 🤓
///
## GraphQL-Bibliotheken { #graphql-libraries }
Hier sind einige der **GraphQL**-Bibliotheken, die **ASGI**-Unterstützung haben. Sie könnten sie mit **FastAPI** verwenden:
* [Strawberry](https://strawberry.rocks/) 🍓
* Mit [Dokumentation für FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* Mit [Dokumentation für FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* Mit [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) für ASGI-Integration
* [Graphene](https://graphene-python.org/)
* Mit [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL mit Strawberry { #graphql-with-strawberry }
Wenn Sie mit **GraphQL** arbeiten möchten oder müssen, ist [**Strawberry**](https://strawberry.rocks/) die **empfohlene** Bibliothek, da deren Design **FastAPIs** Design am nächsten kommt und alles auf **Typannotationen** basiert.
Abhängig von Ihrem Anwendungsfall könnten Sie eine andere Bibliothek vorziehen, aber wenn Sie mich fragen würden, würde ich Ihnen wahrscheinlich empfehlen, **Strawberry** auszuprobieren.
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Weitere Informationen zu Strawberry finden Sie in der [Strawberry-Dokumentation](https://strawberry.rocks/).
Und auch in der Dokumentation zu [Strawberry mit FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Ältere `GraphQLApp` von Starlette { #older-graphqlapp-from-starlette }
Frühere Versionen von Starlette enthielten eine `GraphQLApp`-Klasse zur Integration mit [Graphene](https://graphene-python.org/).
Das wurde von Starlette deprecatet, aber wenn Sie Code haben, der das verwendet, können Sie einfach zu [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3) **migrieren**, das denselben Anwendungsfall abdeckt und eine **fast identische Schnittstelle** hat.
/// tip | Tipp
Wenn Sie GraphQL benötigen, würde ich Ihnen trotzdem empfehlen, sich [Strawberry](https://strawberry.rocks/) anzuschauen, da es auf Typannotationen basiert, statt auf benutzerdefinierten Klassen und Typen.
///
## Mehr darüber lernen { #learn-more }
Weitere Informationen zu **GraphQL** finden Sie in der [offiziellen GraphQL-Dokumentation](https://graphql.org/).
Sie können auch mehr über jede der oben beschriebenen Bibliotheken in den jeweiligen Links lesen.
================================================
FILE: docs/de/docs/how-to/index.md
================================================
# How-To – Rezepte { #how-to-recipes }
Hier finden Sie verschiedene Rezepte und „How-To“-Anleitungen zu **verschiedenen Themen**.
Die meisten dieser Ideen sind mehr oder weniger **unabhängig**, und in den meisten Fällen müssen Sie diese nur studieren, wenn sie direkt auf **Ihr Projekt** anwendbar sind.
Wenn etwas für Ihr Projekt interessant und nützlich erscheint, lesen Sie es, andernfalls überspringen Sie es einfach.
/// tip | Tipp
Wenn Sie strukturiert **FastAPI lernen** möchten (empfohlen), lesen Sie stattdessen Kapitel für Kapitel das [Tutorial – Benutzerhandbuch](../tutorial/index.md).
///
================================================
FILE: docs/de/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Von Pydantic v1 zu Pydantic v2 migrieren { #migrate-from-pydantic-v1-to-pydantic-v2 }
Wenn Sie eine ältere FastAPI-App haben, nutzen Sie möglicherweise Pydantic Version 1.
FastAPI Version 0.100.0 unterstützte sowohl Pydantic v1 als auch v2. Es verwendete, was auch immer Sie installiert hatten.
FastAPI Version 0.119.0 führte eine teilweise Unterstützung für Pydantic v1 innerhalb von Pydantic v2 (als `pydantic.v1`) ein, um die Migration zu v2 zu erleichtern.
FastAPI 0.126.0 entfernte die Unterstützung für Pydantic v1, während `pydantic.v1` noch eine Weile unterstützt wurde.
/// warning | Achtung
Das Pydantic-Team hat die Unterstützung für Pydantic v1 in den neuesten Python-Versionen eingestellt, beginnend mit **Python 3.14**.
Dies schließt `pydantic.v1` ein, das unter Python 3.14 und höher nicht mehr unterstützt wird.
Wenn Sie die neuesten Features von Python nutzen möchten, müssen Sie sicherstellen, dass Sie Pydantic v2 verwenden.
///
Wenn Sie eine ältere FastAPI-App mit Pydantic v1 haben, zeige ich Ihnen hier, wie Sie sie zu Pydantic v2 migrieren, und die **Features in FastAPI 0.119.0**, die Ihnen bei einer schrittweisen Migration helfen.
## Offizieller Leitfaden { #official-guide }
Pydantic hat einen offiziellen [Migrationsleitfaden](https://docs.pydantic.dev/latest/migration/) von v1 zu v2.
Er enthält auch, was sich geändert hat, wie Validierungen nun korrekter und strikter sind, mögliche Stolpersteine, usw.
Sie können ihn lesen, um besser zu verstehen, was sich geändert hat.
## Tests { #tests }
Stellen Sie sicher, dass Sie [Tests](../tutorial/testing.md) für Ihre App haben und diese in Continuous Integration (CI) ausführen.
Auf diese Weise können Sie das Update durchführen und sicherstellen, dass weiterhin alles wie erwartet funktioniert.
## `bump-pydantic` { #bump-pydantic }
In vielen Fällen, wenn Sie reguläre Pydantic-Modelle ohne Anpassungen verwenden, können Sie den Großteil des Prozesses der Migration von Pydantic v1 auf Pydantic v2 automatisieren.
Sie können [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) vom selben Pydantic-Team verwenden.
Dieses Tool hilft Ihnen, den Großteil des zu ändernden Codes automatisch anzupassen.
Danach können Sie die Tests ausführen und prüfen, ob alles funktioniert. Falls ja, sind Sie fertig. 😎
## Pydantic v1 in v2 { #pydantic-v1-in-v2 }
Pydantic v2 enthält alles aus Pydantic v1 als Untermodul `pydantic.v1`. Dies wird aber in Versionen oberhalb von Python 3.13 nicht mehr unterstützt.
Das bedeutet, Sie können die neueste Version von Pydantic v2 installieren und die alten Pydantic‑v1‑Komponenten aus diesem Untermodul importieren und verwenden, als hätten Sie das alte Pydantic v1 installiert.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### FastAPI-Unterstützung für Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Seit FastAPI 0.119.0 gibt es außerdem eine teilweise Unterstützung für Pydantic v1 innerhalb von Pydantic v2, um die Migration auf v2 zu erleichtern.
Sie könnten also Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass das Untermodul `pydantic.v1` verwendet wird, und in vielen Fällen würde es einfach funktionieren.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Achtung
Beachten Sie, dass, da das Pydantic‑Team Pydantic v1 in neueren Python‑Versionen nicht mehr unterstützt, beginnend mit Python 3.14, auch die Verwendung von `pydantic.v1` unter Python 3.14 und höher nicht unterstützt wird.
///
### Pydantic v1 und v2 in derselben App { #pydantic-v1-and-v2-on-the-same-app }
Es wird von Pydantic **nicht unterstützt**, dass ein Pydantic‑v2‑Modell Felder hat, die als Pydantic‑v1‑Modelle definiert sind, und umgekehrt.
```mermaid
graph TB
subgraph "❌ Nicht unterstützt"
direction TB
subgraph V2["Pydantic-v2-Modell"]
V1Field["Pydantic-v1-Modell"]
end
subgraph V1["Pydantic-v1-Modell"]
V2Field["Pydantic-v2-Modell"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
... aber Sie können getrennte Modelle, die Pydantic v1 bzw. v2 nutzen, in derselben App verwenden.
```mermaid
graph TB
subgraph "✅ Unterstützt"
direction TB
subgraph V2["Pydantic-v2-Modell"]
V2Field["Pydantic-v2-Modell"]
end
subgraph V1["Pydantic-v1-Modell"]
V1Field["Pydantic-v1-Modell"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
In einigen Fällen ist es sogar möglich, sowohl Pydantic‑v1‑ als auch Pydantic‑v2‑Modelle in derselben **Pfadoperation** Ihrer FastAPI‑App zu verwenden:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
Im obigen Beispiel ist das Eingabemodell ein Pydantic‑v1‑Modell, und das Ausgabemodell (definiert in `response_model=ItemV2`) ist ein Pydantic‑v2‑Modell.
### Pydantic v1 Parameter { #pydantic-v1-parameters }
Wenn Sie einige der FastAPI-spezifischen Tools für Parameter wie `Body`, `Query`, `Form`, usw. zusammen mit Pydantic‑v1‑Modellen verwenden müssen, können Sie die aus `fastapi.temp_pydantic_v1_params` importieren, während Sie die Migration zu Pydantic v2 abschließen:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### In Schritten migrieren { #migrate-in-steps }
/// tip | Tipp
Probieren Sie zuerst `bump-pydantic` aus. Wenn Ihre Tests erfolgreich sind und das funktioniert, sind Sie mit einem einzigen Befehl fertig. ✨
///
Wenn `bump-pydantic` für Ihren Anwendungsfall nicht funktioniert, können Sie die Unterstützung für Pydantic‑v1‑ und Pydantic‑v2‑Modelle in derselben App nutzen, um die Migration zu Pydantic v2 schrittweise durchzuführen.
Sie könnten zuerst Pydantic auf die neueste Version 2 aktualisieren und die Importe so ändern, dass für all Ihre Modelle `pydantic.v1` verwendet wird.
Anschließend können Sie beginnen, Ihre Modelle gruppenweise von Pydantic v1 auf v2 zu migrieren – in kleinen, schrittweisen Etappen. 🚶
================================================
FILE: docs/de/docs/how-to/separate-openapi-schemas.md
================================================
# Separate OpenAPI-Schemas für Eingabe und Ausgabe oder nicht { #separate-openapi-schemas-for-input-and-output-or-not }
Seit der Veröffentlichung von **Pydantic v2** ist die generierte OpenAPI etwas genauer und **korrekter** als zuvor. 😎
Tatsächlich gibt es in einigen Fällen sogar **zwei JSON-Schemas** in OpenAPI für dasselbe Pydantic-Modell, für Eingabe und Ausgabe, je nachdem, ob sie **Defaultwerte** haben.
Sehen wir uns an, wie das funktioniert und wie Sie es bei Bedarf ändern können.
## Pydantic-Modelle für Eingabe und Ausgabe { #pydantic-models-for-input-and-output }
Nehmen wir an, Sie haben ein Pydantic-Modell mit Defaultwerten wie dieses:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modell für Eingabe { #model-for-input }
Wenn Sie dieses Modell wie hier als Eingabe verwenden:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
... dann ist das Feld `description` **nicht erforderlich**. Weil es den Defaultwert `None` hat.
### Eingabemodell in der Dokumentation { #input-model-in-docs }
Sie können überprüfen, dass das Feld `description` in der Dokumentation kein **rotes Sternchen** enthält, es ist nicht als erforderlich markiert:
### Modell für die Ausgabe { #model-for-output }
Wenn Sie jedoch dasselbe Modell als Ausgabe verwenden, wie hier:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
... dann, weil `description` einen Defaultwert hat, wird es, wenn Sie für dieses Feld **nichts zurückgeben**, immer noch diesen **Defaultwert** haben.
### Modell für Ausgabe-Responsedaten { #model-for-output-response-data }
Wenn Sie mit der Dokumentation interagieren und die Response überprüfen, enthält die JSON-Response den Defaultwert (`null`), obwohl der Code nichts in eines der `description`-Felder geschrieben hat:
Das bedeutet, dass es **immer einen Wert** hat, der Wert kann jedoch manchmal `None` sein (oder `null` in JSON).
Das bedeutet, dass Clients, die Ihre API verwenden, nicht prüfen müssen, ob der Wert vorhanden ist oder nicht. Sie können davon ausgehen, dass das Feld immer vorhanden ist. In einigen Fällen hat es jedoch nur den Defaultwert `None`.
Um dies in OpenAPI zu kennzeichnen, markieren Sie dieses Feld als **erforderlich**, da es immer vorhanden sein wird.
Aus diesem Grund kann das JSON-Schema für ein Modell unterschiedlich sein, je nachdem, ob es für **Eingabe oder Ausgabe** verwendet wird:
* für die **Eingabe** ist `description` **nicht erforderlich**
* für die **Ausgabe** ist es **erforderlich** (und möglicherweise `None` oder, in JSON-Begriffen, `null`)
### Ausgabemodell in der Dokumentation { #model-for-output-in-docs }
Sie können das Ausgabemodell auch in der Dokumentation überprüfen. **Sowohl** `name` **als auch** `description` sind mit einem **roten Sternchen** als **erforderlich** markiert:
### Eingabe- und Ausgabemodell in der Dokumentation { #model-for-input-and-output-in-docs }
Und wenn Sie alle verfügbaren Schemas (JSON-Schemas) in OpenAPI überprüfen, werden Sie feststellen, dass es zwei gibt, ein `Item-Input` und ein `Item-Output`.
Für `Item-Input` ist `description` **nicht erforderlich**, es hat kein rotes Sternchen.
Aber für `Item-Output` ist `description` **erforderlich**, es hat ein rotes Sternchen.
Mit dieser Funktion von **Pydantic v2** ist Ihre API-Dokumentation **präziser**, und wenn Sie über automatisch generierte Clients und SDKs verfügen, sind diese auch präziser, mit einer besseren **Entwicklererfahrung** und Konsistenz. 🎉
## Schemas nicht trennen { #do-not-separate-schemas }
Nun gibt es einige Fälle, in denen Sie möglicherweise **dasselbe Schema für Eingabe und Ausgabe** haben möchten.
Der Hauptanwendungsfall hierfür besteht wahrscheinlich darin, dass Sie das mal tun möchten, wenn Sie bereits über einige automatisch generierte Client-Codes/SDKs verfügen und im Moment nicht alle automatisch generierten Client-Codes/SDKs aktualisieren möchten, möglicherweise später, aber nicht jetzt.
In diesem Fall können Sie diese Funktion in **FastAPI** mit dem Parameter `separate_input_output_schemas=False` deaktivieren.
/// info | Info
Unterstützung für `separate_input_output_schemas` wurde in FastAPI `0.102.0` hinzugefügt. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Gleiches Schema für Eingabe- und Ausgabemodelle in der Dokumentation { #same-schema-for-input-and-output-models-in-docs }
Und jetzt wird es ein einziges Schema für die Eingabe und Ausgabe des Modells geben, nur `Item`, und es wird `description` als **nicht erforderlich** kennzeichnen:
================================================
FILE: docs/de/docs/how-to/testing-database.md
================================================
# Eine Datenbank testen { #testing-a-database }
Sie können sich über Datenbanken, SQL und SQLModel in der [SQLModel-Dokumentation](https://sqlmodel.tiangolo.com/) informieren. 🤓
Es gibt ein kurzes [Tutorial zur Verwendung von SQLModel mit FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
Dieses Tutorial enthält einen Abschnitt über das [Testen von SQL-Datenbanken](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/de/docs/index.md
================================================
# FastAPI { #fastapi }
FastAPI-Framework, hohe Performanz, leicht zu lernen, schnell zu entwickeln, produktionsreif
---
**Dokumentation**: [https://fastapi.tiangolo.com/de](https://fastapi.tiangolo.com/de)
**Quellcode**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI ist ein modernes, schnelles (hoch performantes) Webframework zur Erstellung von APIs mit Python auf Basis von Standard-Python-Typhinweisen.
Seine Schlüssel-Merkmale sind:
* **Schnell**: Sehr hohe Performanz, auf Augenhöhe mit **NodeJS** und **Go** (dank Starlette und Pydantic). [Eines der schnellsten verfügbaren Python-Frameworks](#performance).
* **Schnell zu entwickeln**: Erhöhen Sie die Geschwindigkeit bei der Entwicklung von Features um etwa 200 % bis 300 %. *
* **Weniger Bugs**: Verringern Sie die von Menschen (Entwicklern) verursachten Fehler um etwa 40 %. *
* **Intuitiv**: Hervorragende Editor-Unterstützung. Code-Vervollständigung überall. Weniger Zeit mit Debuggen verbringen.
* **Einfach**: So konzipiert, dass es einfach zu benutzen und zu erlernen ist. Weniger Zeit mit dem Lesen von Dokumentation verbringen.
* **Kurz**: Minimieren Sie die Verdoppelung von Code. Mehrere Features aus jeder Parameterdeklaration. Weniger Bugs.
* **Robust**: Erhalten Sie produktionsreifen Code. Mit automatischer, interaktiver Dokumentation.
* **Standards-basiert**: Basierend auf (und vollständig kompatibel mit) den offenen Standards für APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (früher bekannt als Swagger) und [JSON Schema](https://json-schema.org/).
* Schätzung basierend auf Tests, die von einem internen Entwicklungsteam durchgeführt wurden, das Produktionsanwendungen erstellt.
## Sponsoren { #sponsors }
### Keystone-Sponsor { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Gold- und Silber-Sponsoren { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Andere Sponsoren](https://fastapi.tiangolo.com/de/fastapi-people/#sponsors)
## Meinungen { #opinions }
„_[...] Ich verwende **FastAPI** heutzutage sehr oft. [...] Ich habe tatsächlich vor, es für alle **ML-Services meines Teams bei Microsoft** zu verwenden. Einige davon werden in das Kernprodukt **Windows** und einige **Office**-Produkte integriert._“
---
„_Wir haben die **FastAPI**-Bibliothek übernommen, um einen **REST**-Server zu erstellen, der für **Vorhersagen** abgefragt werden kann. [für Ludwig]_“
Piero Molino, Yaroslav Dudin, und Sai Sumanth Miryala – Uber(Ref.)
---
„_**Netflix** freut sich, die Open-Source-Veröffentlichung unseres **Krisenmanagement**-Orchestrierung-Frameworks bekannt zu geben: **Dispatch**! [erstellt mit **FastAPI**]_“
Kevin Glisson, Marc Vilanova, Forest Monsen – Netflix(Ref.)
---
„_Ich bin hellauf begeistert von **FastAPI**. Es macht so viel Spaß!_“
Brian Okken – [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855) Podcast-Host(Ref.)
---
„_Ehrlich, was Du gebaut hast, sieht super solide und poliert aus. In vielerlei Hinsicht ist es so, wie ich **Hug** haben wollte – es ist wirklich inspirierend, jemanden so etwas bauen zu sehen._“
---
„_Wenn Sie ein **modernes Framework** zum Erstellen von REST-APIs erlernen möchten, schauen Sie sich **FastAPI** an. [...] Es ist schnell, einfach zu verwenden und leicht zu lernen [...]_“
„_Wir haben zu **FastAPI** für unsere **APIs** gewechselt [...] Ich denke, es wird Ihnen gefallen [...]_“
---
„_Falls irgendjemand eine Produktions-Python-API erstellen möchte, kann ich **FastAPI** wärmstens empfehlen. Es ist **wunderschön konzipiert**, **einfach zu verwenden** und **hoch skalierbar**; es ist zu einer **Schlüsselkomponente** unserer API-First-Entwicklungsstrategie geworden und treibt viele Automatisierungen und Services an, wie etwa unseren Virtual TAC Engineer._“
---
## FastAPI Mini-Dokumentarfilm { #fastapi-mini-documentary }
Es gibt einen [FastAPI-Mini-Dokumentarfilm](https://www.youtube.com/watch?v=mpR8ngthqiE), veröffentlicht Ende 2025, Sie können ihn online ansehen:
## **Typer**, das FastAPI der CLIs { #typer-the-fastapi-of-clis }
Wenn Sie eine CLI-Anwendung für das Terminal erstellen, anstelle einer Web-API, schauen Sie sich [**Typer**](https://typer.tiangolo.com/) an.
**Typer** ist die kleine Schwester von FastAPI. Und es soll das **FastAPI der CLIs** sein. ⌨️ 🚀
## Anforderungen { #requirements }
FastAPI steht auf den Schultern von Giganten:
* [Starlette](https://www.starlette.dev/) für die Webanteile.
* [Pydantic](https://docs.pydantic.dev/) für die Datenanteile.
## Installation { #installation }
Erstellen und aktivieren Sie eine [virtuelle Umgebung](https://fastapi.tiangolo.com/de/virtual-environments/) und installieren Sie dann FastAPI:
**Hinweis**: Stellen Sie sicher, dass Sie `"fastapi[standard]"` in Anführungszeichen setzen, damit es in allen Terminals funktioniert.
## Beispiel { #example }
### Erstellung { #create-it }
Erstellen Sie eine Datei `main.py` mit:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Oder verwenden Sie async def ...
Wenn Ihr Code `async` / `await` verwendet, benutzen Sie `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Hinweis**:
Wenn Sie das nicht kennen, schauen Sie sich den Abschnitt _„In Eile?“_ über [`async` und `await` in der Dokumentation](https://fastapi.tiangolo.com/de/async/#in-a-hurry) an.
### Starten { #run-it }
Starten Sie den Server mit:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
Über den Befehl fastapi dev ...
Der Befehl `fastapi dev` liest Ihre `main.py`-Datei, erkennt die **FastAPI**-App darin und startet einen Server mit [Uvicorn](https://www.uvicorn.dev).
Standardmäßig wird `fastapi dev` mit aktiviertem Auto-Reload für die lokale Entwicklung gestartet.
Sie können mehr darüber in der [FastAPI CLI Dokumentation](https://fastapi.tiangolo.com/de/fastapi-cli/) lesen.
### Es testen { #check-it }
Öffnen Sie Ihren Browser unter [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Sie sehen die JSON-Response als:
```JSON
{"item_id": 5, "q": "somequery"}
```
Sie haben bereits eine API erstellt, welche:
* HTTP-Requests auf den _Pfaden_ `/` und `/items/{item_id}` entgegennimmt.
* Beide _Pfade_ nehmen `GET` Operationen (auch bekannt als HTTP-_Methoden_) entgegen.
* Der _Pfad_ `/items/{item_id}` hat einen _Pfad-Parameter_ `item_id`, der ein `int` sein sollte.
* Der _Pfad_ `/items/{item_id}` hat einen optionalen `str`-_Query-Parameter_ `q`.
### Interaktive API-Dokumentation { #interactive-api-docs }
Gehen Sie nun auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Sie sehen die automatische interaktive API-Dokumentation (bereitgestellt von [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Alternative API-Dokumentation { #alternative-api-docs }
Und jetzt gehen Sie auf [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Sie sehen die alternative automatische Dokumentation (bereitgestellt von [ReDoc](https://github.com/Rebilly/ReDoc)):

## Beispielaktualisierung { #example-upgrade }
Ändern Sie jetzt die Datei `main.py`, um den Body eines `PUT`-Requests zu empfangen.
Deklarieren Sie den Body mit Standard-Python-Typen, dank Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Der `fastapi dev`-Server sollte automatisch neu laden.
### Interaktive API-Dokumentation aktualisieren { #interactive-api-docs-upgrade }
Gehen Sie jetzt auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* Die interaktive API-Dokumentation wird automatisch aktualisiert, einschließlich des neuen Bodys:

* Klicken Sie auf den Button „Try it out“, damit können Sie die Parameter ausfüllen und direkt mit der API interagieren:

* Klicken Sie dann auf den Button „Execute“, die Benutzeroberfläche wird mit Ihrer API kommunizieren, die Parameter senden, die Ergebnisse erhalten und sie auf dem Bildschirm anzeigen:

### Alternative API-Dokumentation aktualisieren { #alternative-api-docs-upgrade }
Und jetzt gehen Sie auf [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* Die alternative Dokumentation wird ebenfalls den neuen Query-Parameter und Body widerspiegeln:

### Zusammenfassung { #recap }
Zusammengefasst deklarieren Sie **einmal** die Typen von Parametern, Body, usw. als Funktionsparameter.
Das machen Sie mit modernen Standard-Python-Typen.
Sie müssen keine neue Syntax, Methoden oder Klassen einer bestimmten Bibliothek usw. lernen.
Nur Standard-**Python**.
Zum Beispiel für ein `int`:
```Python
item_id: int
```
oder für ein komplexeres `Item`-Modell:
```Python
item: Item
```
... und mit dieser einen Deklaration erhalten Sie:
* Editor-Unterstützung, einschließlich:
* Vervollständigung.
* Typprüfungen.
* Validierung von Daten:
* Automatische und eindeutige Fehler, wenn die Daten ungültig sind.
* Validierung sogar für tief verschachtelte JSON-Objekte.
* Konvertierung von Eingabedaten: Aus dem Netzwerk kommend, zu Python-Daten und -Typen. Lesen von:
* JSON.
* Pfad-Parametern.
* Query-Parametern.
* Cookies.
* Headern.
* Formularen.
* Dateien.
* Konvertierung von Ausgabedaten: Konvertierung von Python-Daten und -Typen zu Netzwerkdaten (als JSON):
* Konvertieren von Python-Typen (`str`, `int`, `float`, `bool`, `list`, usw.).
* `datetime`-Objekte.
* `UUID`-Objekte.
* Datenbankmodelle.
* ... und viele mehr.
* Automatische interaktive API-Dokumentation, einschließlich zwei alternativer Benutzeroberflächen:
* Swagger UI.
* ReDoc.
---
Um auf das vorherige Codebeispiel zurückzukommen, **FastAPI** wird:
* Validieren, dass es eine `item_id` im Pfad für `GET`- und `PUT`-Requests gibt.
* Validieren, ob die `item_id` vom Typ `int` für `GET`- und `PUT`-Requests ist.
* Falls nicht, sieht der Client einen hilfreichen, klaren Fehler.
* Prüfen, ob es einen optionalen Query-Parameter namens `q` (wie in `http://127.0.0.1:8000/items/foo?q=somequery`) für `GET`-Requests gibt.
* Da der `q`-Parameter mit `= None` deklariert ist, ist er optional.
* Ohne das `None` wäre er erforderlich (wie der Body im Fall von `PUT`).
* Bei `PUT`-Requests an `/items/{item_id}` den Body als JSON lesen:
* Prüfen, ob er ein erforderliches Attribut `name` hat, das ein `str` sein muss.
* Prüfen, ob er ein erforderliches Attribut `price` hat, das ein `float` sein muss.
* Prüfen, ob er ein optionales Attribut `is_offer` hat, das ein `bool` sein muss, falls vorhanden.
* All dies würde auch für tief verschachtelte JSON-Objekte funktionieren.
* Automatisch von und nach JSON konvertieren.
* Alles mit OpenAPI dokumentieren, welches verwendet werden kann von:
* Interaktiven Dokumentationssystemen.
* Automatisch Client-Code generierenden Systemen für viele Sprachen.
* Zwei interaktive Dokumentations-Weboberflächen direkt bereitstellen.
---
Wir haben nur an der Oberfläche gekratzt, aber Sie bekommen schon eine Vorstellung davon, wie das Ganze funktioniert.
Versuchen Sie, diese Zeile zu ändern:
```Python
return {"item_name": item.name, "item_id": item_id}
```
... von:
```Python
... "item_name": item.name ...
```
... zu:
```Python
... "item_price": item.price ...
```
... und sehen Sie, wie Ihr Editor die Attribute automatisch vervollständigt und ihre Typen kennt:

Für ein vollständigeres Beispiel, mit weiteren Funktionen, siehe das Tutorial – Benutzerhandbuch.
**Spoiler-Alarm**: Das Tutorial – Benutzerhandbuch enthält:
* Deklaration von **Parametern** von anderen verschiedenen Stellen wie: **Header**, **Cookies**, **Formularfelder** und **Dateien**.
* Wie man **Validierungs-Constraints** wie `maximum_length` oder `regex` setzt.
* Ein sehr leistungsfähiges und einfach zu bedienendes System für **Dependency Injection**.
* Sicherheit und Authentifizierung, einschließlich Unterstützung für **OAuth2** mit **JWT-Tokens** und **HTTP Basic** Authentifizierung.
* Fortgeschrittenere (aber ebenso einfache) Techniken zur Deklaration **tief verschachtelter JSON-Modelle** (dank Pydantic).
* **GraphQL**-Integration mit [Strawberry](https://strawberry.rocks) und anderen Bibliotheken.
* Viele zusätzliche Features (dank Starlette) wie:
* **WebSockets**
* extrem einfache Tests auf Basis von HTTPX und `pytest`
* **CORS**
* **Cookie-Sessions**
* ... und mehr.
### Ihre App deployen (optional) { #deploy-your-app-optional }
Optional können Sie Ihre FastAPI-App in die [FastAPI Cloud](https://fastapicloud.com) deployen, gehen Sie und treten Sie der Warteliste bei, falls noch nicht geschehen. 🚀
Wenn Sie bereits ein **FastAPI Cloud**-Konto haben (wir haben Sie von der Warteliste eingeladen 😉), können Sie Ihre Anwendung mit einem einzigen Befehl deployen.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Das war’s! Jetzt können Sie unter dieser URL auf Ihre App zugreifen. ✨
#### Über FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** wird vom selben Autor und Team hinter **FastAPI** entwickelt.
Es vereinfacht den Prozess des **Erstellens**, **Deployens** und **Zugreifens** auf eine API mit minimalem Aufwand.
Es bringt die gleiche **Developer-Experience** beim Erstellen von Apps mit FastAPI auch zum **Deployment** in der Cloud. 🎉
FastAPI Cloud ist der Hauptsponsor und Finanzierer der *FastAPI and friends* Open-Source-Projekte. ✨
#### Bei anderen Cloudanbietern deployen { #deploy-to-other-cloud-providers }
FastAPI ist Open Source und basiert auf Standards. Sie können FastAPI-Apps bei jedem Cloudanbieter Ihrer Wahl deployen.
Folgen Sie den Anleitungen Ihres Cloudanbieters, um FastAPI-Apps dort bereitzustellen. 🤓
## Performanz { #performance }
Unabhängige TechEmpower-Benchmarks zeigen **FastAPI**-Anwendungen, die unter Uvicorn laufen, als [eines der schnellsten verfügbaren Python-Frameworks](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), nur hinter Starlette und Uvicorn selbst (intern von FastAPI verwendet). (*)
Um mehr darüber zu erfahren, siehe den Abschnitt [Benchmarks](https://fastapi.tiangolo.com/de/benchmarks/).
## Abhängigkeiten { #dependencies }
FastAPI hängt von Pydantic und Starlette ab.
### `standard`-Abhängigkeiten { #standard-dependencies }
Wenn Sie FastAPI mit `pip install "fastapi[standard]"` installieren, kommt es mit der `standard`-Gruppe optionaler Abhängigkeiten:
Verwendet von Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) – für E-Mail-Validierung.
Verwendet von Starlette:
* [`httpx`](https://www.python-httpx.org) – erforderlich, wenn Sie den `TestClient` verwenden möchten.
* [`jinja2`](https://jinja.palletsprojects.com) – erforderlich, wenn Sie die Default-Template-Konfiguration verwenden möchten.
* [`python-multipart`](https://github.com/Kludex/python-multipart) – erforderlich, wenn Sie Formulare mittels `request.form()` „parsen“ möchten.
Verwendet von FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) – für den Server, der Ihre Anwendung lädt und bereitstellt. Dies umfasst `uvicorn[standard]`, das einige Abhängigkeiten (z. B. `uvloop`) beinhaltet, die für eine Bereitstellung mit hoher Performanz benötigt werden.
* `fastapi-cli[standard]` – um den `fastapi`-Befehl bereitzustellen.
* Dies beinhaltet `fastapi-cloud-cli`, das es Ihnen ermöglicht, Ihre FastAPI-Anwendung auf [FastAPI Cloud](https://fastapicloud.com) bereitzustellen.
### Ohne `standard`-Abhängigkeiten { #without-standard-dependencies }
Wenn Sie die `standard` optionalen Abhängigkeiten nicht einschließen möchten, können Sie mit `pip install fastapi` statt `pip install "fastapi[standard]"` installieren.
### Ohne `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Wenn Sie FastAPI mit den Standardabhängigkeiten, aber ohne das `fastapi-cloud-cli` installieren möchten, können Sie mit `pip install "fastapi[standard-no-fastapi-cloud-cli]"` installieren.
### Zusätzliche optionale Abhängigkeiten { #additional-optional-dependencies }
Es gibt einige zusätzliche Abhängigkeiten, die Sie installieren möchten.
Zusätzliche optionale Pydantic-Abhängigkeiten:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) – für die Verwaltung von Einstellungen.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) – für zusätzliche Typen zur Verwendung mit Pydantic.
Zusätzliche optionale FastAPI-Abhängigkeiten:
* [`orjson`](https://github.com/ijl/orjson) – erforderlich, wenn Sie `ORJSONResponse` verwenden möchten.
* [`ujson`](https://github.com/esnme/ultrajson) – erforderlich, wenn Sie `UJSONResponse` verwenden möchten.
## Lizenz { #license }
Dieses Projekt ist unter den Bedingungen der MIT-Lizenz lizenziert.
================================================
FILE: docs/de/docs/learn/index.md
================================================
# Lernen { #learn }
Hier sind die einführenden Abschnitte und Tutorials, um **FastAPI** zu lernen.
Sie könnten dies als **Buch**, als **Kurs**, als **offizielle** und empfohlene Methode zum Erlernen von FastAPI betrachten. 😎
================================================
FILE: docs/de/docs/project-generation.md
================================================
# Full Stack FastAPI Template { #full-stack-fastapi-template }
Vorlagen, die normalerweise mit einem bestimmten Setup geliefert werden, sind so konzipiert, dass sie flexibel und anpassbar sind. Dies ermöglicht es Ihnen, sie zu ändern und an die Anforderungen Ihres Projekts anzupassen und sie somit zu einem hervorragenden Ausgangspunkt zu machen. 🏁
Sie können diese Vorlage verwenden, um loszulegen, da sie bereits vieles der anfänglichen Einrichtung, Sicherheit, Datenbank und einige API-Endpunkte für Sie eingerichtet hat.
GitHub-Repository: [Full Stack FastAPI Template](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI Template – Technologiestack und Funktionen { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/de) für die Python-Backend-API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) für die Interaktion mit der Python-SQL-Datenbank (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), verwendet von FastAPI, für die Datenvalidierung und das Einstellungsmanagement.
- 💾 [PostgreSQL](https://www.postgresql.org) als SQL-Datenbank.
- 🚀 [React](https://react.dev) für das Frontend.
- 💃 Verwendung von TypeScript, Hooks, Vite und anderen Teilen eines modernen Frontend-Stacks.
- 🎨 [Tailwind CSS](https://tailwindcss.com) und [shadcn/ui](https://ui.shadcn.com) für die Frontend-Komponenten.
- 🤖 Ein automatisch generierter Frontend-Client.
- 🧪 [Playwright](https://playwright.dev) für End-to-End-Tests.
- 🦇 „Dark-Mode“-Unterstützung.
- 🐋 [Docker Compose](https://www.docker.com) für Entwicklung und Produktion.
- 🔒 Sicheres Passwort-Hashing standardmäßig.
- 🔑 JWT (JSON Web Token)-Authentifizierung.
- 📫 E-Mail-basierte Passwortwiederherstellung.
- ✅ Tests mit [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) als Reverse-Proxy / Load Balancer.
- 🚢 Deployment-Anleitungen unter Verwendung von Docker Compose, einschließlich der Einrichtung eines Frontend-Traefik-Proxys zur Handhabung automatischer HTTPS-Zertifikate.
- 🏭 CI (kontinuierliche Integration) und CD (kontinuierliches Deployment) basierend auf GitHub Actions.
================================================
FILE: docs/de/docs/python-types.md
================================================
# Einführung in Python-Typen { #python-types-intro }
Python hat Unterstützung für optionale „Typhinweise“ (auch „Typannotationen“ genannt).
Diese **„Typhinweise“** oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den Typ einer Variablen zu deklarieren.
Durch das Deklarieren von Typen für Ihre Variablen können Editoren und Tools bessere Unterstützung bieten.
Dies ist lediglich eine **schnelle Anleitung / Auffrischung** über Pythons Typhinweise. Sie deckt nur das Minimum ab, das nötig ist, um diese mit **FastAPI** zu verwenden ... was tatsächlich sehr wenig ist.
**FastAPI** basiert vollständig auf diesen Typhinweisen, sie geben der Anwendung viele Vorteile und Möglichkeiten.
Aber selbst wenn Sie **FastAPI** nie verwenden, wird es für Sie nützlich sein, ein wenig darüber zu lernen.
/// note | Hinweis
Wenn Sie ein Python-Experte sind und bereits alles über Typhinweise wissen, überspringen Sie dieses Kapitel und fahren Sie mit dem nächsten fort.
///
## Motivation { #motivation }
Fangen wir mit einem einfachen Beispiel an:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Dieses Programm gibt aus:
```
John Doe
```
Die Funktion macht Folgendes:
* Nimmt einen `first_name` und `last_name`.
* Schreibt den ersten Buchstaben eines jeden Wortes groß, mithilfe von `title()`.
* Verkettet sie mit einem Leerzeichen in der Mitte.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Es bearbeiten { #edit-it }
Es ist ein sehr einfaches Programm.
Aber nun stellen Sie sich vor, Sie würden es selbst schreiben.
Irgendwann sind die Funktions-Parameter fertig, Sie starten mit der Definition des Körpers ...
Aber dann müssen Sie „diese Methode aufrufen, die den ersten Buchstaben in Großbuchstaben umwandelt“.
War es `upper`? War es `uppercase`? `first_uppercase`? `capitalize`?
Dann versuchen Sie es mit dem langjährigen Freund des Programmierers, der Editor-Autovervollständigung.
Sie geben den ersten Parameter der Funktion ein, `first_name`, dann einen Punkt (`.`) und drücken `Strg+Leertaste`, um die Vervollständigung auszulösen.
Aber leider erhalten Sie nichts Nützliches:
### Typen hinzufügen { #add-types }
Lassen Sie uns eine einzelne Zeile aus der vorherigen Version ändern.
Wir ändern den folgenden Teil, die Parameter der Funktion, von:
```Python
first_name, last_name
```
zu:
```Python
first_name: str, last_name: str
```
Das war's.
Das sind die „Typhinweise“:
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Das ist nicht das gleiche wie das Deklarieren von Defaultwerten, wie es hier der Fall ist:
```Python
first_name="john", last_name="doe"
```
Das ist eine andere Sache.
Wir verwenden Doppelpunkte (`:`), nicht Gleichheitszeichen (`=`).
Und das Hinzufügen von Typhinweisen ändert normalerweise nichts an dem, was ohne sie passieren würde.
Aber jetzt stellen Sie sich vor, Sie sind wieder mitten in der Erstellung dieser Funktion, aber mit Typhinweisen.
An derselben Stelle versuchen Sie, die Autovervollständigung mit „Strg+Leertaste“ auszulösen, und Sie sehen:
Hier können Sie durch die Optionen blättern, bis Sie diejenige finden, bei der es „Klick“ macht:
## Mehr Motivation { #more-motivation }
Sehen Sie sich diese Funktion an, sie hat bereits Typhinweise:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervollständigung, sondern auch eine Fehlerprüfung:
Jetzt, da Sie wissen, dass Sie das reparieren müssen, konvertieren Sie `age` mittels `str(age)` in einen String:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Deklarieren von Typen { #declaring-types }
Sie haben gerade den Haupt-Einsatzort für die Deklaration von Typhinweisen gesehen. Als Funktionsparameter.
Das ist auch meistens, wie sie in **FastAPI** verwendet werden.
### Einfache Typen { #simple-types }
Sie können alle Standard-Python-Typen deklarieren, nicht nur `str`.
Zum Beispiel diese:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### `typing`-Modul { #typing-module }
Für einige zusätzliche Anwendungsfälle müssen Sie möglicherweise Dinge aus dem Standardmodul `typing` importieren. Zum Beispiel, wenn Sie deklarieren möchten, dass etwas „jeden Typ“ haben kann, können Sie `Any` aus `typing` verwenden:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generische Typen { #generic-types }
Einige Typen können „Typ-Parameter“ in eckigen Klammern annehmen, um ihre inneren Typen zu definieren, z. B. eine „Liste von Strings“ würde als `list[str]` deklariert.
Diese Typen, die Typ-Parameter annehmen können, werden **generische Typen** oder **Generics** genannt.
Sie können dieselben eingebauten Typen als Generics verwenden (mit eckigen Klammern und Typen darin):
* `list`
* `tuple`
* `set`
* `dict`
#### Liste { #list }
Definieren wir zum Beispiel eine Variable, die eine `list` von `str` – eine Liste von Strings – sein soll.
Deklarieren Sie die Variable mit der gleichen Doppelpunkt-Syntax (`:`).
Als Typ nehmen Sie `list`.
Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Info
Die inneren Typen in den eckigen Klammern werden als „Typ-Parameter“ bezeichnet.
In diesem Fall ist `str` der Typ-Parameter, der an `list` übergeben wird.
///
Das bedeutet: Die Variable `items` ist eine Liste – `list` – und jedes der Elemente in dieser Liste ist ein String – `str`.
Auf diese Weise kann Ihr Editor Sie auch bei der Bearbeitung von Einträgen aus der Liste unterstützen:
Ohne Typen ist das fast unmöglich zu erreichen.
Beachten Sie, dass die Variable `item` eines der Elemente in der Liste `items` ist.
Und trotzdem weiß der Editor, dass es sich um ein `str` handelt, und bietet entsprechende Unterstützung.
#### Tupel und Menge { #tuple-and-set }
Das Gleiche gilt für die Deklaration eines Tupels – `tuple` – und einer Menge – `set`:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Das bedeutet:
* Die Variable `items_t` ist ein `tuple` mit 3 Elementen, einem `int`, einem weiteren `int` und einem `str`.
* Die Variable `items_s` ist ein `set`, und jedes seiner Elemente ist vom Typ `bytes`.
#### Dict { #dict }
Um ein `dict` zu definieren, übergeben Sie zwei Typ-Parameter, getrennt durch Kommas.
Der erste Typ-Parameter ist für die Schlüssel des `dict`.
Der zweite Typ-Parameter ist für die Werte des `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Das bedeutet:
* Die Variable `prices` ist ein `dict`:
* Die Schlüssel dieses `dict` sind vom Typ `str` (z. B. die Namen der einzelnen Artikel).
* Die Werte dieses `dict` sind vom Typ `float` (z. B. der Preis jedes Artikels).
#### Union { #union }
Sie können deklarieren, dass eine Variable einer von **verschiedenen Typen** sein kann, zum Beispiel ein `int` oder ein `str`.
Um das zu definieren, verwenden Sie den vertikalen Balken (`|`), um beide Typen zu trennen.
Das wird „Union“ genannt, weil die Variable etwas aus der Vereinigung dieser beiden Typmengen sein kann.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Das bedeutet, dass `item` ein `int` oder ein `str` sein könnte.
#### Vielleicht `None` { #possibly-none }
Sie können deklarieren, dass ein Wert einen Typ haben könnte, wie `str`, dass er aber auch `None` sein könnte.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Wenn Sie `str | None` anstelle von nur `str` verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer ein `str` ist, obwohl er auch `None` sein könnte.
### Klassen als Typen { #classes-as-types }
Sie können auch eine Klasse als Typ einer Variablen deklarieren.
Nehmen wir an, Sie haben eine Klasse `Person`, mit einem Namen:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Dann können Sie eine Variable vom Typ `Person` deklarieren:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
Und wiederum bekommen Sie die volle Editor-Unterstützung:
Beachten Sie, das bedeutet: „`one_person` ist eine **Instanz** der Klasse `Person`“.
Es bedeutet nicht: „`one_person` ist die **Klasse** genannt `Person`“.
## Pydantic-Modelle { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) ist eine Python-Bibliothek für die Validierung von Daten.
Sie deklarieren die „Form“ der Daten als Klassen mit Attributen.
Und jedes Attribut hat einen Typ.
Dann erzeugen Sie eine Instanz dieser Klasse mit einigen Werten, und Pydantic validiert die Werte, konvertiert sie in den passenden Typ (falls notwendig) und gibt Ihnen ein Objekt mit allen Daten.
Und Sie erhalten volle Editor-Unterstützung für dieses Objekt.
Ein Beispiel aus der offiziellen Pydantic Dokumentation:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Info
Um mehr über [Pydantic zu erfahren, schauen Sie sich dessen Dokumentation an](https://docs.pydantic.dev/).
///
**FastAPI** basiert vollständig auf Pydantic.
Viel mehr von all dem werden Sie in praktischer Anwendung im [Tutorial – Benutzerhandbuch](tutorial/index.md) sehen.
## Typhinweise mit Metadaten-Annotationen { #type-hints-with-metadata-annotations }
Python bietet auch die Möglichkeit, **zusätzliche Metadaten** in Typhinweisen unterzubringen, mittels `Annotated`.
Sie können `Annotated` von `typing` importieren.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python selbst macht nichts mit `Annotated`. Für Editoren und andere Tools ist der Typ immer noch `str`.
Aber Sie können `Annotated` nutzen, um **FastAPI** mit Metadaten zu versorgen, die ihm sagen, wie sich Ihre Anwendung verhalten soll.
Wichtig ist, dass **der erste *Typ-Parameter***, den Sie `Annotated` übergeben, der **tatsächliche Typ** ist. Der Rest sind Metadaten für andere Tools.
Im Moment müssen Sie nur wissen, dass `Annotated` existiert, und dass es Standard-Python ist. 😎
Später werden Sie sehen, wie **mächtig** es sein kann.
/// tip | Tipp
Der Umstand, dass es **Standard-Python** ist, bedeutet, dass Sie immer noch die **bestmögliche Entwickler-Erfahrung** in Ihrem Editor haben, sowie mit den Tools, die Sie nutzen, um Ihren Code zu analysieren, zu refaktorisieren, usw. ✨
Und ebenfalls, dass Ihr Code sehr kompatibel mit vielen anderen Python-Tools und -Bibliotheken sein wird. 🚀
///
## Typhinweise in **FastAPI** { #type-hints-in-fastapi }
**FastAPI** macht sich diese Typhinweise zunutze, um mehrere Dinge zu tun.
Mit **FastAPI** deklarieren Sie Parameter mit Typhinweisen, und Sie erhalten:
* **Editorunterstützung**.
* **Typ-Prüfungen**.
... und **FastAPI** verwendet dieselben Deklarationen, um:
* **Anforderungen** zu definieren: aus Request-Pfadparametern, Query-Parametern, Headern, Bodys, Abhängigkeiten, usw.
* **Daten umzuwandeln**: aus dem Request in den erforderlichen Typ.
* **Daten zu validieren**: aus jedem Request:
* **Automatische Fehler** generieren, die an den Client zurückgegeben werden, wenn die Daten ungültig sind.
* Die API mit OpenAPI zu **dokumentieren**:
* Die dann von den Benutzeroberflächen der automatisch generierten interaktiven Dokumentation verwendet wird.
Das mag alles abstrakt klingen. Machen Sie sich keine Sorgen. Sie werden all das in Aktion sehen im [Tutorial – Benutzerhandbuch](tutorial/index.md).
Das Wichtigste ist, dass **FastAPI** durch die Verwendung von Standard-Python-Typen an einer einzigen Stelle (anstatt weitere Klassen, Dekoratoren usw. hinzuzufügen) einen Großteil der Arbeit für Sie erledigt.
/// info | Info
Wenn Sie bereits das ganze Tutorial durchgearbeitet haben und mehr über Typen erfahren wollen, dann ist eine gute Ressource [der „Cheat Sheet“ von `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/de/docs/resources/index.md
================================================
# Ressourcen { #resources }
Zusätzliche Ressourcen, externe Links und mehr. ✈️
================================================
FILE: docs/de/docs/translation-banner.md
================================================
/// details | 🌐 Übersetzung durch KI und Menschen
Diese Übersetzung wurde von KI erstellt, angeleitet von Menschen. 🤝
Sie könnte Fehler enthalten, etwa Missverständnisse des ursprünglichen Sinns oder unnatürliche Formulierungen, usw. 🤖
Sie können diese Übersetzung verbessern, indem Sie [uns helfen, die KI-LLM besser anzuleiten](https://fastapi.tiangolo.com/de/contributing/#translations).
[Englische Version](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/de/docs/tutorial/background-tasks.md
================================================
# Hintergrundtasks { #background-tasks }
Sie können Hintergrundtasks definieren, die *nach* der Rückgabe einer Response ausgeführt werden sollen.
Das ist nützlich für Vorgänge, die nach einem Request ausgeführt werden müssen, bei denen der Client jedoch nicht unbedingt auf den Abschluss des Vorgangs warten muss, bevor er die Response erhält.
Hierzu zählen beispielsweise:
* E-Mail-Benachrichtigungen, die nach dem Ausführen einer Aktion gesendet werden:
* Da die Verbindung zu einem E-Mail-Server und das Senden einer E-Mail in der Regel „langsam“ ist (einige Sekunden), können Sie die Response sofort zurücksenden und die E-Mail-Benachrichtigung im Hintergrund senden.
* Daten verarbeiten:
* Angenommen, Sie erhalten eine Datei, die einen langsamen Prozess durchlaufen muss. Sie können als Response „Accepted“ (HTTP 202) zurückgeben und die Datei im Hintergrund verarbeiten.
## `BackgroundTasks` verwenden { #using-backgroundtasks }
Importieren Sie zunächst `BackgroundTasks` und definieren Sie einen Parameter in Ihrer *Pfadoperation-Funktion* mit der Typdeklaration `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** erstellt für Sie das Objekt vom Typ `BackgroundTasks` und übergibt es als diesen Parameter.
## Eine Taskfunktion erstellen { #create-a-task-function }
Erstellen Sie eine Funktion, die als Hintergrundtask ausgeführt werden soll.
Es handelt sich schlicht um eine Standard-Funktion, die Parameter empfangen kann.
Es kann sich um eine `async def`- oder normale `def`-Funktion handeln. **FastAPI** weiß, wie damit zu verfahren ist.
In diesem Fall schreibt die Taskfunktion in eine Datei (den Versand einer E-Mail simulierend).
Und da der Schreibvorgang nicht `async` und `await` verwendet, definieren wir die Funktion mit normalem `def`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Den Hintergrundtask hinzufügen { #add-the-background-task }
Übergeben Sie innerhalb Ihrer *Pfadoperation-Funktion* Ihre Taskfunktion mit der Methode `.add_task()` an das *Hintergrundtasks*-Objekt:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` erhält als Argumente:
* Eine Taskfunktion, die im Hintergrund ausgeführt wird (`write_notification`).
* Eine beliebige Folge von Argumenten, die der Reihe nach an die Taskfunktion übergeben werden sollen (`email`).
* Alle Schlüsselwort-Argumente, die an die Taskfunktion übergeben werden sollen (`message="some notification"`).
## Dependency Injection { #dependency-injection }
Die Verwendung von `BackgroundTasks` funktioniert auch mit dem Dependency Injection System. Sie können einen Parameter vom Typ `BackgroundTasks` auf mehreren Ebenen deklarieren: in einer *Pfadoperation-Funktion*, in einer Abhängigkeit (Dependable), in einer Unterabhängigkeit usw.
**FastAPI** weiß, was jeweils zu tun ist und wie dasselbe Objekt wiederverwendet werden kann, sodass alle Hintergrundtasks zusammengeführt und anschließend im Hintergrund ausgeführt werden:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
In obigem Beispiel werden die Nachrichten, *nachdem* die Response gesendet wurde, in die Datei `log.txt` geschrieben.
Wenn im Request ein Query-Parameter enthalten war, wird dieser in einem Hintergrundtask in das Log geschrieben.
Und dann schreibt ein weiterer Hintergrundtask, der in der *Pfadoperation-Funktion* erstellt wird, eine Nachricht unter Verwendung des Pfad-Parameters `email`.
## Technische Details { #technical-details }
Die Klasse `BackgroundTasks` stammt direkt von [`starlette.background`](https://www.starlette.dev/background/).
Sie wird direkt in FastAPI importiert/inkludiert, sodass Sie sie von `fastapi` importieren können und vermeiden, versehentlich das alternative `BackgroundTask` (ohne das `s` am Ende) von `starlette.background` zu importieren.
Indem Sie nur `BackgroundTasks` (und nicht `BackgroundTask`) verwenden, ist es dann möglich, es als *Pfadoperation-Funktion*-Parameter zu verwenden und **FastAPI** den Rest für Sie erledigen zu lassen, genau wie bei der direkten Verwendung des `Request`-Objekts.
Es ist immer noch möglich, `BackgroundTask` allein in FastAPI zu verwenden, aber Sie müssen das Objekt in Ihrem Code erstellen und eine Starlette-`Response` zurückgeben, die es enthält.
Weitere Details finden Sie in [Starlettes offizieller Dokumentation für Hintergrundtasks](https://www.starlette.dev/background/).
## Vorbehalt { #caveat }
Wenn Sie umfangreiche Hintergrundberechnungen durchführen müssen und diese nicht unbedingt vom selben Prozess ausgeführt werden müssen (z. B. müssen Sie Speicher, Variablen, usw. nicht gemeinsam nutzen), könnte die Verwendung anderer größerer Tools wie z. B. [Celery](https://docs.celeryq.dev) von Vorteil sein.
Sie erfordern in der Regel komplexere Konfigurationen und einen Nachrichten-/Job-Queue-Manager wie RabbitMQ oder Redis, ermöglichen Ihnen jedoch die Ausführung von Hintergrundtasks in mehreren Prozessen und insbesondere auf mehreren Servern.
Wenn Sie jedoch über dieselbe **FastAPI**-App auf Variablen und Objekte zugreifen oder kleine Hintergrundtasks ausführen müssen (z. B. das Senden einer E-Mail-Benachrichtigung), können Sie einfach `BackgroundTasks` verwenden.
## Zusammenfassung { #recap }
Importieren und verwenden Sie `BackgroundTasks` mit Parametern in *Pfadoperation-Funktionen* und Abhängigkeiten, um Hintergrundtasks hinzuzufügen.
================================================
FILE: docs/de/docs/tutorial/bigger-applications.md
================================================
# Größere Anwendungen – mehrere Dateien { #bigger-applications-multiple-files }
Wenn Sie eine Anwendung oder eine Web-API erstellen, ist es selten der Fall, dass Sie alles in einer einzigen Datei unterbringen können.
**FastAPI** bietet ein praktisches Werkzeug zur Strukturierung Ihrer Anwendung bei gleichzeitiger Wahrung der Flexibilität.
/// info | Info
Wenn Sie von Flask kommen, wäre dies das Äquivalent zu Flasks Blueprints.
///
## Eine Beispiel-Dateistruktur { #an-example-file-structure }
Nehmen wir an, Sie haben eine Dateistruktur wie diese:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Tipp
Es gibt mehrere `__init__.py`-Dateien: eine in jedem Verzeichnis oder Unterverzeichnis.
Das ermöglicht den Import von Code aus einer Datei in eine andere.
In `app/main.py` könnten Sie beispielsweise eine Zeile wie diese haben:
```
from app.routers import items
```
///
* Das Verzeichnis `app` enthält alles. Und es hat eine leere Datei `app/__init__.py`, es handelt sich also um ein „Python-Package“ (eine Sammlung von „Python-Modulen“): `app`.
* Es enthält eine Datei `app/main.py`. Da sie sich in einem Python-Package (einem Verzeichnis mit einer Datei `__init__.py`) befindet, ist sie ein „Modul“ dieses Packages: `app.main`.
* Es gibt auch eine Datei `app/dependencies.py`, genau wie `app/main.py` ist sie ein „Modul“: `app.dependencies`.
* Es gibt ein Unterverzeichnis `app/routers/` mit einer weiteren Datei `__init__.py`, es handelt sich also um ein „Python-Subpackage“: `app.routers`.
* Die Datei `app/routers/items.py` befindet sich in einem Package, `app/routers/`, also ist sie ein Submodul: `app.routers.items`.
* Das Gleiche gilt für `app/routers/users.py`, es ist ein weiteres Submodul: `app.routers.users`.
* Es gibt auch ein Unterverzeichnis `app/internal/` mit einer weiteren Datei `__init__.py`, es handelt sich also um ein weiteres „Python-Subpackage“: `app.internal`.
* Und die Datei `app/internal/admin.py` ist ein weiteres Submodul: `app.internal.admin`.
Die gleiche Dateistruktur mit Kommentaren:
```bash
.
├── app # "app" ist ein Python-Package
│ ├── __init__.py # diese Datei macht "app" zu einem "Python-Package"
│ ├── main.py # "main"-Modul, z. B. import app.main
│ ├── dependencies.py # "dependencies"-Modul, z. B. import app.dependencies
│ └── routers # "routers" ist ein "Python-Subpackage"
│ │ ├── __init__.py # macht "routers" zu einem "Python-Subpackage"
│ │ ├── items.py # "items"-Submodul, z. B. import app.routers.items
│ │ └── users.py # "users"-Submodul, z. B. import app.routers.users
│ └── internal # "internal" ist ein "Python-Subpackage"
│ ├── __init__.py # macht "internal" zu einem "Python-Subpackage"
│ └── admin.py # "admin"-Submodul, z. B. import app.internal.admin
```
## `APIRouter` { #apirouter }
Nehmen wir an, die Datei, die nur für die Verwaltung von Benutzern zuständig ist, ist das Submodul unter `/app/routers/users.py`.
Sie möchten die *Pfadoperationen* für Ihre Benutzer vom Rest des Codes trennen, um ihn organisiert zu halten.
Aber es ist immer noch Teil derselben **FastAPI**-Anwendung/Web-API (es ist Teil desselben „Python-Packages“).
Sie können die *Pfadoperationen* für dieses Modul mit `APIRouter` erstellen.
### `APIRouter` importieren { #import-apirouter }
Sie importieren ihn und erstellen eine „Instanz“ auf die gleiche Weise wie mit der Klasse `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Pfadoperationen* mit `APIRouter` { #path-operations-with-apirouter }
Und dann verwenden Sie ihn, um Ihre *Pfadoperationen* zu deklarieren.
Verwenden Sie ihn auf die gleiche Weise wie die Klasse `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Sie können sich `APIRouter` als eine „Mini-`FastAPI`“-Klasse vorstellen.
Alle die gleichen Optionen werden unterstützt.
Alle die gleichen `parameters`, `responses`, `dependencies`, `tags`, usw.
/// tip | Tipp
In diesem Beispiel heißt die Variable `router`, aber Sie können ihr einen beliebigen Namen geben.
///
Wir werden diesen `APIRouter` in die Hauptanwendung `FastAPI` einbinden, aber zuerst kümmern wir uns um die Abhängigkeiten und einen anderen `APIRouter`.
## Abhängigkeiten { #dependencies }
Wir sehen, dass wir einige Abhängigkeiten benötigen, die an mehreren Stellen der Anwendung verwendet werden.
Also fügen wir sie in ihr eigenes `dependencies`-Modul (`app/dependencies.py`) ein.
Wir werden nun eine einfache Abhängigkeit verwenden, um einen benutzerdefinierten `X-Token`-Header zu lesen:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Tipp
Um dieses Beispiel zu vereinfachen, verwenden wir einen erfundenen Header.
Aber in der Praxis werden Sie mit den integrierten [Sicherheits-Werkzeugen](security/index.md) bessere Ergebnisse erzielen.
///
## Ein weiteres Modul mit `APIRouter` { #another-module-with-apirouter }
Nehmen wir an, Sie haben im Modul unter `app/routers/items.py` auch die Endpunkte, die für die Verarbeitung von Artikeln („Items“) aus Ihrer Anwendung vorgesehen sind.
Sie haben *Pfadoperationen* für:
* `/items/`
* `/items/{item_id}`
Es ist alles die gleiche Struktur wie bei `app/routers/users.py`.
Aber wir wollen schlauer sein und den Code etwas vereinfachen.
Wir wissen, dass alle *Pfadoperationen* in diesem Modul folgendes haben:
* Pfad-`prefix`: `/items`.
* `tags`: (nur ein Tag: `items`).
* Zusätzliche `responses`.
* `dependencies`: Sie alle benötigen die von uns erstellte `X-Token`-Abhängigkeit.
Anstatt also alles zu jeder *Pfadoperation* hinzuzufügen, können wir es dem `APIRouter` hinzufügen.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Da der Pfad jeder *Pfadoperation* mit `/` beginnen muss, wie in:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
... darf das Präfix kein abschließendes `/` enthalten.
Das Präfix lautet in diesem Fall also `/items`.
Wir können auch eine Liste von `tags` und zusätzliche `responses` hinzufügen, die auf alle in diesem Router enthaltenen *Pfadoperationen* angewendet werden.
Und wir können eine Liste von `dependencies` hinzufügen, die allen *Pfadoperationen* im Router hinzugefügt und für jeden an sie gerichteten Request ausgeführt/aufgelöst werden.
/// tip | Tipp
Beachten Sie, dass ähnlich wie bei [Abhängigkeiten in *Pfadoperation-Dekoratoren*](dependencies/dependencies-in-path-operation-decorators.md) kein Wert an Ihre *Pfadoperation-Funktion* übergeben wird.
///
Das Endergebnis ist, dass die Pfade für diese Artikel jetzt wie folgt lauten:
* `/items/`
* `/items/{item_id}`
... wie wir es beabsichtigt hatten.
* Sie werden mit einer Liste von Tags gekennzeichnet, die einen einzelnen String „items“ enthält.
* Diese „Tags“ sind besonders nützlich für die automatischen interaktiven Dokumentationssysteme (unter Verwendung von OpenAPI).
* Alle enthalten die vordefinierten `responses`.
* Für alle diese *Pfadoperationen* wird die Liste der `dependencies` ausgewertet/ausgeführt, bevor sie selbst ausgeführt werden.
* Wenn Sie außerdem Abhängigkeiten in einer bestimmten *Pfadoperation* deklarieren, **werden diese ebenfalls ausgeführt**.
* Zuerst werden die Router-Abhängigkeiten ausgeführt, dann die [`dependencies` im Dekorator](dependencies/dependencies-in-path-operation-decorators.md) und dann die normalen Parameterabhängigkeiten.
* Sie können auch [`Security`-Abhängigkeiten mit `scopes`](../advanced/security/oauth2-scopes.md) hinzufügen.
/// tip | Tipp
`dependencies` im `APIRouter` können beispielsweise verwendet werden, um eine Authentifizierung für eine ganze Gruppe von *Pfadoperationen* zu erfordern. Selbst wenn die Abhängigkeiten nicht jeder einzeln hinzugefügt werden.
///
/// check | Testen
Die Parameter `prefix`, `tags`, `responses` und `dependencies` sind (wie in vielen anderen Fällen) nur ein Feature von **FastAPI**, um Ihnen dabei zu helfen, Codeverdoppelung zu vermeiden.
///
### Die Abhängigkeiten importieren { #import-the-dependencies }
Der folgende Code befindet sich im Modul `app.routers.items`, also in der Datei `app/routers/items.py`.
Und wir müssen die Abhängigkeitsfunktion aus dem Modul `app.dependencies` importieren, also aus der Datei `app/dependencies.py`.
Daher verwenden wir einen relativen Import mit `..` für die Abhängigkeiten:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Wie relative Importe funktionieren { #how-relative-imports-work }
/// tip | Tipp
Wenn Sie genau wissen, wie Importe funktionieren, fahren Sie mit dem nächsten Abschnitt unten fort.
///
Ein einzelner Punkt `.`, wie in:
```Python
from .dependencies import get_token_header
```
würde bedeuten:
* Beginnend im selben Package, in dem sich dieses Modul (die Datei `app/routers/items.py`) befindet (das Verzeichnis `app/routers/`) ...
* finde das Modul `dependencies` (eine imaginäre Datei unter `app/routers/dependencies.py`) ...
* und importiere daraus die Funktion `get_token_header`.
Aber diese Datei existiert nicht, unsere Abhängigkeiten befinden sich in einer Datei unter `app/dependencies.py`.
Erinnern Sie sich, wie unsere Anwendungs-/Dateistruktur aussieht:
---
Die beiden Punkte `..`, wie in:
```Python
from ..dependencies import get_token_header
```
bedeuten:
* Beginnend im selben Package, in dem sich dieses Modul (die Datei `app/routers/items.py`) befindet (das Verzeichnis `app/routers/`) ...
* gehe zum übergeordneten Package (das Verzeichnis `app/`) ...
* und finde dort das Modul `dependencies` (die Datei unter `app/dependencies.py`) ...
* und importiere daraus die Funktion `get_token_header`.
Das funktioniert korrekt! 🎉
---
Das Gleiche gilt, wenn wir drei Punkte `...` verwendet hätten, wie in:
```Python
from ...dependencies import get_token_header
```
Das würde bedeuten:
* Beginnend im selben Package, in dem sich dieses Modul (die Datei `app/routers/items.py`) befindet (das Verzeichnis `app/routers/`) ...
* gehe zum übergeordneten Package (das Verzeichnis `app/`) ...
* gehe dann zum übergeordneten Package dieses Packages (es gibt kein übergeordnetes Package, `app` ist die oberste Ebene 😱) ...
* und finde dort das Modul `dependencies` (die Datei unter `app/dependencies.py`) ...
* und importiere daraus die Funktion `get_token_header`.
Das würde sich auf ein Paket oberhalb von `app/` beziehen, mit seiner eigenen Datei `__init__.py`, usw. Aber das haben wir nicht. Das würde in unserem Beispiel also einen Fehler auslösen. 🚨
Aber jetzt wissen Sie, wie es funktioniert, sodass Sie relative Importe in Ihren eigenen Anwendungen verwenden können, egal wie komplex diese sind. 🤓
### Einige benutzerdefinierte `tags`, `responses`, und `dependencies` hinzufügen { #add-some-custom-tags-responses-and-dependencies }
Wir fügen weder das Präfix `/items` noch `tags=["items"]` zu jeder *Pfadoperation* hinzu, da wir sie zum `APIRouter` hinzugefügt haben.
Aber wir können immer noch _mehr_ `tags` hinzufügen, die auf eine bestimmte *Pfadoperation* angewendet werden, sowie einige zusätzliche `responses`, die speziell für diese *Pfadoperation* gelten:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Tipp
Diese letzte Pfadoperation wird eine Kombination von Tags haben: `["items", "custom"]`.
Und sie wird auch beide Responses in der Dokumentation haben, eine für `404` und eine für `403`.
///
## Das Haupt-`FastAPI` { #the-main-fastapi }
Sehen wir uns nun das Modul unter `app/main.py` an.
Hier importieren und verwenden Sie die Klasse `FastAPI`.
Dies wird die Hauptdatei Ihrer Anwendung sein, die alles zusammenfügt.
Und da sich der Großteil Ihrer Logik jetzt in seinem eigenen spezifischen Modul befindet, wird die Hauptdatei recht einfach sein.
### `FastAPI` importieren { #import-fastapi }
Sie importieren und erstellen wie gewohnt eine `FastAPI`-Klasse.
Und wir können sogar [globale Abhängigkeiten](dependencies/global-dependencies.md) deklarieren, die mit den Abhängigkeiten für jeden `APIRouter` kombiniert werden:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Den `APIRouter` importieren { #import-the-apirouter }
Jetzt importieren wir die anderen Submodule, die `APIRouter` haben:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Da es sich bei den Dateien `app/routers/users.py` und `app/routers/items.py` um Submodule handelt, die Teil desselben Python-Packages `app` sind, können wir einen einzelnen Punkt `.` verwenden, um sie mit „relativen Imports“ zu importieren.
### Wie das Importieren funktioniert { #how-the-importing-works }
Die Sektion:
```Python
from .routers import items, users
```
bedeutet:
* Beginnend im selben Package, in dem sich dieses Modul (die Datei `app/main.py`) befindet (das Verzeichnis `app/`) ...
* Suche nach dem Subpackage `routers` (das Verzeichnis unter `app/routers/`) ...
* und importiere daraus die Submodule `items` (die Datei unter `app/routers/items.py`) und `users` (die Datei unter `app/routers/users.py`) ...
Das Modul `items` verfügt über eine Variable `router` (`items.router`). Das ist dieselbe, die wir in der Datei `app/routers/items.py` erstellt haben, es ist ein `APIRouter`-Objekt.
Und dann machen wir das gleiche für das Modul `users`.
Wir könnten sie auch wie folgt importieren:
```Python
from app.routers import items, users
```
/// info | Info
Die erste Version ist ein „relativer Import“:
```Python
from .routers import items, users
```
Die zweite Version ist ein „absoluter Import“:
```Python
from app.routers import items, users
```
Um mehr über Python-Packages und -Module zu erfahren, lesen Sie [die offizielle Python-Dokumentation über Module](https://docs.python.org/3/tutorial/modules.html).
///
### Namenskollisionen vermeiden { #avoid-name-collisions }
Wir importieren das Submodul `items` direkt, anstatt nur seine Variable `router` zu importieren.
Das liegt daran, dass wir im Submodul `users` auch eine weitere Variable namens `router` haben.
Wenn wir eine nach der anderen importiert hätten, etwa:
```Python
from .routers.items import router
from .routers.users import router
```
würde der `router` von `users` den von `items` überschreiben und wir könnten sie nicht gleichzeitig verwenden.
Um also beide in derselben Datei verwenden zu können, importieren wir die Submodule direkt:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Die `APIRouter` für `users` und `items` inkludieren { #include-the-apirouters-for-users-and-items }
Inkludieren wir nun die `router` aus diesen Submodulen `users` und `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Info
`users.router` enthält den `APIRouter` in der Datei `app/routers/users.py`.
Und `items.router` enthält den `APIRouter` in der Datei `app/routers/items.py`.
///
Mit `app.include_router()` können wir jeden `APIRouter` zur Hauptanwendung `FastAPI` hinzufügen.
Es wird alle Routen von diesem Router als Teil von dieser inkludieren.
/// note | Technische Details
Tatsächlich wird intern eine *Pfadoperation* für jede *Pfadoperation* erstellt, die im `APIRouter` deklariert wurde.
Hinter den Kulissen wird es also tatsächlich so funktionieren, als ob alles dieselbe einzige Anwendung wäre.
///
/// check | Testen
Bei der Einbindung von Routern müssen Sie sich keine Gedanken über die Leistung machen.
Dies dauert Mikrosekunden und geschieht nur beim Start.
Es hat also keinen Einfluss auf die Leistung. ⚡
///
### Einen `APIRouter` mit benutzerdefinierten `prefix`, `tags`, `responses` und `dependencies` einfügen { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Stellen wir uns nun vor, dass Ihre Organisation Ihnen die Datei `app/internal/admin.py` gegeben hat.
Sie enthält einen `APIRouter` mit einigen administrativen *Pfadoperationen*, die Ihre Organisation zwischen mehreren Projekten teilt.
In diesem Beispiel wird es ganz einfach sein. Nehmen wir jedoch an, dass wir, da sie mit anderen Projekten in der Organisation geteilt wird, sie nicht ändern und kein `prefix`, `dependencies`, `tags`, usw. direkt zum `APIRouter` hinzufügen können:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Aber wir möchten immer noch ein benutzerdefiniertes `prefix` festlegen, wenn wir den `APIRouter` einbinden, sodass alle seine *Pfadoperationen* mit `/admin` beginnen, wir möchten es mit den `dependencies` sichern, die wir bereits für dieses Projekt haben, und wir möchten `tags` und `responses` hinzufügen.
Wir können das alles deklarieren, ohne den ursprünglichen `APIRouter` ändern zu müssen, indem wir diese Parameter an `app.include_router()` übergeben:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Auf diese Weise bleibt der ursprüngliche `APIRouter` unverändert, sodass wir dieselbe `app/internal/admin.py`-Datei weiterhin mit anderen Projekten in der Organisation teilen können.
Das Ergebnis ist, dass in unserer Anwendung jede der *Pfadoperationen* aus dem Modul `admin` Folgendes haben wird:
* Das Präfix `/admin`.
* Den Tag `admin`.
* Die Abhängigkeit `get_token_header`.
* Die Response `418`. 🍵
Dies wirkt sich jedoch nur auf diesen `APIRouter` in unserer Anwendung aus, nicht auf anderen Code, der ihn verwendet.
So könnten beispielsweise andere Projekte denselben `APIRouter` mit einer anderen Authentifizierungsmethode verwenden.
### Eine *Pfadoperation* hinzufügen { #include-a-path-operation }
Wir können *Pfadoperationen* auch direkt zur `FastAPI`-App hinzufügen.
Hier machen wir es ... nur um zu zeigen, dass wir es können 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
und es wird korrekt funktionieren, zusammen mit allen anderen *Pfadoperationen*, die mit `app.include_router()` hinzugefügt wurden.
/// info | Sehr technische Details
**Hinweis**: Dies ist ein sehr technisches Detail, das Sie wahrscheinlich **einfach überspringen** können.
---
Die `APIRouter` sind nicht „gemountet“, sie sind nicht vom Rest der Anwendung isoliert.
Das liegt daran, dass wir deren *Pfadoperationen* in das OpenAPI-Schema und die Benutzeroberflächen einbinden möchten.
Da wir sie nicht einfach isolieren und unabhängig vom Rest „mounten“ können, werden die *Pfadoperationen* „geklont“ (neu erstellt) und nicht direkt einbezogen.
///
## Den `entrypoint` in `pyproject.toml` konfigurieren { #configure-the-entrypoint-in-pyproject-toml }
Da Ihr FastAPI-`app`-Objekt in `app/main.py` liegt, können Sie den `entrypoint` in Ihrer `pyproject.toml`-Datei so konfigurieren:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
das entspricht einem Import wie:
```python
from app.main import app
```
Auf diese Weise weiß der `fastapi`-Befehl, wo er Ihre App findet.
/// Note | Hinweis
Sie könnten auch den Pfad an den Befehl übergeben, etwa:
```console
$ fastapi dev app/main.py
```
Aber dann müssten Sie sich jedes Mal, wenn Sie den `fastapi`-Befehl aufrufen, an den korrekten Pfad erinnern.
Außerdem finden andere Tools Ihre App womöglich nicht, zum Beispiel die [VS Code-Erweiterung](../editor-support.md) oder [FastAPI Cloud](https://fastapicloud.com). Daher wird empfohlen, den `entrypoint` in `pyproject.toml` zu verwenden.
///
## Die automatische API-Dokumentation prüfen { #check-the-automatic-api-docs }
Führen Sie nun Ihre App aus:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Und öffnen Sie die Dokumentation unter [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Sie sehen die automatische API-Dokumentation, einschließlich der Pfade aller Submodule, mit den richtigen Pfaden (und Präfixen) und den richtigen Tags:
## Den gleichen Router mehrmals mit unterschiedlichem `prefix` inkludieren { #include-the-same-router-multiple-times-with-different-prefix }
Sie können `.include_router()` auch mehrmals mit *demselben* Router und unterschiedlichen Präfixen verwenden.
Dies könnte beispielsweise nützlich sein, um dieselbe API unter verschiedenen Präfixen verfügbar zu machen, z. B. `/api/v1` und `/api/latest`.
Dies ist eine fortgeschrittene Verwendung, die Sie möglicherweise nicht wirklich benötigen, aber für den Fall, dass Sie sie benötigen, ist sie vorhanden.
## Einen `APIRouter` in einen anderen einfügen { #include-an-apirouter-in-another }
Auf die gleiche Weise, wie Sie einen `APIRouter` in eine `FastAPI`-Anwendung einbinden können, können Sie einen `APIRouter` in einen anderen `APIRouter` einbinden, indem Sie Folgendes verwenden:
```Python
router.include_router(other_router)
```
Stellen Sie sicher, dass Sie dies tun, bevor Sie `router` in die `FastAPI`-App einbinden, damit auch die *Pfadoperationen* von `other_router` inkludiert werden.
================================================
FILE: docs/de/docs/tutorial/body-fields.md
================================================
# Body – Felder { #body-fields }
So wie Sie zusätzliche Validierung und Metadaten in Parametern der *Pfadoperation-Funktion* mittels `Query`, `Path` und `Body` deklarieren, können Sie auch innerhalb von Pydantic-Modellen zusätzliche Validierung und Metadaten deklarieren, mittels Pydantics `Field`.
## `Field` importieren { #import-field }
Importieren Sie es zuerst:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Achtung
Beachten Sie, dass `Field` direkt von `pydantic` importiert wird, nicht von `fastapi`, wie die anderen (`Query`, `Path`, `Body`, usw.)
///
## Modellattribute deklarieren { #declare-model-attributes }
Dann können Sie `Field` mit Modellattributen deklarieren:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` funktioniert genauso wie `Query`, `Path` und `Body`, es hat die gleichen Parameter, usw.
/// note | Technische Details
Tatsächlich erstellen `Query`, `Path` und andere, die Sie als nächstes sehen werden, Instanzen von Unterklassen einer allgemeinen Klasse `Param`, welche selbst eine Unterklasse von Pydantics `FieldInfo`-Klasse ist.
Und Pydantics `Field` gibt ebenfalls eine Instanz von `FieldInfo` zurück.
`Body` gibt auch direkt Instanzen einer Unterklasse von `FieldInfo` zurück. Später werden Sie andere sehen, die Unterklassen der `Body`-Klasse sind.
Denken Sie daran, dass `Query`, `Path` und andere, wenn Sie sie von `fastapi` importieren, tatsächlich Funktionen sind, die spezielle Klassen zurückgeben.
///
/// tip | Tipp
Beachten Sie, wie jedes Attribut eines Modells mit einem Typ, Defaultwert und `Field` die gleiche Struktur hat wie ein Parameter einer *Pfadoperation-Funktion*, nur mit `Field` statt `Path`, `Query`, `Body`.
///
## Zusätzliche Information hinzufügen { #add-extra-information }
Sie können zusätzliche Information in `Field`, `Query`, `Body`, usw. deklarieren. Und es wird im generierten JSON-Schema untergebracht.
Sie werden später in der Dokumentation mehr darüber lernen, wie man zusätzliche Information unterbringt, wenn Sie lernen, Beispiele zu deklarieren.
/// warning | Achtung
Extra-Schlüssel, die `Field` überreicht werden, werden auch im resultierenden OpenAPI-Schema Ihrer Anwendung gelistet. Da diese Schlüssel möglicherweise nicht Teil der OpenAPI-Spezifikation sind, könnten einige OpenAPI-Tools, wie etwa [der OpenAPI-Validator](https://validator.swagger.io/), nicht mit Ihrem generierten Schema funktionieren.
///
## Zusammenfassung { #recap }
Sie können Pydantics `Field` verwenden, um zusätzliche Validierungen und Metadaten für Modellattribute zu deklarieren.
Sie können auch die zusätzlichen Schlüsselwortargumente verwenden, um zusätzliche JSON-Schema-Metadaten zu übergeben.
================================================
FILE: docs/de/docs/tutorial/body-multiple-params.md
================================================
# Body – Mehrere Parameter { #body-multiple-parameters }
Nun, da wir gesehen haben, wie `Path` und `Query` verwendet werden, schauen wir uns fortgeschrittenere Verwendungsmöglichkeiten von Requestbody-Deklarationen an.
## `Path`-, `Query`- und Body-Parameter vermischen { #mix-path-query-and-body-parameters }
Zuerst einmal, Sie können `Path`-, `Query`- und Requestbody-Parameter-Deklarationen frei mischen und **FastAPI** wird wissen, was zu tun ist.
Und Sie können auch Body-Parameter als optional kennzeichnen, indem Sie den Defaultwert auf `None` setzen:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Hinweis
Beachten Sie, dass in diesem Fall das `item`, welches vom Body genommen wird, optional ist. Da es `None` als Defaultwert hat.
///
## Mehrere Body-Parameter { #multiple-body-parameters }
Im vorherigen Beispiel erwarteten die *Pfadoperationen* einen JSON-Body mit den Attributen eines `Item`s, etwa:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Aber Sie können auch mehrere Body-Parameter deklarieren, z. B. `item` und `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
In diesem Fall wird **FastAPI** bemerken, dass es mehr als einen Body-Parameter in der Funktion gibt (zwei Parameter, die Pydantic-Modelle sind).
Es wird deshalb die Parameternamen als Schlüssel (Feldnamen) im Body verwenden und erwartet einen Body wie folgt:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Hinweis
Beachten Sie, dass, obwohl `item` wie zuvor deklariert wurde, es nun unter einem Schlüssel `item` im Body erwartet wird.
///
**FastAPI** wird die automatische Konvertierung des Requests übernehmen, sodass der Parameter `item` seinen spezifischen Inhalt bekommt, und das Gleiche gilt für den Parameter `user`.
Es wird die Validierung dieser zusammengesetzten Daten übernehmen, und diese im OpenAPI-Schema und der automatischen Dokumentation dokumentieren.
## Einzelne Werte im Body { #singular-values-in-body }
So wie `Query` und `Path` für Query- und Pfad-Parameter, stellt **FastAPI** das Äquivalent `Body` zur Verfügung, um Extra-Daten für Body-Parameter zu definieren.
Zum Beispiel, das vorherige Modell erweiternd, könnten Sie entscheiden, dass Sie einen weiteren Schlüssel `importance` im selben Body haben möchten, neben `item` und `user`.
Wenn Sie diesen Parameter einfach so hinzufügen, wird **FastAPI** annehmen, dass es ein Query-Parameter ist, da er ein einzelner Wert ist.
Aber Sie können **FastAPI** instruieren, ihn als weiteren Body-Schlüssel zu erkennen, indem Sie `Body` verwenden:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
In diesem Fall erwartet **FastAPI** einen Body wie:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Wiederum wird es die Datentypen konvertieren, validieren, dokumentieren, usw.
## Mehrere Body-Parameter und Query-Parameter { #multiple-body-params-and-query }
Natürlich können Sie auch, wann immer Sie das brauchen, weitere Query-Parameter hinzufügen, zusätzlich zu den Body-Parametern.
Da einfache Werte standardmäßig als Query-Parameter interpretiert werden, müssen Sie `Query` nicht explizit hinzufügen, Sie können einfach schreiben:
```Python
q: str | None = None
```
Zum Beispiel:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Info
`Body` hat die gleichen zusätzlichen Validierungs- und Metadaten-Parameter wie `Query`, `Path` und andere, die Sie später kennenlernen werden.
///
## Einen einzelnen Body-Parameter einbetten { #embed-a-single-body-parameter }
Nehmen wir an, Sie haben nur einen einzelnen `item`-Body-Parameter von einem Pydantic-Modell `Item`.
Standardmäßig wird **FastAPI** dann seinen Body direkt erwarten.
Aber wenn Sie möchten, dass es einen JSON-Body mit einem Schlüssel `item` erwartet, und darin den Inhalt des Modells, so wie es das tut, wenn Sie mehrere Body-Parameter deklarieren, dann können Sie den speziellen `Body`-Parameter `embed` setzen:
```Python
item: Item = Body(embed=True)
```
so wie in:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
In diesem Fall erwartet **FastAPI** einen Body wie:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
statt:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Zusammenfassung { #recap }
Sie können mehrere Body-Parameter zu Ihrer *Pfadoperation-Funktion* hinzufügen, obwohl ein Request nur einen einzigen Body enthalten kann.
Aber **FastAPI** wird sich darum kümmern, Ihnen korrekte Daten in Ihrer Funktion zu überreichen, und das korrekte Schema in der *Pfadoperation* zu validieren und zu dokumentieren.
Sie können auch einzelne Werte deklarieren, die als Teil des Bodys empfangen werden.
Und Sie können **FastAPI** instruieren, den Body in einem Schlüssel unterzubringen, selbst wenn nur ein einzelner Body-Parameter deklariert ist.
================================================
FILE: docs/de/docs/tutorial/body-nested-models.md
================================================
# Body – Verschachtelte Modelle { #body-nested-models }
Mit **FastAPI** können Sie (dank Pydantic) beliebig tief verschachtelte Modelle definieren, validieren, dokumentieren und verwenden.
## Listen als Felder { #list-fields }
Sie können ein Attribut als Kindtyp definieren, zum Beispiel eine Python-`list`.
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Das bewirkt, dass `tags` eine Liste ist, wenngleich es nichts über den Typ der Elemente der Liste aussagt.
## Listen mit Typ-Parametern als Felder { #list-fields-with-type-parameter }
Aber Python erlaubt es, Listen mit inneren Typen, auch „Typ-Parameter“ genannt, zu deklarieren.
### Eine `list` mit einem Typ-Parameter deklarieren { #declare-a-list-with-a-type-parameter }
Um Typen zu deklarieren, die Typ-Parameter (innere Typen) haben, wie `list`, `dict`, `tuple`, übergeben Sie den/die inneren Typ(en) als „Typ-Parameter“ in eckigen Klammern: `[` und `]`
```Python
my_list: list[str]
```
Das ist alles Standard-Python-Syntax für Typdeklarationen.
Verwenden Sie dieselbe Standardsyntax für Modellattribute mit inneren Typen.
In unserem Beispiel können wir also bewirken, dass `tags` spezifisch eine „Liste von Strings“ ist:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Set-Typen { #set-types }
Aber dann denken wir darüber nach und stellen fest, dass sich die Tags nicht wiederholen sollen, es sollen eindeutige Strings sein.
Python hat einen Datentyp speziell für Mengen eindeutiger Dinge: das `set`.
Deklarieren wir also `tags` als Set von Strings.
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Jetzt, selbst wenn Sie einen Request mit duplizierten Daten erhalten, werden diese zu einem Set eindeutiger Dinge konvertiert.
Und wann immer Sie diese Daten ausgeben, selbst wenn die Quelle Duplikate hatte, wird es als Set von eindeutigen Dingen ausgegeben.
Und es wird entsprechend annotiert/dokumentiert.
## Verschachtelte Modelle { #nested-models }
Jedes Attribut eines Pydantic-Modells hat einen Typ.
Aber dieser Typ kann selbst ein anderes Pydantic-Modell sein.
Sie können also tief verschachtelte JSON-„Objekte“ deklarieren, mit spezifischen Attributnamen, -typen, und -validierungen.
Alles das beliebig tief verschachtelt.
### Ein Kindmodell definieren { #define-a-submodel }
Für ein Beispiel können wir ein `Image`-Modell definieren.
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Das Kindmodell als Typ verwenden { #use-the-submodel-as-a-type }
Und dann können wir es als Typ eines Attributes verwenden:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Das würde bedeuten, dass **FastAPI** einen Body wie folgt erwartet:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Wiederum, nur mit dieser Deklaration erhalten Sie von **FastAPI**:
* Editor-Unterstützung (Codevervollständigung, usw.), selbst für verschachtelte Modelle
* Datenkonvertierung
* Datenvalidierung
* Automatische Dokumentation
## Spezielle Typen und Validierungen { #special-types-and-validation }
Abgesehen von normalen einfachen Typen wie `str`, `int`, `float`, usw. können Sie komplexere einfache Typen verwenden, die von `str` erben.
Um alle Optionen kennenzulernen, die Sie haben, schauen Sie sich [Pydantics Typübersicht](https://docs.pydantic.dev/latest/concepts/types/) an. Sie werden einige Beispiele im nächsten Kapitel kennenlernen.
Zum Beispiel, da wir im `Image`-Modell ein Feld `url` haben, können wir deklarieren, dass das eine Instanz von Pydantics `HttpUrl` sein soll, anstelle eines `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Es wird getestet, ob der String eine gültige URL ist, und als solche wird er in JSON Schema / OpenAPI dokumentiert.
## Attribute mit Listen von Kindmodellen { #attributes-with-lists-of-submodels }
Sie können Pydantic-Modelle auch als Typen innerhalb von `list`, `set`, usw. verwenden:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Das wird einen JSON-Body erwarten (konvertieren, validieren, dokumentieren, usw.) wie:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Info
Beachten Sie, dass der `images`-Schlüssel jetzt eine Liste von Bild-Objekten hat.
///
## Tief verschachtelte Modelle { #deeply-nested-models }
Sie können beliebig tief verschachtelte Modelle definieren:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Info
Beachten Sie, wie `Offer` eine Liste von `Item`s hat, die ihrerseits eine optionale Liste von `Image`s haben.
///
## Bodys aus reinen Listen { #bodies-of-pure-lists }
Wenn das äußerste Element des JSON-Bodys, das Sie erwarten, ein JSON-`array` (eine Python-`list`) ist, können Sie den Typ im Funktionsparameter deklarieren, mit der gleichen Syntax wie in Pydantic-Modellen:
```Python
images: list[Image]
```
so wie in:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Editor-Unterstützung überall { #editor-support-everywhere }
Und Sie erhalten Editor-Unterstützung überall.
Selbst für Dinge in Listen:
Sie würden diese Editor-Unterstützung nicht erhalten, wenn Sie direkt mit `dict`, statt mit Pydantic-Modellen arbeiten würden.
Aber Sie müssen sich auch nicht weiter um die Modelle kümmern, hereinkommende Dicts werden automatisch in sie konvertiert. Und was Sie zurückgeben, wird automatisch nach JSON konvertiert.
## Bodys mit beliebigen `dict`s { #bodies-of-arbitrary-dicts }
Sie können einen Body auch als `dict` deklarieren, mit Schlüsseln eines Typs und Werten eines anderen Typs.
So brauchen Sie vorher nicht zu wissen, wie die Feld-/Attributnamen lauten (wie es bei Pydantic-Modellen der Fall wäre).
Das ist nützlich, wenn Sie Schlüssel empfangen, deren Namen Sie nicht bereits kennen.
---
Ein anderer nützlicher Anwendungsfall ist, wenn Sie Schlüssel eines anderen Typs haben wollen, z. B. `int`.
Das schauen wir uns mal an.
Im folgenden Beispiel akzeptieren Sie irgendein `dict`, solange es `int`-Schlüssel und `float`-Werte hat:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Tipp
Bedenken Sie, dass JSON nur `str` als Schlüssel unterstützt.
Aber Pydantic hat automatische Datenkonvertierung.
Das bedeutet, dass Ihre API-Clients nur Strings senden können, aber solange diese Strings nur Zahlen enthalten, wird Pydantic sie konvertieren und validieren.
Und das `dict`, welches Sie als `weights` erhalten, wird `int`-Schlüssel und `float`-Werte haben.
///
## Zusammenfassung { #recap }
Mit **FastAPI** haben Sie die maximale Flexibilität von Pydantic-Modellen, während Ihr Code einfach, kurz und elegant bleibt.
Aber mit all den Vorzügen:
* Editor-Unterstützung (Codevervollständigung überall)
* Datenkonvertierung (auch bekannt als Parsen, Serialisierung)
* Datenvalidierung
* Schema-Dokumentation
* Automatische Dokumentation
================================================
FILE: docs/de/docs/tutorial/body-updates.md
================================================
# Body – Aktualisierungen { #body-updates }
## Ersetzendes Aktualisieren mit `PUT` { #update-replacing-with-put }
Um einen Artikel zu aktualisieren, können Sie die [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) Operation verwenden.
Sie können den `jsonable_encoder` verwenden, um die empfangenen Daten in etwas zu konvertieren, das als JSON gespeichert werden kann (z. B. in einer NoSQL-Datenbank). Zum Beispiel, um ein `datetime` in einen `str` zu konvertieren.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` wird verwendet, um Daten zu empfangen, die die existierenden Daten ersetzen sollen.
### Warnung bezüglich des Ersetzens { #warning-about-replacing }
Das bedeutet, dass, wenn Sie den Artikel `bar` aktualisieren wollen, mittels `PUT` und folgendem Body:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
weil das bereits gespeicherte Attribut `"tax": 20.2` nicht enthalten ist, das Eingabemodell den Defaultwert `"tax": 10.5` erhalten würde.
Und die Daten würden mit diesem „neuen“ `tax` von `10.5` gespeichert werden.
## Teil-Aktualisierungen mit `PATCH` { #partial-updates-with-patch }
Sie können auch die [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) Operation verwenden, um Daten *teilweise* zu ersetzen.
Das bedeutet, Sie senden nur die Daten, die Sie aktualisieren wollen, der Rest bleibt unverändert.
/// note | Hinweis
`PATCH` wird seltener verwendet und ist weniger bekannt als `PUT`.
Und viele Teams verwenden ausschließlich `PUT`, selbst für nur Teil-Aktualisierungen.
Es steht Ihnen **frei**, das zu verwenden, was Sie möchten, **FastAPI** legt Ihnen keine Einschränkungen auf.
Aber dieser Leitfaden zeigt Ihnen mehr oder weniger, wie die beiden normalerweise verwendet werden.
///
### Pydantics `exclude_unset`-Parameter verwenden { #using-pydantics-exclude-unset-parameter }
Wenn Sie Teil-Aktualisierungen entgegennehmen, ist der `exclude_unset`-Parameter in der `.model_dump()`-Methode von Pydantic-Modellen sehr nützlich.
Wie in `item.model_dump(exclude_unset=True)`.
Das wird ein `dict` erstellen, mit nur den Daten, die gesetzt wurden, als das `item`-Modell erstellt wurde, Defaultwerte ausgeschlossen.
Sie können das verwenden, um ein `dict` zu erstellen, das nur die (im Request) gesendeten Daten enthält, ohne Defaultwerte:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Pydantics `update`-Parameter verwenden { #using-pydantics-update-parameter }
Jetzt können Sie eine Kopie des existierenden Modells mittels `.model_copy()` erstellen, wobei Sie dem `update`-Parameter ein `dict` mit den zu ändernden Daten übergeben.
Wie in `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Rekapitulation zu Teil-Aktualisierungen { #partial-updates-recap }
Zusammengefasst, um Teil-Aktualisierungen vorzunehmen:
* (Optional) verwenden Sie `PATCH` statt `PUT`.
* Lesen Sie die bereits gespeicherten Daten aus.
* Fügen Sie diese in ein Pydantic-Modell ein.
* Erzeugen Sie aus dem empfangenen Modell ein `dict` ohne Defaultwerte (mittels `exclude_unset`).
* So ersetzen Sie nur die tatsächlich vom Benutzer gesetzten Werte, statt dass bereits gespeicherte Werte mit Defaultwerten des Modells überschrieben werden.
* Erzeugen Sie eine Kopie ihres gespeicherten Modells, wobei Sie die Attribute mit den empfangenen Teil-Ersetzungen aktualisieren (mittels des `update`-Parameters).
* Konvertieren Sie das kopierte Modell zu etwas, das in Ihrer Datenbank gespeichert werden kann (indem Sie beispielsweise `jsonable_encoder` verwenden).
* Das ist vergleichbar dazu, die `.model_dump()`-Methode des Modells erneut aufzurufen, aber es wird sicherstellen, dass die Werte zu Daten konvertiert werden, die ihrerseits zu JSON konvertiert werden können, zum Beispiel `datetime` zu `str`.
* Speichern Sie die Daten in Ihrer Datenbank.
* Geben Sie das aktualisierte Modell zurück.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Tipp
Sie können tatsächlich die gleiche Technik mit einer HTTP `PUT` Operation verwenden.
Aber dieses Beispiel verwendet `PATCH`, da dieses für solche Anwendungsfälle geschaffen wurde.
///
/// note | Hinweis
Beachten Sie, dass das hereinkommende Modell immer noch validiert wird.
Wenn Sie also Teil-Aktualisierungen empfangen wollen, die alle Attribute auslassen können, müssen Sie ein Modell haben, dessen Attribute alle als optional gekennzeichnet sind (mit Defaultwerten oder `None`).
Um zu unterscheiden zwischen Modellen für **Aktualisierungen**, mit lauter optionalen Werten, und solchen für die **Erzeugung**, mit benötigten Werten, können Sie die Techniken verwenden, die in [Extramodelle](extra-models.md) beschrieben wurden.
///
================================================
FILE: docs/de/docs/tutorial/body.md
================================================
# Requestbody { #request-body }
Wenn Sie Daten von einem Client (sagen wir, einem Browser) zu Ihrer API senden müssen, senden Sie sie als **Requestbody**.
Ein **Request**body sind Daten, die vom Client zu Ihrer API gesendet werden. Ein **Response**body sind Daten, die Ihre API zum Client sendet.
Ihre API muss fast immer einen **Response**body senden. Aber Clients müssen nicht unbedingt immer **Requestbodys** senden, manchmal fordern sie nur einen Pfad an, vielleicht mit einigen Query-Parametern, aber senden keinen Body.
Um einen **Request**body zu deklarieren, verwenden Sie [Pydantic](https://docs.pydantic.dev/)-Modelle mit all deren Fähigkeiten und Vorzügen.
/// info | Info
Um Daten zu senden, sollten Sie eines von: `POST` (meistverwendet), `PUT`, `DELETE` oder `PATCH` verwenden.
Das Senden eines Bodys mit einem `GET`-Request hat ein undefiniertes Verhalten in den Spezifikationen, wird aber dennoch von FastAPI unterstützt, nur für sehr komplexe/extreme Anwendungsfälle.
Da davon abgeraten wird, zeigt die interaktive Dokumentation mit Swagger-Benutzeroberfläche die Dokumentation für den Body nicht an, wenn `GET` verwendet wird, und zwischengeschaltete Proxys unterstützen es möglicherweise nicht.
///
## Pydantics `BaseModel` importieren { #import-pydantics-basemodel }
Zuerst müssen Sie `BaseModel` von `pydantic` importieren:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Ihr Datenmodell erstellen { #create-your-data-model }
Dann deklarieren Sie Ihr Datenmodell als eine Klasse, die von `BaseModel` erbt.
Verwenden Sie Standard-Python-Typen für alle Attribute:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Wie auch bei der Deklaration von Query-Parametern gilt: Wenn ein Modellattribut einen Defaultwert hat, ist das Attribut nicht erforderlich. Andernfalls ist es erforderlich. Verwenden Sie `None`, um es einfach optional zu machen.
Zum Beispiel deklariert das obige Modell ein JSON „`object`“ (oder Python-`dict`) wie dieses:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
Da `description` und `tax` optional sind (mit `None` als Defaultwert), wäre folgendes JSON „`object`“ auch gültig:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Als Parameter deklarieren { #declare-it-as-a-parameter }
Um es zu Ihrer *Pfadoperation* hinzuzufügen, deklarieren Sie es auf die gleiche Weise, wie Sie Pfad- und Query-Parameter deklariert haben:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
... und deklarieren Sie dessen Typ als das Modell, welches Sie erstellt haben, `Item`.
## Resultate { #results }
Mit nur dieser Python-Typdeklaration wird **FastAPI**:
* Den Requestbody als JSON lesen.
* Die entsprechenden Typen konvertieren (falls nötig).
* Diese Daten validieren.
* Wenn die Daten ungültig sind, wird ein klar lesbarer Fehler zurückgegeben, der genau anzeigt, wo und was die inkorrekten Daten sind.
* Ihnen die erhaltenen Daten im Parameter `item` übergeben.
* Da Sie ihn in der Funktion als vom Typ `Item` deklariert haben, erhalten Sie auch die volle Unterstützung des Editors (Autovervollständigung, usw.) für alle Attribute und deren Typen.
* [JSON Schema](https://json-schema.org)-Definitionen für Ihr Modell generieren, die Sie auch überall sonst verwenden können, wenn es für Ihr Projekt Sinn macht.
* Diese Schemas werden Teil des generierten OpenAPI-Schemas und werden von den UIs der automatischen Dokumentation genutzt.
## Automatische Dokumentation { #automatic-docs }
Die JSON-Schemas Ihrer Modelle werden Teil Ihres OpenAPI-generierten Schemas und in der interaktiven API-Dokumentation angezeigt:
Und werden auch in der API-Dokumentation innerhalb jeder *Pfadoperation*, die sie benötigt, verwendet:
## Editor-Unterstützung { #editor-support }
In Ihrem Editor erhalten Sie innerhalb Ihrer Funktion Typhinweise und Code-Vervollständigung überall (was nicht der Fall wäre, wenn Sie ein `dict` anstelle eines Pydantic-Modells erhalten hätten):
Sie bekommen auch Fehlermeldungen für inkorrekte Typoperationen:
Das ist nicht zufällig so, das ganze Framework wurde um dieses Design herum aufgebaut.
Und es wurde in der Designphase gründlich getestet, bevor irgendeine Implementierung stattfand, um sicherzustellen, dass es mit allen Editoren funktioniert.
Es gab sogar einige Änderungen an Pydantic selbst, um dies zu unterstützen.
Die vorherigen Screenshots wurden mit [Visual Studio Code](https://code.visualstudio.com) aufgenommen.
Aber Sie würden die gleiche Editor-Unterstützung in [PyCharm](https://www.jetbrains.com/pycharm/) und den meisten anderen Python-Editoren erhalten:
/// tip | Tipp
Wenn Sie [PyCharm](https://www.jetbrains.com/pycharm/) als Ihren Editor verwenden, können Sie das [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/) ausprobieren.
Es verbessert die Editor-Unterstützung für Pydantic-Modelle, mit:
* Code-Vervollständigung
* Typüberprüfungen
* Refaktorisierung
* Suche
* Inspektionen
///
## Das Modell verwenden { #use-the-model }
Innerhalb der Funktion können Sie alle Attribute des Modellobjekts direkt verwenden:
{* ../../docs_src/body/tutorial002_py310.py *}
## Requestbody- + Pfad-Parameter { #request-body-path-parameters }
Sie können Pfad-Parameter und den Requestbody gleichzeitig deklarieren.
**FastAPI** erkennt, dass Funktionsparameter, die mit Pfad-Parametern übereinstimmen, **vom Pfad genommen** werden sollen, und dass Funktionsparameter, welche Pydantic-Modelle sind, **vom Requestbody genommen** werden sollen.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Requestbody- + Pfad- + Query-Parameter { #request-body-path-query-parameters }
Sie können auch zur gleichen Zeit **Body-**, **Pfad-** und **Query-Parameter** deklarieren.
**FastAPI** wird jeden von ihnen korrekt erkennen und die Daten vom richtigen Ort holen.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Die Funktionsparameter werden wie folgt erkannt:
* Wenn der Parameter auch im **Pfad** deklariert wurde, wird er als Pfad-Parameter verwendet.
* Wenn der Parameter ein **einfacher Typ** ist (wie `int`, `float`, `str`, `bool`, usw.), wird er als **Query**-Parameter interpretiert.
* Wenn der Parameter vom Typ eines **Pydantic-Modells** ist, wird er als Request**body** interpretiert.
/// note | Hinweis
FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, aufgrund des definierten Defaultwertes `= None`.
Das `str | None` wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
Das Hinzufügen der Typannotationen ermöglicht jedoch Ihrem Editor, Ihnen eine bessere Unterstützung zu bieten und Fehler zu erkennen.
///
## Ohne Pydantic { #without-pydantic }
Wenn Sie keine Pydantic-Modelle verwenden möchten, können Sie auch **Body**-Parameter verwenden. Siehe die Dokumentation unter [Body – Mehrere Parameter: Einfache Werte im Body](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/de/docs/tutorial/cookie-param-models.md
================================================
# Cookie-Parameter-Modelle { #cookie-parameter-models }
Wenn Sie eine Gruppe von **Cookies** haben, die zusammengehören, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren. 🍪
Damit können Sie das Modell an **mehreren Stellen wiederverwenden** und auch Validierungen und Metadaten für alle Parameter gleichzeitig deklarieren. 😎
/// note | Hinweis
Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
///
/// tip | Tipp
Diese gleiche Technik gilt für `Query`, `Cookie` und `Header`. 😎
///
## Cookies mit einem Pydantic-Modell { #cookies-with-a-pydantic-model }
Deklarieren Sie die **Cookie**-Parameter, die Sie benötigen, in einem **Pydantic-Modell**, und deklarieren Sie dann den Parameter als `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** wird die Daten für **jedes Feld** aus den im Request empfangenen **Cookies** **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell bereitstellen.
## Die Dokumentation testen { #check-the-docs }
Sie können die definierten Cookies in der Dokumentationsoberfläche unter `/docs` sehen:
/// info | Info
Bitte beachten Sie, dass Browser Cookies auf spezielle Weise und im Hintergrund bearbeiten, sodass sie **nicht** leicht **JavaScript** erlauben, diese zu berühren.
Wenn Sie zur **API-Dokumentationsoberfläche** unter `/docs` gehen, können Sie die **Dokumentation** für Cookies für Ihre *Pfadoperationen* sehen.
Aber selbst wenn Sie die **Daten ausfüllen** und auf „Ausführen“ klicken, werden aufgrund der Tatsache, dass die Dokumentationsoberfläche mit **JavaScript** arbeitet, die Cookies nicht gesendet, und Sie werden eine **Fehlermeldung** sehen, als ob Sie keine Werte eingegeben hätten.
///
## Zusätzliche Cookies verbieten { #forbid-extra-cookies }
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die Cookies, die Sie empfangen möchten, **einschränken**.
Ihre API hat jetzt die Macht, ihre eigene Cookie-Einwilligung zu kontrollieren. 🤪🍪
Sie können die Modellkonfiguration von Pydantic verwenden, um `extra` Felder zu verbieten (`forbid`):
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Wenn ein Client versucht, einige **zusätzliche Cookies** zu senden, erhält er eine **Error-Response**.
Arme Cookie-Banner, wie sie sich mühen, Ihre Einwilligung zu erhalten, dass die API sie ablehnen darf. 🍪
Wenn der Client beispielsweise versucht, ein `santa_tracker`-Cookie mit einem Wert von `good-list-please` zu senden, erhält der Client eine **Error-Response**, die ihm mitteilt, dass das `santa_tracker` Cookie nicht erlaubt ist:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Zusammenfassung { #summary }
Sie können **Pydantic-Modelle** verwenden, um **Cookies** in **FastAPI** zu deklarieren. 😎
================================================
FILE: docs/de/docs/tutorial/cookie-params.md
================================================
# Cookie-Parameter { #cookie-parameters }
Sie können Cookie-Parameter auf die gleiche Weise definieren wie `Query`- und `Path`-Parameter.
## `Cookie` importieren { #import-cookie }
Importieren Sie zuerst `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## `Cookie`-Parameter deklarieren { #declare-cookie-parameters }
Deklarieren Sie dann die Cookie-Parameter mit derselben Struktur wie bei `Path` und `Query`.
Sie können den Defaultwert sowie alle zusätzlichen Validierungen oder Annotierungsparameter definieren:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Technische Details
`Cookie` ist eine „Schwester“-Klasse von `Path` und `Query`. Sie erbt auch von derselben gemeinsamen `Param`-Klasse.
Aber denken Sie daran, dass, wenn Sie `Query`, `Path`, `Cookie` und andere von `fastapi` importieren, diese tatsächlich Funktionen sind, die spezielle Klassen zurückgeben.
///
/// info | Info
Um Cookies zu deklarieren, müssen Sie `Cookie` verwenden, da die Parameter sonst als Query-Parameter interpretiert würden.
///
/// info | Info
Beachten Sie, dass **Browser Cookies auf besondere Weise und hinter den Kulissen handhaben** und **JavaScript** **nicht** ohne Weiteres erlauben, auf sie zuzugreifen.
Wenn Sie zur **API-Dokumentations-UI** unter `/docs` gehen, können Sie die **Dokumentation** zu Cookies für Ihre *Pfadoperationen* sehen.
Aber selbst wenn Sie die **Daten ausfüllen** und auf „Execute“ klicken, da die Dokumentations-UI mit **JavaScript** arbeitet, werden die Cookies nicht gesendet, und Sie sehen eine **Fehler**-Meldung, als hätten Sie keine Werte eingegeben.
///
## Zusammenfassung { #recap }
Deklarieren Sie Cookies mit `Cookie` und verwenden Sie dabei das gleiche allgemeine Muster wie bei `Query` und `Path`.
================================================
FILE: docs/de/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS oder „Cross-Origin Resource Sharing“](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) bezieht sich auf Situationen, in denen ein Frontend, das in einem Browser läuft, JavaScript-Code enthält, der mit einem Backend kommuniziert, und das Backend sich in einem anderen „Origin“ als das Frontend befindet.
## Origin { #origin }
Ein Origin ist die Kombination aus Protokoll (`http`, `https`), Domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`) und Port (`80`, `443`, `8080`).
Alle folgenden sind also unterschiedliche Origins:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Auch wenn sie alle in `localhost` sind, verwenden sie unterschiedliche Protokolle oder Ports, daher sind sie unterschiedliche „Origins“.
## Schritte { #steps }
Angenommen, Sie haben ein Frontend, das in Ihrem Browser unter `http://localhost:8080` läuft, und dessen JavaScript versucht, mit einem Backend zu kommunizieren, das unter `http://localhost` läuft (da wir keinen Port angegeben haben, geht der Browser vom Default-Port `80` aus).
Dann wird der Browser ein HTTP-`OPTIONS`-Request an das `:80`-Backend senden, und wenn das Backend die entsprechenden Header sendet, die die Kommunikation von diesem anderen Origin (`http://localhost:8080`) autorisieren, lässt der `:8080`-Browser das JavaScript im Frontend seinen Request an das `:80`-Backend senden.
Um dies zu erreichen, muss das `:80`-Backend eine Liste von „erlaubten Origins“ haben.
In diesem Fall müsste die Liste `http://localhost:8080` enthalten, damit das `:8080`-Frontend korrekt funktioniert.
## Wildcards { #wildcards }
Es ist auch möglich, die Liste als `"*"` (ein „Wildcard“) zu deklarieren, um anzuzeigen, dass alle erlaubt sind.
Aber das erlaubt nur bestimmte Arten der Kommunikation und schließt alles aus, was Anmeldeinformationen beinhaltet: Cookies, Autorisierungsheader wie die, die mit Bearer Tokens verwendet werden, usw.
Um sicherzustellen, dass alles korrekt funktioniert, ist es besser, die erlaubten Origins explizit anzugeben.
## `CORSMiddleware` verwenden { #use-corsmiddleware }
Sie können das in Ihrer **FastAPI**-Anwendung mit der `CORSMiddleware` konfigurieren.
* Importieren Sie `CORSMiddleware`.
* Erstellen Sie eine Liste der erlaubten Origins (als Strings).
* Fügen Sie es als „Middleware“ zu Ihrer **FastAPI**-Anwendung hinzu.
Sie können auch angeben, ob Ihr Backend erlaubt:
* Anmeldeinformationen (Autorisierungsheader, Cookies, usw.).
* Bestimmte HTTP-Methoden (`POST`, `PUT`) oder alle mit der Wildcard `"*"`.
* Bestimmte HTTP-Header oder alle mit der Wildcard `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Die von der `CORSMiddleware`-Implementierung verwendeten Defaultparameter sind standardmäßig restriktiv, daher müssen Sie bestimmte Origins, Methoden oder Header ausdrücklich aktivieren, damit Browser sie in einem Cross-Domain-Kontext verwenden dürfen.
Die folgenden Argumente werden unterstützt:
* `allow_origins` – Eine Liste von Origins, die Cross-Origin-Requests machen dürfen. z. B. `['https://example.org', 'https://www.example.org']`. Sie können `['*']` verwenden, um jedes Origin zuzulassen.
* `allow_origin_regex` – Ein Regex-String zum Abgleichen gegen Origins, die Cross-Origin-Requests machen dürfen. z. B. `'https://.*\.example\.org'`.
* `allow_methods` – Eine Liste von HTTP-Methoden, die für Cross-Origin-Requests erlaubt sein sollen. Standardmäßig `['GET']`. Sie können `['*']` verwenden, um alle Standardmethoden zu erlauben.
* `allow_headers` – Eine Liste von HTTP-Requestheadern, die für Cross-Origin-Requests unterstützt werden sollten. Standardmäßig `[]`. Sie können `['*']` verwenden, um alle Header zu erlauben. Die Header `Accept`, `Accept-Language`, `Content-Language` und `Content-Type` sind immer für [einfache CORS-Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) erlaubt.
* `allow_credentials` – Anzeigen, dass Cookies für Cross-Origin-Requests unterstützt werden sollten. Standardmäßig `False`.
Keines der `allow_origins`, `allow_methods` und `allow_headers` kann auf `['*']` gesetzt werden, wenn `allow_credentials` auf `True` gesetzt ist. Alle müssen [explizit angegeben](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards) werden.
* `expose_headers` – Angabe der Responseheader, auf die der Browser zugreifen können soll. Standardmäßig `[]`.
* `max_age` – Legt eine maximale Zeit in Sekunden fest, die Browser CORS-Responses zwischenspeichern dürfen. Standardmäßig `600`.
Die Middleware antwortet auf zwei besondere Arten von HTTP-Requests ...
### CORS-Preflight-Requests { #cors-preflight-requests }
Dies sind alle `OPTIONS`-Requests mit `Origin`- und `Access-Control-Request-Method`-Headern.
In diesem Fall wird die Middleware den eingehenden Request abfangen und mit entsprechenden CORS-Headern, und entweder einer `200`- oder `400`-Response zu Informationszwecken antworten.
### Einfache Requests { #simple-requests }
Jeder Request mit einem `Origin`-Header. In diesem Fall wird die Middleware den Request wie gewohnt durchlassen, aber entsprechende CORS-Header in die Response aufnehmen.
## Weitere Informationen { #more-info }
Weitere Informationen zu CORS finden Sie in der [Mozilla CORS-Dokumentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Technische Details
Sie könnten auch `from starlette.middleware.cors import CORSMiddleware` verwenden.
**FastAPI** bietet mehrere Middlewares in `fastapi.middleware` nur als Komfort für Sie, den Entwickler. Aber die meisten der verfügbaren Middlewares stammen direkt von Starlette.
///
================================================
FILE: docs/de/docs/tutorial/debugging.md
================================================
# Debugging { #debugging }
Sie können den Debugger in Ihrem Editor verbinden, zum Beispiel mit Visual Studio Code oder PyCharm.
## `uvicorn` aufrufen { #call-uvicorn }
Importieren und führen Sie `uvicorn` direkt in Ihrer FastAPI-Anwendung aus:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Über `__name__ == "__main__"` { #about-name-main }
Der Hauptzweck von `__name__ == "__main__"` ist, dass Code ausgeführt wird, wenn Ihre Datei mit folgendem Befehl aufgerufen wird:
```console
$ python myapp.py
```
aber nicht aufgerufen wird, wenn eine andere Datei sie importiert, wie in:
```Python
from myapp import app
```
#### Weitere Details { #more-details }
Angenommen, Ihre Datei heißt `myapp.py`.
Wenn Sie sie mit folgendem Befehl ausführen:
```console
$ python myapp.py
```
dann hat in Ihrer Datei die interne Variable `__name__`, die von Python automatisch erstellt wird, als Wert den String `"__main__"`.
Daher wird der Abschnitt:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
ausgeführt.
---
Dies wird nicht passieren, wenn Sie das Modul (die Datei) importieren.
Wenn Sie also eine weitere Datei `importer.py` mit folgendem Inhalt haben:
```Python
from myapp import app
# Hier mehr Code
```
wird in diesem Fall in `myapp.py` die automatisch erstellte Variable `__name__` nicht den Wert `"__main__"` haben.
Daher wird die Zeile:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
nicht ausgeführt.
/// info | Info
Für weitere Informationen besuchen Sie bitte [die offizielle Python-Dokumentation](https://docs.python.org/3/library/__main__.html).
///
## Ihren Code mit Ihrem Debugger ausführen { #run-your-code-with-your-debugger }
Da Sie den Uvicorn-Server direkt aus Ihrem Code ausführen, können Sie Ihr Python-Programm (Ihre FastAPI-Anwendung) direkt aus dem Debugger aufrufen.
---
Zum Beispiel können Sie in Visual Studio Code:
* Zum „Debug“-Panel gehen.
* „Konfiguration hinzufügen ...“ auswählen.
* „Python“ auswählen.
* Den Debugger mit der Option „`Python: Current File (Integrated Terminal)`“ ausführen.
Der Server wird dann mit Ihrem **FastAPI**-Code gestartet, an Ihren Haltepunkten angehalten, usw.
So könnte es aussehen:
---
Wenn Sie Pycharm verwenden, können Sie:
* Das Menü „Run“ öffnen.
* Die Option „Debug ...“ auswählen.
* Ein Kontextmenü wird angezeigt.
* Die zu debuggende Datei auswählen (in diesem Fall `main.py`).
Der Server wird dann mit Ihrem **FastAPI**-Code gestartet, an Ihren Haltepunkten angehalten, usw.
So könnte es aussehen:
================================================
FILE: docs/de/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Klassen als Abhängigkeiten { #classes-as-dependencies }
Bevor wir tiefer in das **Dependency Injection** System eintauchen, lassen Sie uns das vorherige Beispiel verbessern.
## Ein `dict` aus dem vorherigen Beispiel { #a-dict-from-the-previous-example }
Im vorherigen Beispiel haben wir ein `dict` von unserer Abhängigkeit („Dependable“) zurückgegeben:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Aber dann haben wir ein `dict` im Parameter `commons` der *Pfadoperation-Funktion*.
Und wir wissen, dass Editoren nicht viel Unterstützung (wie etwa Code-Vervollständigung) für `dict`s bieten können, weil sie ihre Schlüssel- und Werttypen nicht kennen.
Das können wir besser machen ...
## Was macht eine Abhängigkeit aus { #what-makes-a-dependency }
Bisher haben Sie Abhängigkeiten gesehen, die als Funktionen deklariert wurden.
Das ist jedoch nicht die einzige Möglichkeit, Abhängigkeiten zu deklarieren (obwohl es wahrscheinlich die gebräuchlichste ist).
Der springende Punkt ist, dass eine Abhängigkeit aufrufbar („callable“) sein sollte.
Ein „**Callable**“ in Python ist etwas, das wie eine Funktion aufgerufen werden kann („to call“).
Wenn Sie also ein Objekt `something` haben (das möglicherweise _keine_ Funktion ist) und Sie es wie folgt aufrufen (ausführen) können:
```Python
something()
```
oder
```Python
something(some_argument, some_keyword_argument="foo")
```
dann ist das ein „Callable“ (ein „Aufrufbares“).
## Klassen als Abhängigkeiten { #classes-as-dependencies_1 }
Möglicherweise stellen Sie fest, dass Sie zum Erstellen einer Instanz einer Python-Klasse die gleiche Syntax verwenden.
Zum Beispiel:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
In diesem Fall ist `fluffy` eine Instanz der Klasse `Cat`.
Und um `fluffy` zu erzeugen, rufen Sie `Cat` auf.
Eine Python-Klasse ist also auch ein **Callable**.
Darum können Sie in **FastAPI** auch eine Python-Klasse als Abhängigkeit verwenden.
Was FastAPI tatsächlich prüft, ist, ob es sich um ein „Callable“ (Funktion, Klasse oder irgendetwas anderes) handelt und ob die Parameter definiert sind.
Wenn Sie **FastAPI** ein „Callable“ als Abhängigkeit übergeben, analysiert es die Parameter dieses „Callables“ und verarbeitet sie auf die gleiche Weise wie die Parameter einer *Pfadoperation-Funktion*. Einschließlich Unterabhängigkeiten.
Das gilt auch für Callables ohne Parameter. So wie es auch für *Pfadoperation-Funktionen* ohne Parameter gilt.
Dann können wir das „Dependable“ `common_parameters` der Abhängigkeit von oben in die Klasse `CommonQueryParams` ändern:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Achten Sie auf die Methode `__init__`, die zum Erstellen der Instanz der Klasse verwendet wird:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
... sie hat die gleichen Parameter wie unsere vorherige `common_parameters`:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Diese Parameter werden von **FastAPI** verwendet, um die Abhängigkeit „aufzulösen“.
In beiden Fällen wird sie haben:
* Einen optionalen `q`-Query-Parameter, der ein `str` ist.
* Einen `skip`-Query-Parameter, der ein `int` ist, mit einem Defaultwert `0`.
* Einen `limit`-Query-Parameter, der ein `int` ist, mit einem Defaultwert `100`.
In beiden Fällen werden die Daten konvertiert, validiert, im OpenAPI-Schema dokumentiert, usw.
## Verwenden { #use-it }
Jetzt können Sie Ihre Abhängigkeit mithilfe dieser Klasse deklarieren.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** ruft die Klasse `CommonQueryParams` auf. Dadurch wird eine „Instanz“ dieser Klasse erstellt und die Instanz wird als Parameter `commons` an Ihre Funktion überreicht.
## Typannotation vs. `Depends` { #type-annotation-vs-depends }
Beachten Sie, wie wir `CommonQueryParams` im obigen Code zweimal schreiben:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Das letzte `CommonQueryParams`, in:
```Python
... Depends(CommonQueryParams)
```
... ist das, was **FastAPI** tatsächlich verwendet, um die Abhängigkeit zu ermitteln.
Aus diesem extrahiert FastAPI die deklarierten Parameter, und dieses ist es, was FastAPI auch aufruft.
---
In diesem Fall hat das erste `CommonQueryParams` in:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons: CommonQueryParams ...
```
////
... keine besondere Bedeutung für **FastAPI**. FastAPI verwendet es nicht für die Datenkonvertierung, -validierung, usw. (da es dafür `Depends(CommonQueryParams)` verwendet).
Sie könnten tatsächlich einfach schreiben:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons = Depends(CommonQueryParams)
```
////
... wie in:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Es wird jedoch empfohlen, den Typ zu deklarieren, da Ihr Editor so weiß, was als Parameter `commons` übergeben wird, und Ihnen dann bei der Codevervollständigung, Typprüfungen, usw. helfen kann:
## Abkürzung { #shortcut }
Aber Sie sehen, dass wir hier etwas Codeduplizierung haben, indem wir `CommonQueryParams` zweimal schreiben:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** bietet eine Abkürzung für diese Fälle, wo die Abhängigkeit *speziell* eine Klasse ist, welche **FastAPI** aufruft, um eine Instanz der Klasse selbst zu erstellen.
In diesem speziellen Fall können Sie Folgendes tun:
Anstatt zu schreiben:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
... schreiben Sie:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Sie deklarieren die Abhängigkeit als Typ des Parameters und verwenden `Depends()` ohne Parameter, anstatt die vollständige Klasse *erneut* in `Depends(CommonQueryParams)` schreiben zu müssen.
Dasselbe Beispiel würde dann so aussehen:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
... und **FastAPI** wird wissen, was zu tun ist.
/// tip | Tipp
Wenn Sie das eher verwirrt, als Ihnen zu helfen, ignorieren Sie es, Sie *brauchen* es nicht.
Es ist nur eine Abkürzung. Es geht **FastAPI** darum, Ihnen dabei zu helfen, Codeverdoppelung zu minimieren.
///
================================================
FILE: docs/de/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Abhängigkeiten in Pfadoperation-Dekoratoren { #dependencies-in-path-operation-decorators }
Manchmal benötigen Sie den Rückgabewert einer Abhängigkeit innerhalb Ihrer *Pfadoperation-Funktion* nicht wirklich.
Oder die Abhängigkeit gibt keinen Wert zurück.
Aber Sie müssen sie trotzdem ausführen/auflösen.
In diesen Fällen können Sie, anstatt einen Parameter der *Pfadoperation-Funktion* mit `Depends` zu deklarieren, eine `list` von `dependencies` zum *Pfadoperation-Dekorator* hinzufügen.
## `dependencies` zum *Pfadoperation-Dekorator* hinzufügen { #add-dependencies-to-the-path-operation-decorator }
Der *Pfadoperation-Dekorator* erhält ein optionales Argument `dependencies`.
Es sollte eine `list` von `Depends()` sein:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Diese Abhängigkeiten werden auf die gleiche Weise wie normale Abhängigkeiten ausgeführt/aufgelöst. Aber ihr Wert (falls sie einen zurückgeben) wird nicht an Ihre *Pfadoperation-Funktion* übergeben.
/// tip | Tipp
Einige Editoren prüfen, ob Funktionsparameter nicht verwendet werden, und zeigen das als Fehler an.
Wenn Sie `dependencies` im *Pfadoperation-Dekorator* verwenden, stellen Sie sicher, dass sie ausgeführt werden, während gleichzeitig Ihr Editor/Ihre Tools keine Fehlermeldungen ausgeben.
Damit wird auch vermieden, neue Entwickler möglicherweise zu verwirren, die einen nicht verwendeten Parameter in Ihrem Code sehen und ihn für unnötig halten könnten.
///
/// info | Info
In diesem Beispiel verwenden wir zwei erfundene benutzerdefinierte Header `X-Key` und `X-Token`.
Aber in realen Fällen würden Sie bei der Implementierung von Sicherheit mehr Vorteile durch die Verwendung der integrierten [Sicherheits-Werkzeuge (siehe nächstes Kapitel)](../security/index.md) erzielen.
///
## Abhängigkeitsfehler und -Rückgabewerte { #dependencies-errors-and-return-values }
Sie können dieselben Abhängigkeits-*Funktionen* verwenden, die Sie normalerweise verwenden.
### Abhängigkeitsanforderungen { #dependency-requirements }
Sie können Anforderungen für einen Request (wie Header) oder andere Unterabhängigkeiten deklarieren:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Exceptions auslösen { #raise-exceptions }
Die Abhängigkeiten können Exceptions `raise`n, genau wie normale Abhängigkeiten:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Rückgabewerte { #return-values }
Und sie können Werte zurückgeben oder nicht, die Werte werden nicht verwendet.
Sie können also eine normale Abhängigkeit (die einen Wert zurückgibt), die Sie bereits an anderer Stelle verwenden, wiederverwenden, und auch wenn der Wert nicht verwendet wird, wird die Abhängigkeit ausgeführt:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Abhängigkeiten für eine Gruppe von *Pfadoperationen* { #dependencies-for-a-group-of-path-operations }
Wenn Sie später lesen, wie Sie größere Anwendungen strukturieren ([Größere Anwendungen – Mehrere Dateien](../../tutorial/bigger-applications.md)), möglicherweise mit mehreren Dateien, lernen Sie, wie Sie einen einzelnen `dependencies`-Parameter für eine Gruppe von *Pfadoperationen* deklarieren.
## Globale Abhängigkeiten { #global-dependencies }
Als Nächstes werden wir sehen, wie man Abhängigkeiten zur gesamten `FastAPI`-Anwendung hinzufügt, sodass sie für jede *Pfadoperation* gelten.
================================================
FILE: docs/de/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Abhängigkeiten mit `yield` { #dependencies-with-yield }
FastAPI unterstützt Abhängigkeiten, die einige zusätzliche Schritte nach Abschluss ausführen.
Verwenden Sie dazu `yield` statt `return` und schreiben Sie die zusätzlichen Schritte / den zusätzlichen Code danach.
/// tip | Tipp
Stellen Sie sicher, dass Sie `yield` nur einmal pro Abhängigkeit verwenden.
///
/// note | Technische Details
Jede Funktion, die dekoriert werden kann mit:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) oder
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
kann auch als gültige **FastAPI**-Abhängigkeit verwendet werden.
Tatsächlich verwendet FastAPI diese beiden Dekoratoren intern.
///
## Eine Datenbank-Abhängigkeit mit `yield` { #a-database-dependency-with-yield }
Sie könnten damit beispielsweise eine Datenbanksession erstellen und diese nach Abschluss schließen.
Nur der Code vor und einschließlich der `yield`-Anweisung wird ausgeführt, bevor eine Response erzeugt wird:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
Der ge`yield`ete Wert ist das, was in *Pfadoperationen* und andere Abhängigkeiten eingefügt wird:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Der auf die `yield`-Anweisung folgende Code wird nach der Response ausgeführt:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Tipp
Sie können `async`- oder reguläre Funktionen verwenden.
**FastAPI** wird bei jeder das Richtige tun, so wie auch bei normalen Abhängigkeiten.
///
## Eine Abhängigkeit mit `yield` und `try` { #a-dependency-with-yield-and-try }
Wenn Sie einen `try`-Block in einer Abhängigkeit mit `yield` verwenden, empfangen Sie alle Exceptions, die bei Verwendung der Abhängigkeit geworfen wurden.
Wenn beispielsweise ein Code irgendwann in der Mitte, in einer anderen Abhängigkeit oder in einer *Pfadoperation*, ein „Rollback“ einer Datenbanktransaktion macht oder eine andere Exception verursacht, empfangen Sie die Exception in Ihrer Abhängigkeit.
Sie können also mit `except SomeException` diese bestimmte Exception innerhalb der Abhängigkeit handhaben.
Auf die gleiche Weise können Sie `finally` verwenden, um sicherzustellen, dass die Exit-Schritte ausgeführt werden, unabhängig davon, ob eine Exception geworfen wurde oder nicht.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Unterabhängigkeiten mit `yield` { #sub-dependencies-with-yield }
Sie können Unterabhängigkeiten und „Bäume“ von Unterabhängigkeiten beliebiger Größe und Form haben, und einige oder alle davon können `yield` verwenden.
**FastAPI** stellt sicher, dass der „Exit-Code“ in jeder Abhängigkeit mit `yield` in der richtigen Reihenfolge ausgeführt wird.
Beispielsweise kann `dependency_c` von `dependency_b` und `dependency_b` von `dependency_a` abhängen:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
Und alle können `yield` verwenden.
In diesem Fall benötigt `dependency_c` zum Ausführen seines Exit-Codes, dass der Wert von `dependency_b` (hier `dep_b` genannt) verfügbar ist.
Und wiederum benötigt `dependency_b` den Wert von `dependency_a` (hier `dep_a` genannt) für seinen Exit-Code.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Auf die gleiche Weise könnten Sie einige Abhängigkeiten mit `yield` und einige andere Abhängigkeiten mit `return` haben, und alle können beliebig voneinander abhängen.
Und Sie könnten eine einzelne Abhängigkeit haben, die auf mehreren ge`yield`eten Abhängigkeiten basiert, usw.
Sie können beliebige Kombinationen von Abhängigkeiten haben.
**FastAPI** stellt sicher, dass alles in der richtigen Reihenfolge ausgeführt wird.
/// note | Technische Details
Dieses funktioniert dank Pythons [Kontextmanager](https://docs.python.org/3/library/contextlib.html).
**FastAPI** verwendet sie intern, um das zu erreichen.
///
## Abhängigkeiten mit `yield` und `HTTPException` { #dependencies-with-yield-and-httpexception }
Sie haben gesehen, dass Sie Abhängigkeiten mit `yield` verwenden und `try`-Blöcke haben können, die versuchen, irgendeinen Code auszuführen und dann, nach `finally`, Exit-Code ausführen.
Sie können auch `except` verwenden, um die geworfene Exception abzufangen und damit etwas zu tun.
Zum Beispiel können Sie eine andere Exception auslösen, wie `HTTPException`.
/// tip | Tipp
Dies ist eine etwas fortgeschrittene Technik, die Sie in den meisten Fällen nicht wirklich benötigen, da Sie Exceptions (einschließlich `HTTPException`) innerhalb des restlichen Anwendungscodes auslösen können, beispielsweise in der *Pfadoperation-Funktion*.
Aber es ist für Sie da, wenn Sie es brauchen. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Wenn Sie Exceptions abfangen und darauf basierend eine benutzerdefinierte Response erstellen möchten, erstellen Sie einen [benutzerdefinierten Exceptionhandler](../handling-errors.md#install-custom-exception-handlers).
## Abhängigkeiten mit `yield` und `except` { #dependencies-with-yield-and-except }
Wenn Sie eine Exception mit `except` in einer Abhängigkeit mit `yield` abfangen und sie nicht erneut auslösen (oder eine neue Exception auslösen), kann FastAPI nicht feststellen, dass es eine Exception gab, genau so wie es bei normalem Python der Fall wäre:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
In diesem Fall sieht der Client eine *HTTP 500 Internal Server Error*-Response, wie es sein sollte, da wir keine `HTTPException` oder Ähnliches auslösen, aber der Server hat **keine Logs** oder einen anderen Hinweis darauf, was der Fehler war. 😱
### In Abhängigkeiten mit `yield` und `except` immer `raise` verwenden { #always-raise-in-dependencies-with-yield-and-except }
Wenn Sie eine Exception in einer Abhängigkeit mit `yield` abfangen, sollten Sie – sofern Sie nicht eine andere `HTTPException` oder Ähnliches auslösen – **die ursprüngliche Exception erneut auslösen**.
Sie können dieselbe Exception mit `raise` erneut auslösen:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Jetzt erhält der Client dieselbe *HTTP 500 Internal Server Error*-Response, aber der Server enthält unseren benutzerdefinierten `InternalError` in den Logs. 😎
## Ausführung von Abhängigkeiten mit `yield` { #execution-of-dependencies-with-yield }
Die Ausführungsreihenfolge ähnelt mehr oder weniger dem folgenden Diagramm. Die Zeit verläuft von oben nach unten. Und jede Spalte ist einer der interagierenden oder Code-ausführenden Teilnehmer.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exceptionhandler
participant dep as Abhängigkeit mit yield
participant operation as Pfadoperation
participant tasks as Hintergrundtasks
Note over client,operation: Kann Exceptions auslösen, inklusive HTTPException
client ->> dep: Startet den Request
Note over dep: Führt den Code bis zum yield aus
opt Löst Exception aus
dep -->> handler: Löst Exception aus
handler -->> client: HTTP-Error-Response
end
dep ->> operation: Führt Abhängigkeit aus, z. B. DB-Session
opt Löst aus
operation -->> dep: Löst Exception aus (z. B. HTTPException)
opt Handhabt
dep -->> dep: Kann Exception abfangen, eine neue HTTPException auslösen, andere Exception auslösen
end
handler -->> client: HTTP-Error-Response
end
operation ->> client: Sendet Response an Client
Note over client,operation: Response wurde bereits gesendet, kann nicht mehr geändert werden
opt Tasks
operation -->> tasks: Sendet Hintergrundtasks
end
opt Löst andere Exception aus
tasks -->> tasks: Handhabt Exceptions im Hintergrundtask-Code
end
```
/// info | Info
Es wird nur **eine Response** an den Client gesendet. Es kann eine Error-Response oder die Response der *Pfadoperation* sein.
Nachdem eine dieser Responses gesendet wurde, kann keine weitere Response gesendet werden.
///
/// tip | Tipp
Wenn Sie in dem Code der *Pfadoperation-Funktion* irgendeine Exception auslösen, wird sie an die Abhängigkeiten mit `yield` weitergegeben, einschließlich `HTTPException`. In den meisten Fällen sollten Sie dieselbe Exception oder eine neue aus der Abhängigkeit mit `yield` erneut auslösen, um sicherzustellen, dass sie korrekt gehandhabt wird.
///
## Frühes Beenden und `scope` { #early-exit-and-scope }
Normalerweise wird der Exit-Code von Abhängigkeiten mit `yield` ausgeführt **nachdem die Response** an den Client gesendet wurde.
Wenn Sie aber wissen, dass Sie die Abhängigkeit nach der Rückkehr aus der *Pfadoperation-Funktion* nicht mehr benötigen, können Sie `Depends(scope="function")` verwenden, um FastAPI mitzuteilen, dass es die Abhängigkeit nach der Rückkehr aus der *Pfadoperation-Funktion* schließen soll, jedoch **bevor** die **Response gesendet wird**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` erhält einen `scope`-Parameter, der sein kann:
* `"function"`: startet die Abhängigkeit vor der *Pfadoperation-Funktion*, die den Request bearbeitet, beendet die Abhängigkeit nach dem Ende der *Pfadoperation-Funktion*, aber **bevor** die Response an den Client zurückgesendet wird. Die Abhängigkeitsfunktion wird also **um** die *Pfadoperation-**Funktion*** **herum** ausgeführt.
* `"request"`: startet die Abhängigkeit vor der *Pfadoperation-Funktion*, die den Request bearbeitet (ähnlich wie bei `"function"`), beendet sie jedoch **nachdem** die Response an den Client zurückgesendet wurde. Die Abhängigkeitsfunktion wird also **um** den **Request**- und Response-Zyklus **herum** ausgeführt.
Wenn nicht angegeben und die Abhängigkeit `yield` hat, hat sie standardmäßig einen `scope` von `"request"`.
### `scope` für Unterabhängigkeiten { #scope-for-sub-dependencies }
Wenn Sie eine Abhängigkeit mit `scope="request"` (dem Default) deklarieren, muss jede Unterabhängigkeit ebenfalls einen `scope` von `"request"` haben.
Eine Abhängigkeit mit `scope` von `"function"` kann jedoch Abhängigkeiten mit `scope` von `"function"` und `scope` von `"request"` haben.
Das liegt daran, dass jede Abhängigkeit in der Lage sein muss, ihren Exit-Code vor den Unterabhängigkeiten auszuführen, da sie diese während ihres Exit-Codes möglicherweise noch verwenden muss.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Abhängigkeit scope="request"
participant dep_func as Abhängigkeit scope="function"
participant operation as Pfadoperation
client ->> dep_req: Startet den Request
Note over dep_req: Führt den Code bis zum yield aus
dep_req ->> dep_func: Reicht Abhängigkeit weiter
Note over dep_func: Führt den Code bis zum yield aus
dep_func ->> operation: Führt Pfadoperation mit Abhängigkeit aus
operation ->> dep_func: Kehrt aus Pfadoperation zurück
Note over dep_func: Führt Code nach yield aus
Note over dep_func: ✅ Abhängigkeit geschlossen
dep_func ->> client: Sendet Response an Client
Note over client: Response gesendet
Note over dep_req: Führt Code nach yield aus
Note over dep_req: ✅ Abhängigkeit geschlossen
```
## Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
Abhängigkeiten mit `yield` haben sich im Laufe der Zeit weiterentwickelt, um verschiedene Anwendungsfälle abzudecken und einige Probleme zu beheben.
Wenn Sie sehen möchten, was sich in verschiedenen Versionen von FastAPI geändert hat, lesen Sie mehr dazu im fortgeschrittenen Teil, unter [Fortgeschrittene Abhängigkeiten – Abhängigkeiten mit `yield`, `HTTPException`, `except` und Hintergrundtasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Kontextmanager { #context-managers }
### Was sind „Kontextmanager“ { #what-are-context-managers }
„Kontextmanager“ (Englisch „Context Manager“) sind bestimmte Python-Objekte, die Sie in einer `with`-Anweisung verwenden können.
Beispielsweise können Sie [„with“ verwenden, um eine Datei auszulesen](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Im Hintergrund erstellt das `open("./somefile.txt")` ein Objekt, das als „Kontextmanager“ bezeichnet wird.
Dieser stellt sicher, dass, wenn der `with`-Block beendet ist, die Datei geschlossen wird, auch wenn Exceptions geworfen wurden.
Wenn Sie eine Abhängigkeit mit `yield` erstellen, erstellt **FastAPI** dafür intern einen Kontextmanager und kombiniert ihn mit einigen anderen zugehörigen Tools.
### Kontextmanager in Abhängigkeiten mit `yield` verwenden { #using-context-managers-in-dependencies-with-yield }
/// warning | Achtung
Dies ist mehr oder weniger eine „fortgeschrittene“ Idee.
Wenn Sie gerade erst mit **FastAPI** beginnen, möchten Sie das vielleicht vorerst überspringen.
///
In Python können Sie Kontextmanager erstellen, indem Sie [eine Klasse mit zwei Methoden erzeugen: `__enter__()` und `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
Sie können solche auch innerhalb von **FastAPI**-Abhängigkeiten mit `yield` verwenden, indem Sie `with`- oder `async with`-Anweisungen innerhalb der Abhängigkeits-Funktion verwenden:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Tipp
Andere Möglichkeiten, einen Kontextmanager zu erstellen, sind:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) oder
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
Verwenden Sie diese, um eine Funktion zu dekorieren, die ein einziges `yield` hat.
Das ist es auch, was **FastAPI** intern für Abhängigkeiten mit `yield` verwendet.
Aber Sie müssen die Dekoratoren nicht für FastAPI-Abhängigkeiten verwenden (und das sollten Sie auch nicht).
FastAPI erledigt das intern für Sie.
///
================================================
FILE: docs/de/docs/tutorial/dependencies/global-dependencies.md
================================================
# Globale Abhängigkeiten { #global-dependencies }
Bei einigen Anwendungstypen möchten Sie möglicherweise Abhängigkeiten zur gesamten Anwendung hinzufügen.
Ähnlich wie Sie [`dependencies` zu den *Pfadoperation-Dekoratoren* hinzufügen](dependencies-in-path-operation-decorators.md) können, können Sie sie auch zur `FastAPI`-Anwendung hinzufügen.
In diesem Fall werden sie auf alle *Pfadoperationen* in der Anwendung angewendet:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Und alle Ideen aus dem Abschnitt über das [Hinzufügen von `dependencies` zu den *Pfadoperation-Dekoratoren*](dependencies-in-path-operation-decorators.md) gelten weiterhin, aber in diesem Fall für alle *Pfadoperationen* in der App.
## Abhängigkeiten für Gruppen von *Pfadoperationen* { #dependencies-for-groups-of-path-operations }
Wenn Sie später lesen, wie Sie größere Anwendungen strukturieren ([Größere Anwendungen – mehrere Dateien](../../tutorial/bigger-applications.md)), möglicherweise mit mehreren Dateien, lernen Sie, wie Sie einen einzelnen `dependencies`-Parameter für eine Gruppe von *Pfadoperationen* deklarieren.
================================================
FILE: docs/de/docs/tutorial/dependencies/index.md
================================================
# Abhängigkeiten { #dependencies }
**FastAPI** hat ein sehr mächtiges, aber intuitives **Abhängigkeitsinjektion** System.
Es ist so konzipiert, sehr einfach zu verwenden zu sein und es jedem Entwickler sehr leicht zu machen, andere Komponenten mit **FastAPI** zu integrieren.
## Was ist „Dependency Injection“ { #what-is-dependency-injection }
**„Dependency Injection“** bedeutet in der Programmierung, dass es für Ihren Code (in diesem Fall Ihre *Pfadoperation-Funktionen*) eine Möglichkeit gibt, Dinge zu deklarieren, die er verwenden möchte und die er zum Funktionieren benötigt: „Abhängigkeiten“ – „Dependencies“.
Das System (in diesem Fall **FastAPI**) kümmert sich dann darum, Ihren Code mit den erforderlichen Abhängigkeiten zu versorgen („die Abhängigkeiten einfügen“ – „inject the dependencies“).
Das ist sehr nützlich, wenn Sie:
* Eine gemeinsame Logik haben (die gleiche Code-Logik immer und immer wieder).
* Datenbankverbindungen teilen.
* Sicherheit, Authentifizierung, Rollenanforderungen, usw. durchsetzen.
* Und viele andere Dinge ...
All dies, während Sie Codeverdoppelung minimieren.
## Erste Schritte { #first-steps }
Sehen wir uns ein sehr einfaches Beispiel an. Es ist so einfach, dass es vorerst nicht sehr nützlich ist.
Aber so können wir uns besser auf die Funktionsweise des **Dependency Injection** Systems konzentrieren.
### Eine Abhängigkeit erstellen, oder „Dependable“ { #create-a-dependency-or-dependable }
Konzentrieren wir uns zunächst auf die Abhängigkeit – die Dependency.
Es handelt sich einfach um eine Funktion, die die gleichen Parameter entgegennimmt wie eine *Pfadoperation-Funktion*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Das war's schon.
**Zwei Zeilen**.
Und sie hat die gleiche Form und Struktur wie alle Ihre *Pfadoperation-Funktionen*.
Sie können sie sich als *Pfadoperation-Funktion* ohne den „Dekorator“ (ohne `@app.get("/some-path")`) vorstellen.
Und sie kann alles zurückgeben, was Sie möchten.
In diesem Fall erwartet diese Abhängigkeit:
* Einen optionalen Query-Parameter `q`, der ein `str` ist.
* Einen optionalen Query-Parameter `skip`, der ein `int` ist und standardmäßig `0` ist.
* Einen optionalen Query-Parameter `limit`, der ein `int` ist und standardmäßig `100` ist.
Und dann wird einfach ein `dict` zurückgegeben, welches diese Werte enthält.
/// info | Info
FastAPI unterstützt (und empfiehlt die Verwendung von) `Annotated` seit Version 0.95.0.
Wenn Sie eine ältere Version haben, werden Sie Fehler angezeigt bekommen, wenn Sie versuchen, `Annotated` zu verwenden.
Bitte [aktualisieren Sie FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) daher mindestens zu Version 0.95.1, bevor Sie `Annotated` verwenden.
///
### `Depends` importieren { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Die Abhängigkeit im „Dependant“ deklarieren { #declare-the-dependency-in-the-dependant }
So wie auch `Body`, `Query`, usw., verwenden Sie `Depends` mit den Parametern Ihrer *Pfadoperation-Funktion*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Obwohl Sie `Depends` in den Parametern Ihrer Funktion genauso verwenden wie `Body`, `Query`, usw., funktioniert `Depends` etwas anders.
Sie übergeben `Depends` nur einen einzigen Parameter.
Dieser Parameter muss so etwas wie eine Funktion sein.
Sie **rufen diese nicht direkt auf** (fügen Sie am Ende keine Klammern hinzu), sondern übergeben sie einfach als Parameter an `Depends()`.
Und diese Funktion akzeptiert Parameter auf die gleiche Weise wie *Pfadoperation-Funktionen*.
/// tip | Tipp
Im nächsten Kapitel erfahren Sie, welche anderen „Dinge“, außer Funktionen, Sie als Abhängigkeiten verwenden können.
///
Immer wenn ein neuer Request eintrifft, kümmert sich **FastAPI** darum:
* Ihre Abhängigkeitsfunktion („Dependable“) mit den richtigen Parametern aufzurufen.
* Sich das Ergebnis von dieser Funktion zu holen.
* Dieses Ergebnis dem Parameter Ihrer *Pfadoperation-Funktion* zuzuweisen.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Auf diese Weise schreiben Sie gemeinsam genutzten Code nur einmal, und **FastAPI** kümmert sich darum, ihn für Ihre *Pfadoperationen* aufzurufen.
/// check | Testen
Beachten Sie, dass Sie keine spezielle Klasse erstellen und diese irgendwo an **FastAPI** übergeben müssen, um sie zu „registrieren“ oder so ähnlich.
Sie übergeben es einfach an `Depends` und **FastAPI** weiß, wie der Rest erledigt wird.
///
## `Annotated`-Abhängigkeiten wiederverwenden { #share-annotated-dependencies }
In den Beispielen oben sehen Sie, dass es ein kleines bisschen **Codeverdoppelung** gibt.
Wenn Sie die Abhängigkeit `common_parameters()` verwenden, müssen Sie den gesamten Parameter mit der Typannotation und `Depends()` schreiben:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Da wir jedoch `Annotated` verwenden, können wir diesen `Annotated`-Wert in einer Variablen speichern und an mehreren Stellen verwenden:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Tipp
Das ist schlicht Standard-Python, es wird als „Typalias“ bezeichnet und ist eigentlich nicht **FastAPI**-spezifisch.
Da **FastAPI** jedoch auf Standard-Python, einschließlich `Annotated`, basiert, können Sie diesen Trick in Ihrem Code verwenden. 😎
///
Die Abhängigkeiten funktionieren weiterhin wie erwartet, und das **Beste daran** ist, dass die **Typinformationen erhalten bleiben**, was bedeutet, dass Ihr Editor Ihnen weiterhin **automatische Vervollständigung**, **Inline-Fehler**, usw. bieten kann. Das Gleiche gilt für andere Tools wie `mypy`.
Das ist besonders nützlich, wenn Sie es in einer **großen Codebasis** verwenden, in der Sie in **vielen *Pfadoperationen*** immer wieder **dieselben Abhängigkeiten** verwenden.
## `async` oder nicht `async` { #to-async-or-not-to-async }
Da Abhängigkeiten auch von **FastAPI** aufgerufen werden (so wie Ihre *Pfadoperation-Funktionen*), gelten beim Definieren Ihrer Funktionen die gleichen Regeln.
Sie können `async def` oder einfach `def` verwenden.
Und Sie können Abhängigkeiten mit `async def` innerhalb normaler `def`-*Pfadoperation-Funktionen* oder `def`-Abhängigkeiten innerhalb von `async def`-*Pfadoperation-Funktionen*, usw. deklarieren.
Es spielt keine Rolle. **FastAPI** weiß, was zu tun ist.
/// note | Hinweis
Wenn Ihnen das nichts sagt, lesen Sie den [Async: *„In Eile?“*](../../async.md#in-a-hurry)-Abschnitt über `async` und `await` in der Dokumentation.
///
## Integriert in OpenAPI { #integrated-with-openapi }
Alle Requestdeklarationen, -validierungen und -anforderungen Ihrer Abhängigkeiten (und Unterabhängigkeiten) werden in dasselbe OpenAPI-Schema integriert.
Die interaktive Dokumentation enthält also auch alle Informationen aus diesen Abhängigkeiten:
## Einfache Verwendung { #simple-usage }
Näher betrachtet, werden *Pfadoperation-Funktionen* deklariert, um verwendet zu werden, wann immer ein *Pfad* und eine *Operation* übereinstimmen, und dann kümmert sich **FastAPI** darum, die Funktion mit den richtigen Parametern aufzurufen, die Daten aus dem Request extrahierend.
Tatsächlich funktionieren alle (oder die meisten) Webframeworks auf die gleiche Weise.
Sie rufen diese Funktionen niemals direkt auf. Sie werden von Ihrem Framework aufgerufen (in diesem Fall **FastAPI**).
Mit dem Dependency Injection System können Sie **FastAPI** ebenfalls mitteilen, dass Ihre *Pfadoperation-Funktion* von etwas anderem „abhängt“, das vor Ihrer *Pfadoperation-Funktion* ausgeführt werden soll, und **FastAPI** kümmert sich darum, es auszuführen und die Ergebnisse zu „injizieren“.
Andere gebräuchliche Begriffe für dieselbe Idee der „Abhängigkeitsinjektion“ sind:
* Ressourcen
* Provider
* Services
* Injectables
* Komponenten
## **FastAPI**-Plugins { #fastapi-plug-ins }
Integrationen und „Plugins“ können mit dem **Dependency Injection** System erstellt werden. Aber tatsächlich besteht **keine Notwendigkeit, „Plugins“ zu erstellen**, da es durch die Verwendung von Abhängigkeiten möglich ist, eine unendliche Anzahl von Integrationen und Interaktionen zu deklarieren, die dann für Ihre *Pfadoperation-Funktionen* verfügbar sind.
Und Abhängigkeiten können auf sehr einfache und intuitive Weise erstellt werden, sodass Sie einfach die benötigten Python-Packages importieren und sie in wenigen Codezeilen, *im wahrsten Sinne des Wortes*, mit Ihren API-Funktionen integrieren.
Beispiele hierfür finden Sie in den nächsten Kapiteln zu relationalen und NoSQL-Datenbanken, Sicherheit usw.
## **FastAPI**-Kompatibilität { #fastapi-compatibility }
Die Einfachheit des Dependency Injection Systems macht **FastAPI** kompatibel mit:
* allen relationalen Datenbanken
* NoSQL-Datenbanken
* externen Packages
* externen APIs
* Authentifizierungs- und Autorisierungssystemen
* API-Nutzungs-Überwachungssystemen
* Responsedaten-Injektionssystemen
* usw.
## Einfach und leistungsstark { #simple-and-powerful }
Obwohl das hierarchische Dependency Injection System sehr einfach zu definieren und zu verwenden ist, ist es dennoch sehr mächtig.
Sie können Abhängigkeiten definieren, die selbst wiederum Abhängigkeiten definieren können.
Am Ende wird ein hierarchischer Baum von Abhängigkeiten erstellt, und das **Dependency Injection** System kümmert sich darum, alle diese Abhängigkeiten (und deren Unterabhängigkeiten) für Sie aufzulösen und die Ergebnisse bei jedem Schritt einzubinden (zu injizieren).
Nehmen wir zum Beispiel an, Sie haben vier API-Endpunkte (*Pfadoperationen*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
Dann könnten Sie für jeden davon unterschiedliche Berechtigungsanforderungen hinzufügen, nur mit Abhängigkeiten und Unterabhängigkeiten:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integriert mit **OpenAPI** { #integrated-with-openapi_1 }
Alle diese Abhängigkeiten, während sie ihre Anforderungen deklarieren, fügen auch Parameter, Validierungen, usw. zu Ihren *Pfadoperationen* hinzu.
**FastAPI** kümmert sich darum, alles zum OpenAPI-Schema hinzuzufügen, damit es in den interaktiven Dokumentationssystemen angezeigt wird.
================================================
FILE: docs/de/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Unterabhängigkeiten { #sub-dependencies }
Sie können Abhängigkeiten erstellen, die **Unterabhängigkeiten** haben.
Diese können so **tief** verschachtelt sein, wie nötig.
**FastAPI** kümmert sich darum, sie aufzulösen.
## Erste Abhängigkeit, „Dependable“ { #first-dependency-dependable }
Sie könnten eine erste Abhängigkeit („Dependable“) wie folgt erstellen:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Diese deklariert einen optionalen Query-Parameter `q` vom Typ `str` und gibt ihn dann einfach zurück.
Das ist recht einfach (nicht sehr nützlich), hilft uns aber dabei, uns auf die Funktionsweise der Unterabhängigkeiten zu konzentrieren.
## Zweite Abhängigkeit, „Dependable“ und „Dependant“ { #second-dependency-dependable-and-dependant }
Dann können Sie eine weitere Abhängigkeitsfunktion (ein „Dependable“) erstellen, die gleichzeitig eine eigene Abhängigkeit deklariert (also auch ein „Dependant“ ist):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Betrachten wir die deklarierten Parameter:
* Obwohl diese Funktion selbst eine Abhängigkeit ist („Dependable“, etwas hängt von ihr ab), deklariert sie auch eine andere Abhängigkeit („Dependant“, sie hängt von etwas anderem ab).
* Sie hängt von `query_extractor` ab und weist den von diesem zurückgegebenen Wert dem Parameter `q` zu.
* Sie deklariert außerdem ein optionales `last_query`-Cookie, ein `str`.
* Wenn der Benutzer keine Query `q` übermittelt hat, verwenden wir die zuletzt übermittelte Query, die wir zuvor in einem Cookie gespeichert haben.
## Die Abhängigkeit verwenden { #use-the-dependency }
Diese Abhängigkeit verwenden wir nun wie folgt:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Info
Beachten Sie, dass wir in der *Pfadoperation-Funktion* nur eine einzige Abhängigkeit deklarieren, den `query_or_cookie_extractor`.
Aber **FastAPI** wird wissen, dass es zuerst `query_extractor` auflösen muss, um dessen Resultat an `query_or_cookie_extractor` zu übergeben, wenn dieses aufgerufen wird.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Dieselbe Abhängigkeit mehrmals verwenden { #using-the-same-dependency-multiple-times }
Wenn eine Ihrer Abhängigkeiten mehrmals für dieselbe *Pfadoperation* deklariert wird, beispielsweise wenn mehrere Abhängigkeiten eine gemeinsame Unterabhängigkeit haben, wird **FastAPI** diese Unterabhängigkeit nur einmal pro Request aufrufen.
Und es speichert den zurückgegebenen Wert in einem „Cache“ und übergibt diesen gecachten Wert an alle „Dependanten“, die ihn in diesem spezifischen Request benötigen, anstatt die Abhängigkeit mehrmals für denselben Request aufzurufen.
In einem fortgeschrittenen Szenario, bei dem Sie wissen, dass die Abhängigkeit bei jedem Schritt (möglicherweise mehrmals) in demselben Request aufgerufen werden muss, anstatt den zwischengespeicherten Wert zu verwenden, können Sie den Parameter `use_cache=False` festlegen, wenn Sie `Depends` verwenden:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ nicht annotiert
/// tip | Tipp
Bevorzugen Sie die `Annotated`-Version, falls möglich.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Zusammenfassung { #recap }
Abgesehen von all den ausgefallenen Wörtern, die hier verwendet werden, ist das **Dependency Injection**-System recht simpel.
Einfach Funktionen, die genauso aussehen wie *Pfadoperation-Funktionen*.
Dennoch ist es sehr mächtig und ermöglicht Ihnen die Deklaration beliebig tief verschachtelter Abhängigkeits-„Graphen“ (Bäume).
/// tip | Tipp
All dies scheint angesichts dieser einfachen Beispiele möglicherweise nicht so nützlich zu sein.
Aber Sie werden in den Kapiteln über **Sicherheit** sehen, wie nützlich das ist.
Und Sie werden auch sehen, wie viel Code Sie dadurch einsparen.
///
================================================
FILE: docs/de/docs/tutorial/encoder.md
================================================
# JSON-kompatibler Encoder { #json-compatible-encoder }
Es gibt Fälle, da möchten Sie einen Datentyp (etwa ein Pydantic-Modell) in etwas konvertieren, das kompatibel mit JSON ist (etwa ein `dict`, eine `list`, usw.).
Zum Beispiel, wenn Sie es in einer Datenbank speichern möchten.
Dafür bietet **FastAPI** eine Funktion `jsonable_encoder()`.
## `jsonable_encoder` verwenden { #using-the-jsonable-encoder }
Stellen wir uns vor, Sie haben eine Datenbank `fake_db`, die nur JSON-kompatible Daten entgegennimmt.
Sie akzeptiert zum Beispiel keine `datetime`-Objekte, da die nicht kompatibel mit JSON sind.
Ein `datetime`-Objekt müsste also in einen `str` umgewandelt werden, der die Daten im [ISO-Format](https://en.wikipedia.org/wiki/ISO_8601) enthält.
Genauso würde die Datenbank kein Pydantic-Modell (ein Objekt mit Attributen) akzeptieren, sondern nur ein `dict`.
Sie können für diese Fälle `jsonable_encoder` verwenden.
Es nimmt ein Objekt entgegen, wie etwa ein Pydantic-Modell, und gibt eine JSON-kompatible Version zurück:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
In diesem Beispiel wird das Pydantic-Modell in ein `dict`, und das `datetime`-Objekt in ein `str` konvertiert.
Das Resultat dieses Aufrufs ist etwas, das mit Pythons Standard-[`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps) kodiert werden kann.
Es wird also kein großer `str` zurückgegeben, der die Daten im JSON-Format (als String) enthält. Es wird eine Python-Standarddatenstruktur (z. B. ein `dict`) zurückgegeben, mit Werten und Unterwerten, die alle mit JSON kompatibel sind.
/// note | Hinweis
`jsonable_encoder` wird tatsächlich von **FastAPI** intern verwendet, um Daten zu konvertieren. Aber es ist in vielen anderen Szenarien hilfreich.
///
================================================
FILE: docs/de/docs/tutorial/extra-data-types.md
================================================
# Zusätzliche Datentypen { #extra-data-types }
Bisher haben Sie gängige Datentypen verwendet, wie zum Beispiel:
* `int`
* `float`
* `str`
* `bool`
Sie können aber auch komplexere Datentypen verwenden.
Und Sie haben immer noch dieselbe Funktionalität wie bisher gesehen:
* Großartige Editor-Unterstützung.
* Datenkonvertierung bei eingehenden Requests.
* Datenkonvertierung für Response-Daten.
* Datenvalidierung.
* Automatische Annotation und Dokumentation.
## Andere Datentypen { #other-data-types }
Hier sind einige der zusätzlichen Datentypen, die Sie verwenden können:
* `UUID`:
* Ein standardmäßiger „universell eindeutiger Bezeichner“ („Universally Unique Identifier“), der in vielen Datenbanken und Systemen als ID üblich ist.
* Wird in Requests und Responses als `str` dargestellt.
* `datetime.datetime`:
* Ein Python-`datetime.datetime`.
* Wird in Requests und Responses als `str` im ISO 8601-Format dargestellt, etwa: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Python-`datetime.date`.
* Wird in Requests und Responses als `str` im ISO 8601-Format dargestellt, etwa: `2008-09-15`.
* `datetime.time`:
* Ein Python-`datetime.time`.
* Wird in Requests und Responses als `str` im ISO 8601-Format dargestellt, etwa: `14:23:55.003`.
* `datetime.timedelta`:
* Ein Python-`datetime.timedelta`.
* Wird in Requests und Responses als `float` der Gesamtsekunden dargestellt.
* Pydantic ermöglicht auch die Darstellung als „ISO 8601 Zeitdifferenz-Kodierung“, [siehe die Dokumentation für weitere Informationen](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* Wird in Requests und Responses wie ein `set` behandelt:
* Bei Requests wird eine Liste gelesen, Duplikate entfernt und in ein `set` umgewandelt.
* Bei Responses wird das `set` in eine `list` umgewandelt.
* Das generierte Schema zeigt an, dass die `set`-Werte eindeutig sind (unter Verwendung von JSON Schemas `uniqueItems`).
* `bytes`:
* Standard-Python-`bytes`.
* In Requests und Responses werden sie als `str` behandelt.
* Das generierte Schema wird anzeigen, dass es sich um einen `str` mit `binary` „Format“ handelt.
* `Decimal`:
* Standard-Python-`Decimal`.
* In Requests und Responses wird es wie ein `float` behandelt.
* Sie können alle gültigen Pydantic-Datentypen hier überprüfen: [Pydantic-Datentypen](https://docs.pydantic.dev/latest/usage/types/types/).
## Beispiel { #example }
Hier ist ein Beispiel für eine *Pfadoperation* mit Parametern, die einige der oben genannten Typen verwenden.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Beachten Sie, dass die Parameter innerhalb der Funktion ihren natürlichen Datentyp haben und Sie beispielsweise normale Datumsmanipulationen durchführen können, wie zum Beispiel:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/de/docs/tutorial/extra-models.md
================================================
# Extramodelle { #extra-models }
Im Anschluss an das vorherige Beispiel ist es üblich, mehr als ein zusammenhängendes Modell zu haben.
Dies gilt insbesondere für Benutzermodelle, denn:
* Das **Eingabemodell** muss ein Passwort enthalten können.
* Das **Ausgabemodell** sollte kein Passwort haben.
* Das **Datenbankmodell** müsste wahrscheinlich ein gehashtes Passwort haben.
/// danger | Gefahr
Speichern Sie niemals das Klartextpasswort eines Benutzers. Speichern Sie immer einen „sicheren Hash“, den Sie dann verifizieren können.
Wenn Sie nicht wissen, was das ist, werden Sie in den [Sicherheitskapiteln](security/simple-oauth2.md#password-hashing) lernen, was ein „Passworthash“ ist.
///
## Mehrere Modelle { #multiple-models }
Hier ist eine allgemeine Idee, wie die Modelle mit ihren Passwortfeldern aussehen könnten und an welchen Stellen sie verwendet werden:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Über `**user_in.model_dump()` { #about-user-in-model-dump }
#### Pydantics `.model_dump()` { #pydantics-model-dump }
`user_in` ist ein Pydantic-Modell der Klasse `UserIn`.
Pydantic-Modelle haben eine `.model_dump()`-Methode, die ein `dict` mit den Daten des Modells zurückgibt.
Wenn wir also ein Pydantic-Objekt `user_in` erstellen, etwa so:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
und dann aufrufen:
```Python
user_dict = user_in.model_dump()
```
haben wir jetzt ein `dict` mit den Daten in der Variablen `user_dict` (es ist ein `dict` statt eines Pydantic-Modellobjekts).
Und wenn wir aufrufen:
```Python
print(user_dict)
```
würden wir ein Python-`dict` erhalten mit:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Ein `dict` entpacken { #unpacking-a-dict }
Wenn wir ein `dict` wie `user_dict` nehmen und es einer Funktion (oder Klasse) mit `**user_dict` übergeben, wird Python es „entpacken“. Es wird die Schlüssel und Werte von `user_dict` direkt als Schlüsselwort-Argumente übergeben.
Setzen wir also das `user_dict` von oben ein:
```Python
UserInDB(**user_dict)
```
so ist das äquivalent zu:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Oder genauer gesagt, dazu, `user_dict` direkt zu verwenden, mit welchen Inhalten es auch immer in der Zukunft haben mag:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Ein Pydantic-Modell aus dem Inhalt eines anderen { #a-pydantic-model-from-the-contents-of-another }
Da wir im obigen Beispiel `user_dict` von `user_in.model_dump()` bekommen haben, wäre dieser Code:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
gleichwertig zu:
```Python
UserInDB(**user_in.model_dump())
```
... weil `user_in.model_dump()` ein `dict` ist, und dann lassen wir Python es „entpacken“, indem wir es an `UserInDB` mit vorangestelltem `**` übergeben.
Auf diese Weise erhalten wir ein Pydantic-Modell aus den Daten eines anderen Pydantic-Modells.
#### Ein `dict` entpacken und zusätzliche Schlüsselwort-Argumente { #unpacking-a-dict-and-extra-keywords }
Und dann fügen wir das zusätzliche Schlüsselwort-Argument `hashed_password=hashed_password` hinzu, wie in:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
... was so ist wie:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Achtung
Die unterstützenden zusätzlichen Funktionen `fake_password_hasher` und `fake_save_user` dienen nur zur Demo eines möglichen Datenflusses, bieten jedoch natürlich keine echte Sicherheit.
///
## Verdopplung vermeiden { #reduce-duplication }
Die Reduzierung von Code-Verdoppelung ist eine der Kernideen von **FastAPI**.
Da die Verdopplung von Code die Wahrscheinlichkeit von Fehlern, Sicherheitsproblemen, Problemen mit der Desynchronisation des Codes (wenn Sie an einer Stelle, aber nicht an der anderen aktualisieren) usw. erhöht.
Und diese Modelle teilen alle eine Menge der Daten und verdoppeln Attributnamen und -typen.
Wir könnten es besser machen.
Wir können ein `UserBase`-Modell deklarieren, das als Basis für unsere anderen Modelle dient. Und dann können wir Unterklassen dieses Modells erstellen, die seine Attribute (Typdeklarationen, Validierung usw.) erben.
Die ganze Datenkonvertierung, -validierung, -dokumentation usw. wird immer noch wie gewohnt funktionieren.
Auf diese Weise können wir nur die Unterschiede zwischen den Modellen (mit Klartext-`password`, mit `hashed_password` und ohne Passwort) deklarieren:
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` oder `anyOf` { #union-or-anyof }
Sie können deklarieren, dass eine Response eine `Union` mehrerer Typen ist, das bedeutet, dass die Response einer von ihnen ist.
Dies wird in OpenAPI mit `anyOf` definiert.
Um das zu tun, verwenden Sie den Standard-Python-Typhinweis [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note | Hinweis
Wenn Sie eine [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions) definieren, listen Sie den spezifischeren Typ zuerst auf, gefolgt vom weniger spezifischen Typ. Im Beispiel unten steht `PlaneItem` vor `CarItem` in `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` in Python 3.10 { #union-in-python-3-10 }
In diesem Beispiel übergeben wir `Union[PlaneItem, CarItem]` als Wert des Arguments `response_model`.
Da wir es als **Wert an ein Argument übergeben**, anstatt es in einer **Typannotation** zu verwenden, müssen wir `Union` verwenden, sogar in Python 3.10.
Wäre es eine Typannotation gewesen, hätten wir den vertikalen Strich verwenden können, wie in:
```Python
some_variable: PlaneItem | CarItem
```
Aber wenn wir das in der Zuweisung `response_model=PlaneItem | CarItem` machen, würden wir einen Fehler erhalten, weil Python versuchen würde, eine **ungültige Operation** zwischen `PlaneItem` und `CarItem` auszuführen, anstatt es als Typannotation zu interpretieren.
## Liste von Modellen { #list-of-models }
Auf die gleiche Weise können Sie Responses von Listen von Objekten deklarieren.
Dafür verwenden Sie Pythons Standard-`list`:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Response mit beliebigem `dict` { #response-with-arbitrary-dict }
Sie können auch eine Response deklarieren, die ein beliebiges `dict` zurückgibt, indem Sie nur die Typen der Schlüssel und Werte ohne ein Pydantic-Modell deklarieren.
Dies ist nützlich, wenn Sie die gültigen Feld-/Attributnamen nicht im Voraus kennen (die für ein Pydantic-Modell benötigt werden würden).
In diesem Fall können Sie `dict` verwenden:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Zusammenfassung { #recap }
Verwenden Sie gerne mehrere Pydantic-Modelle und vererben Sie je nach Bedarf.
Sie brauchen kein einzelnes Datenmodell pro Einheit, wenn diese Einheit in der Lage sein muss, verschiedene „Zustände“ zu haben. Wie im Fall der Benutzer-„Einheit“ mit einem Zustand einschließlich `password`, `password_hash` und ohne Passwort.
================================================
FILE: docs/de/docs/tutorial/first-steps.md
================================================
# Erste Schritte { #first-steps }
Die einfachste FastAPI-Datei könnte wie folgt aussehen:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Kopieren Sie das in eine Datei `main.py`.
Starten Sie den Live-Server:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
In der Konsolenausgabe sollte es eine Zeile geben, die ungefähr so aussieht:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Diese Zeile zeigt die URL, unter der Ihre App auf Ihrem lokalen Computer bereitgestellt wird.
### Es testen { #check-it }
Öffnen Sie Ihren Browser unter [http://127.0.0.1:8000](http://127.0.0.1:8000).
Sie werden die JSON-Response sehen:
```JSON
{"message": "Hello World"}
```
### Interaktive API-Dokumentation { #interactive-api-docs }
Gehen Sie als Nächstes auf [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Sie werden die automatisch erzeugte, interaktive API-Dokumentation sehen (bereitgestellt durch [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Alternative API-Dokumentation { #alternative-api-docs }
Gehen Sie nun auf [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Dort sehen Sie die alternative, automatische Dokumentation (bereitgestellt durch [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
**FastAPI** generiert ein „Schema“ mit all Ihren APIs unter Verwendung des **OpenAPI**-Standards zur Definition von APIs.
#### „Schema“ { #schema }
Ein „Schema“ ist eine Definition oder Beschreibung von etwas. Nicht der eigentliche Code, der es implementiert, sondern lediglich eine abstrakte Beschreibung.
#### API-„Schema“ { #api-schema }
In diesem Fall ist [OpenAPI](https://github.com/OAI/OpenAPI-Specification) eine Spezifikation, die vorschreibt, wie ein Schema für Ihre API zu definieren ist.
Diese Schemadefinition enthält Ihre API-Pfade, die möglichen Parameter, welche diese entgegennehmen, usw.
#### Daten-„Schema“ { #data-schema }
Der Begriff „Schema“ kann sich auch auf die Form von Daten beziehen, wie z. B. einen JSON-Inhalt.
In diesem Fall sind die JSON-Attribute und deren Datentypen, usw. gemeint.
#### OpenAPI und JSON Schema { #openapi-and-json-schema }
OpenAPI definiert ein API-Schema für Ihre API. Dieses Schema enthält Definitionen (oder „Schemas“) der Daten, die von Ihrer API unter Verwendung von **JSON Schema**, dem Standard für JSON-Datenschemata, gesendet und empfangen werden.
#### Die `openapi.json` testen { #check-the-openapi-json }
Falls Sie wissen möchten, wie das rohe OpenAPI-Schema aussieht: FastAPI generiert automatisch ein JSON (Schema) mit den Beschreibungen Ihrer gesamten API.
Sie können es direkt einsehen unter: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Es wird ein JSON angezeigt, welches ungefähr so aussieht:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Wofür OpenAPI gedacht ist { #what-is-openapi-for }
Das OpenAPI-Schema ist die Grundlage für die beiden enthaltenen interaktiven Dokumentationssysteme.
Es gibt dutzende Alternativen, die alle auf OpenAPI basieren. Sie können jede dieser Alternativen problemlos zu Ihrer mit **FastAPI** erstellten Anwendung hinzufügen.
Ebenfalls können Sie es verwenden, um automatisch Code für Clients zu generieren, die mit Ihrer API kommunizieren. Zum Beispiel für Frontend-, Mobile- oder IoT-Anwendungen.
### Den App-`entrypoint` in `pyproject.toml` konfigurieren { #configure-the-app-entrypoint-in-pyproject-toml }
Sie können in einer `pyproject.toml`-Datei konfigurieren, wo sich Ihre App befindet, z. B.:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Dieser `entrypoint` teilt dem `fastapi`-Befehl mit, dass er die App folgendermaßen importieren soll:
```python
from main import app
```
Wenn Ihr Code so strukturiert wäre:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Dann würden Sie den `entrypoint` so setzen:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
was äquivalent wäre zu:
```python
from backend.main import app
```
### `fastapi dev` mit Pfad { #fastapi-dev-with-path }
Sie können auch den Dateipfad an den Befehl `fastapi dev` übergeben, und er wird das zu verwendende FastAPI-App-Objekt erraten:
```console
$ fastapi dev main.py
```
Aber Sie müssten sich daran erinnern, bei jedem Aufruf des `fastapi`-Befehls den korrekten Pfad zu übergeben.
Zusätzlich könnten andere Tools es nicht finden, z. B. die [VS Code-Erweiterung](../editor-support.md) oder [FastAPI Cloud](https://fastapicloud.com). Daher wird empfohlen, den `entrypoint` in `pyproject.toml` zu verwenden.
### Ihre App deployen (optional) { #deploy-your-app-optional }
Sie können optional Ihre FastAPI-App in der [FastAPI Cloud](https://fastapicloud.com) deployen, treten Sie der Warteliste bei, falls Sie es noch nicht getan haben. 🚀
Wenn Sie bereits ein **FastAPI Cloud**-Konto haben (wir haben Sie von der Warteliste eingeladen 😉), können Sie Ihre Anwendung mit einem Befehl deployen.
Vor dem Deployen, stellen Sie sicher, dass Sie eingeloggt sind:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Dann stellen Sie Ihre App bereit:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Das war's! Jetzt können Sie Ihre App unter dieser URL aufrufen. ✨
## Zusammenfassung, Schritt für Schritt { #recap-step-by-step }
### Schritt 1: `FastAPI` importieren { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` ist eine Python-Klasse, die die gesamte Funktionalität für Ihre API bereitstellt.
/// note | Technische Details
`FastAPI` ist eine Klasse, die direkt von `Starlette` erbt.
Sie können alle [Starlette](https://www.starlette.dev/)-Funktionalitäten auch mit `FastAPI` nutzen.
///
### Schritt 2: Erzeugen einer `FastAPI`-„Instanz“ { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
In diesem Beispiel ist die Variable `app` eine „Instanz“ der Klasse `FastAPI`.
Dies wird der Hauptinteraktionspunkt für die Erstellung all Ihrer APIs sein.
### Schritt 3: Erstellen einer *Pfadoperation* { #step-3-create-a-path-operation }
#### Pfad { #path }
„Pfad“ bezieht sich hier auf den letzten Teil der URL, beginnend mit dem ersten `/`.
In einer URL wie:
```
https://example.com/items/foo
```
... wäre der Pfad folglich:
```
/items/foo
```
/// info | Info
Ein „Pfad“ wird häufig auch als „Endpunkt“ oder „Route“ bezeichnet.
///
Bei der Erstellung einer API ist der „Pfad“ die wichtigste Möglichkeit zur Trennung von „Anliegen“ und „Ressourcen“.
#### Operation { #operation }
„Operation“ bezieht sich hier auf eine der HTTP-„Methoden“.
Eine von diesen:
* `POST`
* `GET`
* `PUT`
* `DELETE`
... und die etwas Exotischeren:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
Im HTTP-Protokoll können Sie mit jedem Pfad über eine (oder mehrere) dieser „Methoden“ kommunizieren.
---
Bei der Erstellung von APIs verwenden Sie normalerweise diese spezifischen HTTP-Methoden, um eine bestimmte Aktion durchzuführen.
Normalerweise verwenden Sie:
* `POST`: um Daten zu erzeugen (create).
* `GET`: um Daten zu lesen (read).
* `PUT`: um Daten zu aktualisieren (update).
* `DELETE`: um Daten zu löschen (delete).
In OpenAPI wird folglich jede dieser HTTP-Methoden als „Operation“ bezeichnet.
Wir werden sie auch „**Operationen**“ nennen.
#### Definieren eines *Pfadoperation-Dekorators* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
Das `@app.get("/")` sagt **FastAPI**, dass die Funktion direkt darunter für die Bearbeitung von Requests zuständig ist, die an:
* den Pfad `/`
* unter der Verwendung der get-Operation gehen
/// info | `@decorator` Info
Diese `@something`-Syntax wird in Python „Dekorator“ genannt.
Sie platzieren ihn über einer Funktion. Wie ein hübscher, dekorativer Hut (daher kommt wohl der Begriff).
Ein „Dekorator“ nimmt die darunter stehende Funktion und macht etwas damit.
In unserem Fall teilt dieser Dekorator **FastAPI** mit, dass die folgende Funktion mit dem **Pfad** `/` und der **Operation** `get` zusammenhängt.
Dies ist der „**Pfadoperation-Dekorator**“.
///
Sie können auch die anderen Operationen verwenden:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Und die exotischeren:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Tipp
Es steht Ihnen frei, jede Operation (HTTP-Methode) so zu verwenden, wie Sie es möchten.
**FastAPI** erzwingt keine bestimmte Bedeutung.
Die hier aufgeführten Informationen dienen als Leitfaden und sind nicht verbindlich.
Wenn Sie beispielsweise GraphQL verwenden, führen Sie normalerweise alle Aktionen nur mit „POST“-Operationen durch.
///
### Schritt 4: Definieren der **Pfadoperation-Funktion** { #step-4-define-the-path-operation-function }
Das ist unsere „**Pfadoperation-Funktion**“:
* **Pfad**: ist `/`.
* **Operation**: ist `get`.
* **Funktion**: ist die Funktion direkt unter dem „Dekorator“ (unter `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Dies ist eine Python-Funktion.
Sie wird von **FastAPI** immer dann aufgerufen, wenn sie einen Request an die URL „`/`“ mittels einer `GET`-Operation erhält.
In diesem Fall handelt es sich um eine `async`-Funktion.
---
Sie könnten sie auch als normale Funktion anstelle von `async def` definieren:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Hinweis
Wenn Sie den Unterschied nicht kennen, lesen Sie [Async: *„In Eile?“*](../async.md#in-a-hurry).
///
### Schritt 5: den Inhalt zurückgeben { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Sie können ein `dict`, eine `list`, einzelne Werte wie `str`, `int`, usw. zurückgeben.
Sie können auch Pydantic-Modelle zurückgeben (dazu später mehr).
Es gibt viele andere Objekte und Modelle, die automatisch zu JSON konvertiert werden (einschließlich ORMs, usw.). Versuchen Sie, Ihre Lieblingsobjekte zu verwenden. Es ist sehr wahrscheinlich, dass sie bereits unterstützt werden.
### Schritt 6: Deployen { #step-6-deploy-it }
Stellen Sie Ihre App in der **[FastAPI Cloud](https://fastapicloud.com)** mit einem Befehl bereit: `fastapi deploy`. 🎉
#### Über FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** wird vom selben Autor und Team hinter **FastAPI** entwickelt.
Es vereinfacht den Prozess des Erstellens, Deployens und des Zugriffs auf eine API mit minimalem Aufwand.
Es bringt die gleiche **Developer-Experience** beim Erstellen von Apps mit FastAPI auch zum **Deployment** in der Cloud. 🎉
FastAPI Cloud ist der Hauptsponsor und Finanzierer der „FastAPI and friends“ Open-Source-Projekte. ✨
#### Zu anderen Cloudanbietern deployen { #deploy-to-other-cloud-providers }
FastAPI ist Open Source und basiert auf Standards. Sie können FastAPI-Apps bei jedem Cloudanbieter Ihrer Wahl deployen.
Folgen Sie den Anleitungen Ihres Cloudanbieters, um dort FastAPI-Apps bereitzustellen. 🤓
## Zusammenfassung { #recap }
* Importieren Sie `FastAPI`.
* Erstellen Sie eine `app` Instanz.
* Schreiben Sie einen **Pfadoperation-Dekorator** unter Verwendung von Dekoratoren wie `@app.get("/")`.
* Definieren Sie eine **Pfadoperation-Funktion**, zum Beispiel `def root(): ...`.
* Starten Sie den Entwicklungsserver mit dem Befehl `fastapi dev`.
* Optional: Ihre App mit `fastapi deploy` deployen.
================================================
FILE: docs/de/docs/tutorial/handling-errors.md
================================================
# Fehler behandeln { #handling-errors }
Es gibt viele Situationen, in denen Sie einem Client, der Ihre API nutzt, einen Fehler mitteilen müssen.
Dieser Client könnte ein Browser mit einem Frontend sein, ein Code von jemand anderem, ein IoT-Gerät usw.
Sie könnten dem Client mitteilen müssen, dass:
* Der Client nicht genügend Berechtigungen für diese Operation hat.
* Der Client keinen Zugriff auf diese Ressource hat.
* Die Ressource, auf die der Client versucht hat, zuzugreifen, nicht existiert.
* usw.
In diesen Fällen würden Sie normalerweise einen **HTTP-Statuscode** im Bereich **400** (von 400 bis 499) zurückgeben.
Dies ist vergleichbar mit den HTTP-Statuscodes im Bereich 200 (von 200 bis 299). Diese „200“-Statuscodes bedeuten, dass der Request in irgendeiner Weise erfolgreich war.
Die Statuscodes im Bereich 400 bedeuten hingegen, dass es einen Fehler seitens des Clients gab.
Erinnern Sie sich an all diese **„404 Not Found“** Fehler (und Witze)?
## `HTTPException` verwenden { #use-httpexception }
Um HTTP-Responses mit Fehlern an den Client zurückzugeben, verwenden Sie `HTTPException`.
### `HTTPException` importieren { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Eine `HTTPException` in Ihrem Code auslösen { #raise-an-httpexception-in-your-code }
`HTTPException` ist eine normale Python-Exception mit zusätzlichen Daten, die für APIs relevant sind.
Weil es eine Python-Exception ist, geben Sie sie nicht zurück (`return`), sondern lösen sie aus (`raise`).
Das bedeutet auch, wenn Sie sich innerhalb einer Hilfsfunktion befinden, die Sie innerhalb Ihrer *Pfadoperation-Funktion* aufrufen, und Sie die `HTTPException` aus dieser Hilfsfunktion heraus auslösen, wird der restliche Code in der *Pfadoperation-Funktion* nicht ausgeführt. Der Request wird sofort abgebrochen und der HTTP-Error der `HTTPException` wird an den Client gesendet.
Der Vorteil des Auslösens einer Exception gegenüber dem Zurückgeben eines Wertes wird im Abschnitt über Abhängigkeiten und Sicherheit deutlicher werden.
In diesem Beispiel lösen wir eine Exception mit einem Statuscode von `404` aus, wenn der Client einen Artikel mit einer nicht existierenden ID anfordert:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Die resultierende Response { #the-resulting-response }
Wenn der Client `http://example.com/items/foo` anfordert (ein `item_id` `"foo"`), erhält dieser Client einen HTTP-Statuscode 200 und diese JSON-Response:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Aber wenn der Client `http://example.com/items/bar` anfordert (ein nicht-existierendes `item_id` `"bar"`), erhält er einen HTTP-Statuscode 404 (der „Not Found“-Error) und eine JSON-Response wie:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Tipp
Wenn Sie eine `HTTPException` auslösen, können Sie dem Parameter `detail` jeden Wert übergeben, der in JSON konvertiert werden kann, nicht nur `str`.
Sie könnten ein `dict`, eine `list`, usw. übergeben.
Diese werden von **FastAPI** automatisch gehandhabt und in JSON konvertiert.
///
## Benutzerdefinierte Header hinzufügen { #add-custom-headers }
Es gibt Situationen, in denen es nützlich ist, dem HTTP-Error benutzerdefinierte Header hinzuzufügen. Zum Beispiel in einigen Sicherheitsszenarien.
Sie werden es wahrscheinlich nicht direkt in Ihrem Code verwenden müssen.
Aber falls Sie es für ein fortgeschrittenes Szenario benötigen, können Sie benutzerdefinierte Header hinzufügen:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Benutzerdefinierte Exceptionhandler installieren { #install-custom-exception-handlers }
Sie können benutzerdefinierte Exceptionhandler mit [denselben Exception-Werkzeugen von Starlette](https://www.starlette.dev/exceptions/) hinzufügen.
Angenommen, Sie haben eine benutzerdefinierte Exception `UnicornException`, die Sie (oder eine Bibliothek, die Sie verwenden) `raise`n könnten.
Und Sie möchten diese Exception global mit FastAPI handhaben.
Sie könnten einen benutzerdefinierten Exceptionhandler mit `@app.exception_handler()` hinzufügen:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Hier, wenn Sie `/unicorns/yolo` anfordern, wird die *Pfadoperation* eine `UnicornException` `raise`n.
Aber diese wird von `unicorn_exception_handler` gehandhabt.
Sie erhalten also einen sauberen Fehler mit einem HTTP-Statuscode von `418` und dem JSON-Inhalt:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Technische Details
Sie könnten auch `from starlette.requests import Request` und `from starlette.responses import JSONResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, nur als Annehmlichkeit für Sie, den Entwickler. Aber die meisten verfügbaren Responses kommen direkt von Starlette. Dasselbe gilt für `Request`.
///
## Die Default-Exceptionhandler überschreiben { #override-the-default-exception-handlers }
**FastAPI** hat einige Default-Exceptionhandler.
Diese Handler sind dafür verantwortlich, die Default-JSON-Responses zurückzugeben, wenn Sie eine `HTTPException` `raise`n und wenn der Request ungültige Daten enthält.
Sie können diese Exceptionhandler mit Ihren eigenen überschreiben.
### Überschreiben von Request-Validierungs-Exceptions { #override-request-validation-exceptions }
Wenn ein Request ungültige Daten enthält, löst **FastAPI** intern einen `RequestValidationError` aus.
Und es enthält auch einen Default-Exceptionhandler für diesen.
Um diesen zu überschreiben, importieren Sie den `RequestValidationError` und verwenden Sie ihn mit `@app.exception_handler(RequestValidationError)`, um den Exceptionhandler zu dekorieren.
Der Exceptionhandler erhält einen `Request` und die Exception.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Wenn Sie nun zu `/items/foo` gehen, erhalten Sie anstelle des standardmäßigen JSON-Fehlers mit:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
eine Textversion mit:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Überschreiben des `HTTPException`-Fehlerhandlers { #override-the-httpexception-error-handler }
Auf die gleiche Weise können Sie den `HTTPException`-Handler überschreiben.
Zum Beispiel könnten Sie eine Klartext-Response statt JSON für diese Fehler zurückgeben wollen:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Technische Details
Sie könnten auch `from starlette.responses import PlainTextResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, nur als Annehmlichkeit für Sie, den Entwickler. Aber die meisten verfügbaren Responses kommen direkt von Starlette.
///
/// warning | Achtung
Beachten Sie, dass der `RequestValidationError` Informationen über den Dateinamen und die Zeile enthält, in der der Validierungsfehler auftritt, sodass Sie ihn bei Bedarf mit den relevanten Informationen in Ihren Logs anzeigen können.
Das bedeutet aber auch, dass, wenn Sie ihn einfach in einen String umwandeln und diese Informationen direkt zurückgeben, Sie möglicherweise ein paar Informationen über Ihr System preisgeben. Daher extrahiert und zeigt der Code hier jeden Fehler getrennt.
///
### Verwenden des `RequestValidationError`-Bodys { #use-the-requestvalidationerror-body }
Der `RequestValidationError` enthält den empfangenen `body` mit den ungültigen Daten.
Sie könnten diesen während der Entwicklung Ihrer Anwendung verwenden, um den Body zu loggen und zu debuggen, ihn an den Benutzer zurückzugeben usw.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Versuchen Sie nun, einen ungültigen Artikel zu senden:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Sie erhalten eine Response, die Ihnen sagt, dass die Daten ungültig sind und die den empfangenen Body enthält:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### FastAPIs `HTTPException` vs. Starlettes `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** hat seine eigene `HTTPException`.
Und die `HTTPException`-Fehlerklasse von **FastAPI** erbt von der `HTTPException`-Fehlerklasse von Starlette.
Der einzige Unterschied besteht darin, dass die `HTTPException` von **FastAPI** beliebige JSON-konvertierbare Daten für das `detail`-Feld akzeptiert, während die `HTTPException` von Starlette nur Strings dafür akzeptiert.
Sie können also weiterhin die `HTTPException` von **FastAPI** wie üblich in Ihrem Code auslösen.
Aber wenn Sie einen Exceptionhandler registrieren, sollten Sie ihn für die `HTTPException` von Starlette registrieren.
Auf diese Weise, wenn irgendein Teil des internen Codes von Starlette, oder eine Starlette-Erweiterung oder ein Plug-in, eine Starlette `HTTPException` auslöst, wird Ihr Handler in der Lage sein, diese abzufangen und zu handhaben.
Um in diesem Beispiel beide `HTTPException`s im selben Code zu haben, wird die Exception von Starlette zu `StarletteHTTPException` umbenannt:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Die Exceptionhandler von **FastAPI** wiederverwenden { #reuse-fastapis-exception-handlers }
Wenn Sie die Exception zusammen mit den gleichen Default-Exceptionhandlern von **FastAPI** verwenden möchten, können Sie die Default-Exceptionhandler aus `fastapi.exception_handlers` importieren und wiederverwenden:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
In diesem Beispiel geben Sie nur den Fehler mit einer sehr ausdrucksstarken Nachricht aus, aber Sie verstehen das Prinzip. Sie können die Exception verwenden und dann einfach die Default-Exceptionhandler wiederverwenden.
================================================
FILE: docs/de/docs/tutorial/header-param-models.md
================================================
# Header-Parameter-Modelle { #header-parameter-models }
Wenn Sie eine Gruppe verwandter **Header-Parameter** haben, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren.
Dadurch können Sie das **Modell an mehreren Stellen wiederverwenden** und auch Validierungen und Metadaten für alle Parameter gleichzeitig deklarieren. 😎
/// note | Hinweis
Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
///
## Header-Parameter mit einem Pydantic-Modell { #header-parameters-with-a-pydantic-model }
Deklarieren Sie die erforderlichen **Header-Parameter** in einem **Pydantic-Modell** und dann den Parameter als `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** wird die Daten für **jedes Feld** aus den **Headern** des Request extrahieren und Ihnen das von Ihnen definierte Pydantic-Modell geben.
## Die Dokumentation testen { #check-the-docs }
Sie können die erforderlichen Header in der Dokumentationsoberfläche unter `/docs` sehen:
## Zusätzliche Header verbieten { #forbid-extra-headers }
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die **Header einschränken**, die Sie erhalten möchten.
Sie können Pydantics Modellkonfiguration verwenden, um `extra` Felder zu verbieten (`forbid`):
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Wenn ein Client versucht, einige **zusätzliche Header** zu senden, erhält er eine **Error-Response**.
Zum Beispiel, wenn der Client versucht, einen `tool`-Header mit einem Wert von `plumbus` zu senden, erhält er eine **Error-Response**, die ihm mitteilt, dass der Header-Parameter `tool` nicht erlaubt ist:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Automatische Umwandlung von Unterstrichen deaktivieren { #disable-convert-underscores }
Ähnlich wie bei regulären Header-Parametern werden bei der Verwendung von Unterstrichen in den Parameternamen diese **automatisch in Bindestriche umgewandelt**.
Wenn Sie beispielsweise einen Header-Parameter `save_data` im Code haben, wird der erwartete HTTP-Header `save-data` sein, und er wird auch so in der Dokumentation angezeigt.
Falls Sie aus irgendeinem Grund diese automatische Umwandlung deaktivieren müssen, können Sie dies auch für Pydantic-Modelle für Header-Parameter tun.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Achtung
Bevor Sie `convert_underscores` auf `False` setzen, bedenken Sie, dass einige HTTP-Proxies und -Server die Verwendung von Headern mit Unterstrichen nicht zulassen.
///
## Zusammenfassung { #summary }
Sie können **Pydantic-Modelle** verwenden, um **Header** in **FastAPI** zu deklarieren. 😎
================================================
FILE: docs/de/docs/tutorial/header-params.md
================================================
# Header-Parameter { #header-parameters }
Sie können Header-Parameter genauso definieren, wie Sie `Query`-, `Path`- und `Cookie`-Parameter definieren.
## `Header` importieren { #import-header }
Importieren Sie zuerst `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## `Header`-Parameter deklarieren { #declare-header-parameters }
Deklarieren Sie dann die Header-Parameter mit derselben Struktur wie bei `Path`, `Query` und `Cookie`.
Sie können den Defaultwert sowie alle zusätzlichen Validierungs- oder Annotationsparameter definieren:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Technische Details
`Header` ist eine „Schwester“-Klasse von `Path`, `Query` und `Cookie`. Sie erbt ebenfalls von der gemeinsamen `Param`-Klasse.
Aber denken Sie daran, dass bei der Nutzung von `Query`, `Path`, `Header` und anderen Importen aus `fastapi`, diese tatsächlich Funktionen sind, die spezielle Klassen zurückgeben.
///
/// info | Info
Um Header zu deklarieren, müssen Sie `Header` verwenden, da die Parameter sonst als Query-Parameter interpretiert werden würden.
///
## Automatische Konvertierung { #automatic-conversion }
`Header` bietet etwas zusätzliche Funktionalität im Vergleich zu `Path`, `Query` und `Cookie`.
Die meisten Standard-Header sind durch ein „Bindestrich“-Zeichen getrennt, auch bekannt als „Minus-Symbol“ (`-`).
Aber eine Variable wie `user-agent` ist in Python ungültig.
Daher wird `Header` standardmäßig die Zeichen des Parameter-Namens von Unterstrich (`_`) zu Bindestrich (`-`) konvertieren, um die Header zu extrahieren und zu dokumentieren.
Außerdem ist Groß-/Kleinschreibung in HTTP-Headern nicht relevant, daher können Sie sie im Standard-Python-Stil (auch bekannt als „snake_case“) deklarieren.
Sie können also `user_agent` verwenden, wie Sie es normalerweise im Python-Code tun würden, anstatt die Anfangsbuchstaben wie bei `User_Agent` großzuschreiben oder Ähnliches.
Wenn Sie aus irgendeinem Grund die automatische Konvertierung von Unterstrichen zu Bindestrichen deaktivieren müssen, setzen Sie den Parameter `convert_underscores` von `Header` auf `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Achtung
Bevor Sie `convert_underscores` auf `False` setzen, bedenken Sie, dass manche HTTP-Proxys und Server die Verwendung von Headern mit Unterstrichen nicht erlauben.
///
## Doppelte Header { #duplicate-headers }
Es ist möglich, doppelte Header zu empfangen. Damit ist gemeint, denselben Header mit mehreren Werten.
Sie können solche Fälle definieren, indem Sie in der Typdeklaration eine Liste verwenden.
Sie erhalten dann alle Werte von diesem doppelten Header als Python-`list`.
Um beispielsweise einen `X-Token`-Header zu deklarieren, der mehrmals vorkommen kann, können Sie schreiben:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Wenn Sie mit dieser *Pfadoperation* kommunizieren und zwei HTTP-Header senden, wie:
```
X-Token: foo
X-Token: bar
```
Dann wäre die Response:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Zusammenfassung { #recap }
Deklarieren Sie Header mit `Header`, wobei Sie dasselbe gängige Muster wie bei `Query`, `Path` und `Cookie` verwenden.
Und machen Sie sich keine Sorgen um Unterstriche in Ihren Variablen, **FastAPI** wird sich darum kümmern, sie zu konvertieren.
================================================
FILE: docs/de/docs/tutorial/index.md
================================================
# Tutorial – Benutzerhandbuch { #tutorial-user-guide }
Dieses Tutorial zeigt Ihnen Schritt für Schritt, wie Sie **FastAPI** mit den meisten seiner Funktionen verwenden können.
Jeder Abschnitt baut schrittweise auf den vorhergehenden auf, ist jedoch in einzelne Themen gegliedert, sodass Sie direkt zu einem bestimmten Thema übergehen können, um Ihre spezifischen API-Anforderungen zu lösen.
Es ist auch so gestaltet, dass es als zukünftige Referenz dient, sodass Sie jederzeit zurückkommen und genau das sehen, was Sie benötigen.
## Den Code ausführen { #run-the-code }
Alle Codeblöcke können kopiert und direkt verwendet werden (es sind tatsächlich getestete Python-Dateien).
Um eines der Beispiele auszuführen, kopieren Sie den Code in eine Datei `main.py`, und starten Sie `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Es wird **dringend empfohlen**, den Code zu schreiben oder zu kopieren, ihn zu bearbeiten und lokal auszuführen.
Die Verwendung in Ihrem eigenen Editor zeigt Ihnen die Vorteile von FastAPI am besten, wenn Sie sehen, wie wenig Code Sie schreiben müssen, all die Typprüfungen, die automatische Vervollständigung usw.
---
## FastAPI installieren { #install-fastapi }
Der erste Schritt besteht darin, FastAPI zu installieren.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und dann **FastAPI installieren**:
/// note | Hinweis
Wenn Sie mit `pip install "fastapi[standard]"` installieren, werden einige optionale Standard-Abhängigkeiten mit installiert, einschließlich `fastapi-cloud-cli`, welches Ihnen das Deployment in der [FastAPI Cloud](https://fastapicloud.com) ermöglicht.
Wenn Sie diese optionalen Abhängigkeiten nicht haben möchten, können Sie stattdessen `pip install fastapi` installieren.
Wenn Sie die Standard-Abhängigkeiten, aber ohne das `fastapi-cloud-cli` installieren möchten, können Sie mit `pip install "fastapi[standard-no-fastapi-cloud-cli]"` installieren.
///
/// tip | Tipp
FastAPI hat eine [offizielle Erweiterung für VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (und Cursor), die viele Funktionen bereitstellt, darunter einen Pfadoperation-Explorer, eine Pfadoperation-Suche, CodeLens-Navigation in Tests (zur Definition aus Tests springen) sowie FastAPI-Cloud-Deployment und Logs – alles direkt aus Ihrem Editor.
///
## Handbuch für fortgeschrittene Benutzer { #advanced-user-guide }
Es gibt auch ein **Handbuch für fortgeschrittene Benutzer**, das Sie nach diesem **Tutorial – Benutzerhandbuch** lesen können.
Das **Handbuch für fortgeschrittene Benutzer** baut hierauf auf, verwendet dieselben Konzepte und bringt Ihnen einige zusätzliche Funktionen bei.
Sie sollten jedoch zuerst das **Tutorial – Benutzerhandbuch** lesen (was Sie gerade tun).
Es ist so konzipiert, dass Sie mit dem **Tutorial – Benutzerhandbuch** eine vollständige Anwendung erstellen können und diese dann je nach Bedarf mit einigen der zusätzlichen Ideen aus dem **Handbuch für fortgeschrittene Benutzer** erweitern können.
================================================
FILE: docs/de/docs/tutorial/metadata.md
================================================
# Metadaten und Dokumentations-URLs { #metadata-and-docs-urls }
Sie können mehrere Metadaten-Konfigurationen in Ihrer **FastAPI**-Anwendung anpassen.
## Metadaten für die API { #metadata-for-api }
Sie können die folgenden Felder festlegen, die in der OpenAPI-Spezifikation und in den Benutzeroberflächen der automatischen API-Dokumentation verwendet werden:
| Parameter | Typ | Beschreibung |
|------------|------|-------------|
| `title` | `str` | Der Titel der API. |
| `summary` | `str` | Eine kurze Zusammenfassung der API. Verfügbar seit OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Eine kurze Beschreibung der API. Kann Markdown verwenden. |
| `version` | `string` | Die Version der API. Das ist die Version Ihrer eigenen Anwendung, nicht die von OpenAPI. Zum Beispiel `2.5.0`. |
| `terms_of_service` | `str` | Eine URL zu den Nutzungsbedingungen für die API. Falls angegeben, muss es sich um eine URL handeln. |
| `contact` | `dict` | Die Kontaktinformationen für die freigegebene API. Kann mehrere Felder enthalten. contact-Felder
Parameter
Typ
Beschreibung
name
str
Der identifizierende Name der Kontaktperson/Organisation.
url
str
Die URL, die auf die Kontaktinformationen verweist. MUSS im Format einer URL vorliegen.
email
str
Die E-Mail-Adresse der Kontaktperson/Organisation. MUSS im Format einer E-Mail-Adresse vorliegen.
|
| `license_info` | `dict` | Die Lizenzinformationen für die freigegebene API. Kann mehrere Felder enthalten. license_info-Felder
Parameter
Typ
Beschreibung
name
str
ERFORDERLICH (wenn eine license_info festgelegt ist). Der für die API verwendete Lizenzname.
identifier
str
Ein [SPDX](https://spdx.org/licenses/)-Lizenzausdruck für die API. Das Feld identifier und das Feld url schließen sich gegenseitig aus. Verfügbar seit OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
Eine URL zur Lizenz, die für die API verwendet wird. MUSS im Format einer URL vorliegen.
|
Sie können diese wie folgt setzen:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Tipp
Sie können Markdown im Feld `description` verwenden, und es wird in der Ausgabe gerendert.
///
Mit dieser Konfiguration würde die automatische API-Dokumentation wie folgt aussehen:
## Lizenzkennung { #license-identifier }
Seit OpenAPI 3.1.0 und FastAPI 0.99.0 können Sie die `license_info` auch mit einem `identifier` anstelle einer `url` festlegen.
Zum Beispiel:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Metadaten für Tags { #metadata-for-tags }
Sie können auch zusätzliche Metadaten für die verschiedenen Tags hinzufügen, die zum Gruppieren Ihrer Pfadoperationen verwendet werden, mit dem Parameter `openapi_tags`.
Er nimmt eine Liste entgegen, die für jeden Tag ein Dictionary enthält.
Jedes Dictionary kann Folgendes enthalten:
* `name` (**erforderlich**): ein `str` mit demselben Tag-Namen, den Sie im Parameter `tags` in Ihren *Pfadoperationen* und `APIRouter`n verwenden.
* `description`: ein `str` mit einer kurzen Beschreibung für das Tag. Sie kann Markdown enthalten und wird in der Benutzeroberfläche der Dokumentation angezeigt.
* `externalDocs`: ein `dict`, das externe Dokumentation beschreibt mit:
* `description`: ein `str` mit einer kurzen Beschreibung für die externe Dokumentation.
* `url` (**erforderlich**): ein `str` mit der URL für die externe Dokumentation.
### Metadaten für Tags erstellen { #create-metadata-for-tags }
Versuchen wir es mit einem Beispiel mit Tags für `users` und `items`.
Erstellen Sie Metadaten für Ihre Tags und übergeben Sie diese an den Parameter `openapi_tags`:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Beachten Sie, dass Sie Markdown innerhalb der Beschreibungen verwenden können. Zum Beispiel wird „login“ in Fettschrift (**login**) und „fancy“ in Kursivschrift (_fancy_) angezeigt.
/// tip | Tipp
Sie müssen nicht für alle von Ihnen verwendeten Tags Metadaten hinzufügen.
///
### Ihre Tags verwenden { #use-your-tags }
Verwenden Sie den Parameter `tags` mit Ihren *Pfadoperationen* (und `APIRouter`n), um diese verschiedenen Tags zuzuweisen:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Info
Lesen Sie mehr zu Tags unter [Pfadoperation-Konfiguration](path-operation-configuration.md#tags).
///
### Die Dokumentation testen { #check-the-docs }
Wenn Sie nun die Dokumentation ansehen, werden dort alle zusätzlichen Metadaten angezeigt:
### Reihenfolge der Tags { #order-of-tags }
Die Reihenfolge der Tag-Metadaten-Dictionarys definiert auch die Reihenfolge, in der diese in der Benutzeroberfläche der Dokumentation angezeigt werden.
Auch wenn beispielsweise `users` im Alphabet nach `items` kommt, wird es vor diesen angezeigt, da wir deren Metadaten als erstes Dictionary der Liste hinzugefügt haben.
## OpenAPI-URL { #openapi-url }
Standardmäßig wird das OpenAPI-Schema unter `/openapi.json` bereitgestellt.
Sie können das aber mit dem Parameter `openapi_url` konfigurieren.
Um beispielsweise festzulegen, dass es unter `/api/v1/openapi.json` bereitgestellt wird:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Wenn Sie das OpenAPI-Schema vollständig deaktivieren möchten, können Sie `openapi_url=None` festlegen, wodurch auch die Dokumentationsbenutzeroberflächen deaktiviert werden, die es verwenden.
## Dokumentations-URLs { #docs-urls }
Sie können die beiden enthaltenen Dokumentationsbenutzeroberflächen konfigurieren:
* **Swagger UI**: bereitgestellt unter `/docs`.
* Sie können deren URL mit dem Parameter `docs_url` festlegen.
* Sie können sie deaktivieren, indem Sie `docs_url=None` festlegen.
* **ReDoc**: bereitgestellt unter `/redoc`.
* Sie können deren URL mit dem Parameter `redoc_url` festlegen.
* Sie können sie deaktivieren, indem Sie `redoc_url=None` festlegen.
Um beispielsweise Swagger UI so einzustellen, dass sie unter `/documentation` bereitgestellt wird, und ReDoc zu deaktivieren:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/de/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
Sie können Middleware zu **FastAPI**-Anwendungen hinzufügen.
Eine „Middleware“ ist eine Funktion, die mit jedem **Request** arbeitet, bevor er von einer bestimmten *Pfadoperation* verarbeitet wird. Und auch mit jeder **Response**, bevor sie zurückgegeben wird.
* Sie nimmt jeden **Request** entgegen, der an Ihre Anwendung gesendet wird.
* Sie kann dann etwas mit diesem **Request** tun oder beliebigen Code ausführen.
* Dann gibt sie den **Request** zur Verarbeitung durch den Rest der Anwendung weiter (durch eine bestimmte *Pfadoperation*).
* Sie nimmt dann die **Response** entgegen, die von der Anwendung generiert wurde (durch eine bestimmte *Pfadoperation*).
* Sie kann etwas mit dieser **Response** tun oder beliebigen Code ausführen.
* Dann gibt sie die **Response** zurück.
/// note | Technische Details
Wenn Sie Abhängigkeiten mit `yield` haben, wird der Exit-Code *nach* der Middleware ausgeführt.
Wenn es Hintergrundtasks gab (dies wird später im [Hintergrundtasks](background-tasks.md)-Abschnitt behandelt), werden sie *nach* allen Middlewares ausgeführt.
///
## Eine Middleware erstellen { #create-a-middleware }
Um eine Middleware zu erstellen, verwenden Sie den Dekorator `@app.middleware("http")` über einer Funktion.
Die Middleware-Funktion erhält:
* Den `request`.
* Eine Funktion `call_next`, die den `request` als Parameter erhält.
* Diese Funktion gibt den `request` an die entsprechende *Pfadoperation* weiter.
* Dann gibt es die von der entsprechenden *Pfadoperation* generierte `response` zurück.
* Sie können die `response` dann weiter modifizieren, bevor Sie sie zurückgeben.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Tipp
Beachten Sie, dass benutzerdefinierte proprietäre Header hinzugefügt werden können [unter Verwendung des `X-`-Präfixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Wenn Sie jedoch benutzerdefinierte Header haben, die ein Client in einem Browser sehen soll, müssen Sie sie zu Ihrer CORS-Konfiguration ([CORS (Cross-Origin Resource Sharing)](cors.md)) hinzufügen, indem Sie den Parameter `expose_headers` verwenden, der in [Starlettes CORS-Dokumentation](https://www.starlette.dev/middleware/#corsmiddleware) dokumentiert ist.
///
/// note | Technische Details
Sie könnten auch `from starlette.requests import Request` verwenden.
**FastAPI** bietet es als Komfort für Sie, den Entwickler, an. Aber es stammt direkt von Starlette.
///
### Vor und nach der `response` { #before-and-after-the-response }
Sie können Code hinzufügen, der mit dem `request` ausgeführt wird, bevor dieser von einer beliebigen *Pfadoperation* empfangen wird.
Und auch nachdem die `response` generiert wurde, bevor sie zurückgegeben wird.
Sie könnten beispielsweise einen benutzerdefinierten Header `X-Process-Time` hinzufügen, der die Zeit in Sekunden enthält, die benötigt wurde, um den Request zu verarbeiten und eine Response zu generieren:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Tipp
Hier verwenden wir [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) anstelle von `time.time()`, da es für diese Anwendungsfälle präziser sein kann. 🤓
///
## Ausführungsreihenfolge bei mehreren Middlewares { #multiple-middleware-execution-order }
Wenn Sie mehrere Middlewares hinzufügen, entweder mit dem `@app.middleware()` Dekorator oder der Methode `app.add_middleware()`, umschließt jede neue Middleware die Anwendung und bildet einen Stapel. Die zuletzt hinzugefügte Middleware ist die *äußerste*, und die erste ist die *innerste*.
Auf dem Requestpfad läuft die *äußerste* Middleware zuerst.
Auf dem Responsepfad läuft sie zuletzt.
Zum Beispiel:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Dies führt zu folgender Ausführungsreihenfolge:
* **Request**: MiddlewareB → MiddlewareA → Route
* **Response**: Route → MiddlewareA → MiddlewareB
Dieses Stapelverhalten stellt sicher, dass Middlewares in einer vorhersehbaren und kontrollierbaren Reihenfolge ausgeführt werden.
## Andere Middlewares { #other-middlewares }
Sie können später mehr über andere Middlewares im [Handbuch für fortgeschrittene Benutzer: Fortgeschrittene Middleware](../advanced/middleware.md) lesen.
In der nächsten Sektion erfahren Sie, wie Sie CORS mit einer Middleware behandeln können.
================================================
FILE: docs/de/docs/tutorial/path-operation-configuration.md
================================================
# Pfadoperation-Konfiguration { #path-operation-configuration }
Es gibt mehrere Parameter, die Sie Ihrem *Pfadoperation-Dekorator* übergeben können, um ihn zu konfigurieren.
/// warning | Achtung
Beachten Sie, dass diese Parameter direkt dem *Pfadoperation-Dekorator* übergeben werden, nicht der *Pfadoperation-Funktion*.
///
## Response-Statuscode { #response-status-code }
Sie können den (HTTP-)`status_code` definieren, der in der Response Ihrer *Pfadoperation* verwendet werden soll.
Sie können direkt den `int`-Code übergeben, etwa `404`.
Aber falls Sie sich nicht mehr erinnern, wofür jeder Nummerncode steht, können Sie die Abkürzungs-Konstanten in `status` verwenden:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Dieser Statuscode wird in der Response verwendet und zum OpenAPI-Schema hinzugefügt.
/// note | Technische Details
Sie können auch `from starlette import status` verwenden.
**FastAPI** bietet dieselben `starlette.status`-Codes auch via `fastapi.status` an, als Annehmlichkeit für Sie, den Entwickler. Sie kommen aber direkt von Starlette.
///
## Tags { #tags }
Sie können Ihrer *Pfadoperation* Tags hinzufügen, indem Sie dem Parameter `tags` eine `list`e von `str`s übergeben (in der Regel nur ein `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Diese werden zum OpenAPI-Schema hinzugefügt und von den automatischen Dokumentations-Benutzeroberflächen verwendet:
### Tags mittels Enumeration { #tags-with-enums }
Wenn Sie eine große Anwendung haben, können sich am Ende **viele Tags** anhäufen, und Sie möchten sicherstellen, dass Sie für verwandte *Pfadoperationen* immer den **gleichen Tag** verwenden.
In diesem Fall macht es Sinn, die Tags in einem `Enum` zu speichern.
**FastAPI** unterstützt das auf die gleiche Weise wie einfache Strings:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Zusammenfassung und Beschreibung { #summary-and-description }
Sie können eine `summary` und eine `description` hinzufügen:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Beschreibung mittels Docstring { #description-from-docstring }
Da Beschreibungen oft mehrere Zeilen lang sind, können Sie die Beschreibung der *Pfadoperation* im Docstring der Funktion deklarieren, und **FastAPI** wird sie daraus auslesen.
Sie können [Markdown](https://en.wikipedia.org/wiki/Markdown) im Docstring schreiben, es wird korrekt interpretiert und angezeigt (unter Berücksichtigung der Einrückung des Docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Es wird in der interaktiven Dokumentation verwendet:
## Beschreibung der Response { #response-description }
Sie können die Response mit dem Parameter `response_description` beschreiben:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Info
Beachten Sie, dass sich `response_description` speziell auf die Response bezieht, während `description` sich generell auf die *Pfadoperation* bezieht.
///
/// check | Testen
OpenAPI verlangt, dass jede *Pfadoperation* über eine Beschreibung der Response verfügt.
Daher, wenn Sie keine vergeben, wird **FastAPI** automatisch eine für „Erfolgreiche Response“ erstellen.
///
## Eine *Pfadoperation* deprecaten { #deprecate-a-path-operation }
Wenn Sie eine *Pfadoperation* als deprecatet kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Sie wird in der interaktiven Dokumentation gut sichtbar als deprecatet markiert werden:
Vergleichen Sie, wie deprecatete und nicht-deprecatete *Pfadoperationen* aussehen:
## Zusammenfassung { #recap }
Sie können auf einfache Weise Metadaten für Ihre *Pfadoperationen* definieren, indem Sie den *Pfadoperation-Dekoratoren* Parameter hinzufügen.
================================================
FILE: docs/de/docs/tutorial/path-params-numeric-validations.md
================================================
# Pfad-Parameter und Validierung von Zahlen { #path-parameters-and-numeric-validations }
So wie Sie mit `Query` für Query-Parameter zusätzliche Validierungen und Metadaten deklarieren können, können Sie mit `Path` die gleichen Validierungen und Metadaten für Pfad-Parameter deklarieren.
## `Path` importieren { #import-path }
Importieren Sie zuerst `Path` von `fastapi`, und importieren Sie `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Info
FastAPI hat in Version 0.95.0 Unterstützung für `Annotated` hinzugefügt und es zur Verwendung empfohlen.
Wenn Sie eine ältere Version haben, würden Fehler angezeigt werden, wenn Sie versuchen, `Annotated` zu verwenden.
Stellen Sie sicher, dass Sie [FastAPI aktualisieren](../deployment/versions.md#upgrading-the-fastapi-versions), auf mindestens Version 0.95.1, bevor Sie `Annotated` verwenden.
///
## Metadaten deklarieren { #declare-metadata }
Sie können dieselben Parameter wie für `Query` deklarieren.
Um zum Beispiel einen `title`-Metadaten-Wert für den Pfad-Parameter `item_id` zu deklarieren, können Sie schreiben:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Hinweis
Ein Pfad-Parameter ist immer erforderlich, da er Teil des Pfads sein muss. Selbst wenn Sie ihn mit `None` deklarieren oder einen Defaultwert setzen, würde das nichts ändern, er wäre dennoch immer erforderlich.
///
## Die Parameter sortieren, wie Sie möchten { #order-the-parameters-as-you-need }
/// tip | Tipp
Das ist wahrscheinlich nicht so wichtig oder notwendig, wenn Sie `Annotated` verwenden.
///
Angenommen, Sie möchten den Query-Parameter `q` als erforderlichen `str` deklarieren.
Und Sie müssen sonst nichts anderes für diesen Parameter deklarieren, Sie brauchen also `Query` nicht wirklich.
Aber Sie müssen dennoch `Path` für den `item_id`-Pfad-Parameter verwenden. Und aus irgendeinem Grund möchten Sie `Annotated` nicht verwenden.
Python wird sich beschweren, wenn Sie einen Wert mit einem „Default“ vor einem Wert ohne „Default“ setzen.
Aber Sie können die Reihenfolge ändern und den Wert ohne Default (den Query-Parameter `q`) zuerst setzen.
Für **FastAPI** spielt es keine Rolle. Es erkennt die Parameter anhand ihrer Namen, Typen und Default-Deklarationen (`Query`, `Path`, usw.), es kümmert sich nicht um die Reihenfolge.
Sie können Ihre Funktion also so deklarieren:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da es nicht darauf ankommt, dass Sie keine Funktionsparameter-Defaultwerte für `Query()` oder `Path()` verwenden.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Die Parameter sortieren, wie Sie möchten: Tricks { #order-the-parameters-as-you-need-tricks }
/// tip | Tipp
Das ist wahrscheinlich nicht so wichtig oder notwendig, wenn Sie `Annotated` verwenden.
///
Hier ist ein **kleiner Trick**, der nützlich sein kann, obwohl Sie ihn nicht oft benötigen werden.
Wenn Sie:
* den `q`-Query-Parameter sowohl ohne `Query` als auch ohne Defaultwert deklarieren
* den Pfad-Parameter `item_id` mit `Path` deklarieren
* sie in einer anderen Reihenfolge haben
* nicht `Annotated` verwenden
... möchten, dann hat Python eine kleine Spezial-Syntax dafür.
Übergeben Sie `*`, als den ersten Parameter der Funktion.
Python wird nichts mit diesem `*` machen, aber es wird wissen, dass alle folgenden Parameter als Schlüsselwortargumente (Schlüssel-Wert-Paare) verwendet werden sollen, auch bekannt als kwargs. Selbst wenn diese keinen Defaultwert haben.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Besser mit `Annotated` { #better-with-annotated }
Bedenken Sie, dass Sie, wenn Sie `Annotated` verwenden, da Sie keine Funktionsparameter-Defaultwerte verwenden, dieses Problem nicht haben werden und wahrscheinlich nicht `*` verwenden müssen.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Validierung von Zahlen: Größer oder gleich { #number-validations-greater-than-or-equal }
Mit `Query` und `Path` (und anderen, die Sie später sehen werden) können Sie Zahlenbeschränkungen deklarieren.
Hier, mit `ge=1`, muss `item_id` eine ganze Zahl sein, die „`g`reater than or `e`qual to“ (größer oder gleich) `1` ist.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Validierung von Zahlen: Größer und kleiner oder gleich { #number-validations-greater-than-and-less-than-or-equal }
Das Gleiche gilt für:
* `gt`: `g`reater `t`han (größer als)
* `le`: `l`ess than or `e`qual (kleiner oder gleich)
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Validierung von Zahlen: Floats, größer und kleiner { #number-validations-floats-greater-than-and-less-than }
Zahlenvalidierung funktioniert auch für `float`-Werte.
Hier wird es wichtig, in der Lage zu sein, gt und nicht nur ge zu deklarieren. Da Sie mit dieser Option erzwingen können, dass ein Wert größer als `0` sein muss, selbst wenn er kleiner als `1` ist.
Also wäre `0.5` ein gültiger Wert. Aber `0.0` oder `0` nicht.
Und das Gleiche gilt für lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Zusammenfassung { #recap }
Mit `Query`, `Path` (und anderen, die Sie noch nicht gesehen haben) können Sie Metadaten und Stringvalidierungen auf die gleichen Weisen deklarieren wie in [Query-Parameter und Stringvalidierungen](query-params-str-validations.md) beschrieben.
Und Sie können auch Zahlenvalidierungen deklarieren:
* `gt`: `g`reater `t`han (größer als)
* `ge`: `g`reater than or `e`qual (größer oder gleich)
* `lt`: `l`ess `t`han (kleiner als)
* `le`: `l`ess than or `e`qual (kleiner oder gleich)
/// info | Info
`Query`, `Path`, und andere Klassen, die Sie später sehen werden, sind Unterklassen einer gemeinsamen `Param`-Klasse.
Alle von ihnen teilen die gleichen Parameter für zusätzliche Validierung und Metadaten, die Sie gesehen haben.
///
/// note | Technische Details
Wenn Sie `Query`, `Path` und andere von `fastapi` importieren, sind sie tatsächlich Funktionen.
Die, wenn sie aufgerufen werden, Instanzen von Klassen mit demselben Namen zurückgeben.
Sie importieren also `Query`, was eine Funktion ist. Und wenn Sie sie aufrufen, gibt sie eine Instanz einer Klasse zurück, die auch `Query` genannt wird.
Diese Funktionen existieren (statt die Klassen direkt zu verwenden), damit Ihr Editor keine Fehlermeldungen über ihre Typen ausgibt.
Auf diese Weise können Sie Ihren normalen Editor und Ihre Programmier-Tools verwenden, ohne besondere Einstellungen vornehmen zu müssen, um diese Fehlermeldungen stummzuschalten.
///
================================================
FILE: docs/de/docs/tutorial/path-params.md
================================================
# Pfad-Parameter { #path-parameters }
Sie können Pfad-„Parameter“ oder -„Variablen“ mit der gleichen Syntax deklarieren, welche in Python-Formatstrings verwendet wird:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
Der Wert des Pfad-Parameters `item_id` wird Ihrer Funktion als das Argument `item_id` übergeben.
Wenn Sie dieses Beispiel ausführen und auf [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) gehen, sehen Sie als Response:
```JSON
{"item_id":"foo"}
```
## Pfad-Parameter mit Typen { #path-parameters-with-types }
Sie können den Typ eines Pfad-Parameters in der Argumentliste der Funktion deklarieren, mit Standard-Python-Typannotationen:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
In diesem Fall wird `item_id` als `int` deklariert, also als Ganzzahl.
/// check | Testen
Dadurch erhalten Sie Editor-Unterstützung innerhalb Ihrer Funktion, mit Fehlerprüfungen, Codevervollständigung, usw.
///
## Daten-Konversion { #data-conversion }
Wenn Sie dieses Beispiel ausführen und Ihren Browser unter [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3) öffnen, sehen Sie als Response:
```JSON
{"item_id":3}
```
/// check | Testen
Beachten Sie, dass der Wert, den Ihre Funktion erhält und zurückgibt, die Zahl `3` ist, also ein `int`. Nicht der String „3“, also ein `str`.
Sprich, mit dieser Typdeklaration wird **FastAPI** den „parsen“.
///
## Datenvalidierung { #data-validation }
Wenn Sie aber im Browser [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) besuchen, erhalten Sie eine hübsche HTTP-Fehlermeldung:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
Der Pfad-Parameter `item_id` hatte den Wert „foo“, was kein `int` ist.
Die gleiche Fehlermeldung würde angezeigt werden, wenn Sie ein `float` (also eine Kommazahl) statt eines `int`s übergeben würden, wie etwa in: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Testen
Sprich, mit der gleichen Python-Typdeklaration gibt Ihnen **FastAPI** Datenvalidierung.
Beachten Sie, dass die Fehlermeldung auch direkt die Stelle anzeigt, wo die Validierung nicht erfolgreich war.
Das ist unglaublich hilfreich, wenn Sie Code entwickeln und debuggen, welcher mit Ihrer API interagiert.
///
## Dokumentation { #documentation }
Wenn Sie die Seite [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) in Ihrem Browser öffnen, sehen Sie eine automatische, interaktive API-Dokumentation:
/// check | Testen
Wiederum, mit dieser gleichen Python-Typdeklaration gibt Ihnen **FastAPI** eine automatische, interaktive Dokumentation (verwendet die Swagger-Benutzeroberfläche).
Beachten Sie, dass der Pfad-Parameter dort als Ganzzahl deklariert ist.
///
## Nützliche Standards, alternative Dokumentation { #standards-based-benefits-alternative-documentation }
Und weil das generierte Schema vom [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md)-Standard kommt, gibt es viele kompatible Tools.
Zum Beispiel bietet **FastAPI** selbst eine alternative API-Dokumentation (verwendet ReDoc), welche Sie unter [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) einsehen können:
Und viele weitere kompatible Tools. Inklusive Codegenerierung für viele Sprachen.
## Pydantic { #pydantic }
Die ganze Datenvalidierung wird hinter den Kulissen von [Pydantic](https://docs.pydantic.dev/) durchgeführt, Sie profitieren also von dessen Vorteilen. Und Sie wissen, dass Sie in guten Händen sind.
Sie können für Typdeklarationen auch `str`, `float`, `bool` und viele andere komplexe Datentypen verwenden.
Mehrere davon werden wir in den nächsten Kapiteln erkunden.
## Die Reihenfolge ist wichtig { #order-matters }
Wenn Sie *Pfadoperationen* erstellen, haben Sie manchmal einen fixen Pfad.
Etwa `/users/me`, um Daten über den aktuellen Benutzer zu erhalten.
Und Sie haben auch einen Pfad `/users/{user_id}`, um Daten über einen spezifischen Benutzer zu erhalten, mittels einer Benutzer-ID.
Weil *Pfadoperationen* in ihrer Reihenfolge ausgewertet werden, müssen Sie sicherstellen, dass der Pfad `/users/me` vor `/users/{user_id}` deklariert wurde:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Ansonsten würde der Pfad für `/users/{user_id}` auch `/users/me` auswerten, und annehmen, dass ein Parameter `user_id` mit dem Wert „me“ übergeben wurde.
Sie können eine Pfadoperation auch nicht erneut definieren:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Die erste Definition wird immer verwendet werden, da ihr Pfad zuerst übereinstimmt.
## Vordefinierte Parameterwerte { #predefined-values }
Wenn Sie eine *Pfadoperation* haben, welche einen *Pfad-Parameter* hat, aber Sie wollen, dass dessen gültige Werte vordefiniert sind, können Sie ein Standard-Python `Enum` verwenden.
### Eine `Enum`-Klasse erstellen { #create-an-enum-class }
Importieren Sie `Enum` und erstellen Sie eine Unterklasse, die von `str` und `Enum` erbt.
Indem Sie von `str` erben, weiß die API-Dokumentation, dass die Werte vom Typ `str` sein müssen, und wird in der Lage sein, korrekt zu rendern.
Erstellen Sie dann Klassen-Attribute mit festgelegten Werten, welches die erlaubten Werte sein werden:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Tipp
Falls Sie sich fragen, was „AlexNet“, „ResNet“ und „LeNet“ ist, das sind Namen von Modellen für maschinelles Lernen.
///
### Einen *Pfad-Parameter* deklarieren { #declare-a-path-parameter }
Dann erstellen Sie einen *Pfad-Parameter*, der als Typ die gerade erstellte Enum-Klasse hat (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Die API-Dokumentation testen { #check-the-docs }
Weil die erlaubten Werte für den *Pfad-Parameter* nun vordefiniert sind, kann die interaktive Dokumentation sie als Auswahl-Drop-Down anzeigen:
### Mit Python-*Enumerationen* arbeiten { #working-with-python-enumerations }
Der *Pfad-Parameter* wird ein *Member einer Enumeration* sein.
#### *Enumeration-Member* vergleichen { #compare-enumeration-members }
Sie können ihn mit einem Member Ihrer Enumeration `ModelName` vergleichen:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### *Enumerations-Wert* erhalten { #get-the-enumeration-value }
Den tatsächlichen Wert (in diesem Fall ein `str`) erhalten Sie via `model_name.value`, oder generell, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Tipp
Sie können den Wert `"lenet"` außerdem mittels `ModelName.lenet.value` abrufen.
///
#### *Enumeration-Member* zurückgeben { #return-enumeration-members }
Sie können *Enum-Member* in ihrer *Pfadoperation* zurückgeben, sogar verschachtelt in einem JSON-Body (z. B. als `dict`).
Diese werden zu ihren entsprechenden Werten konvertiert (in diesem Fall Strings), bevor sie zum Client übertragen werden:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
In Ihrem Client erhalten Sie eine JSON-Response, wie etwa:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Pfad-Parameter, die Pfade enthalten { #path-parameters-containing-paths }
Angenommen, Sie haben eine *Pfadoperation* mit einem Pfad `/files/{file_path}`.
Aber `file_path` soll selbst einen *Pfad* enthalten, etwa `home/johndoe/myfile.txt`.
Sprich, die URL für diese Datei wäre etwas wie: `/files/home/johndoe/myfile.txt`.
### OpenAPI-Unterstützung { #openapi-support }
OpenAPI bietet nicht die Möglichkeit, dass ein *Pfad-Parameter* seinerseits einen *Pfad* enthalten kann, das würde zu Szenarios führen, die schwierig zu testen und zu definieren sind.
Trotzdem können Sie das in **FastAPI** tun, indem Sie eines der internen Tools von Starlette verwenden.
Die Dokumentation würde weiterhin funktionieren, allerdings wird nicht dokumentiert werden, dass der Parameter ein Pfad sein sollte.
### Pfad-Konverter { #path-convertor }
Mittels einer Option direkt von Starlette können Sie einen *Pfad-Parameter* deklarieren, der einen Pfad enthalten soll, indem Sie eine URL wie folgt definieren:
```
/files/{file_path:path}
```
In diesem Fall ist der Name des Parameters `file_path`. Der letzte Teil, `:path`, sagt aus, dass der Parameter ein *Pfad* sein soll.
Sie verwenden das also wie folgt:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Tipp
Der Parameter könnte einen führenden Schrägstrich (`/`) haben, wie etwa in `/home/johndoe/myfile.txt`.
In dem Fall wäre die URL: `/files//home/johndoe/myfile.txt`, mit einem doppelten Schrägstrich (`//`) zwischen `files` und `home`.
///
## Zusammenfassung { #recap }
In **FastAPI** erhalten Sie mittels kurzer, intuitiver Typdeklarationen:
* Editor-Unterstützung: Fehlerprüfungen, Codevervollständigung, usw.
* Daten „parsen“
* Datenvalidierung
* API-Annotationen und automatische Dokumentation
Und Sie müssen sie nur einmal deklarieren.
Das ist wahrscheinlich der sichtbarste Unterschied zwischen **FastAPI** und alternativen Frameworks (abgesehen von der reinen Performanz).
================================================
FILE: docs/de/docs/tutorial/query-param-models.md
================================================
# Query-Parameter-Modelle { #query-parameter-models }
Wenn Sie eine Gruppe von **Query-Parametern** haben, die miteinander in Beziehung stehen, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren.
Dadurch können Sie das **Modell an mehreren Stellen wiederverwenden** und gleichzeitig Validierungen und Metadaten für alle Parameter auf einmal deklarieren. 😎
/// note | Hinweis
Dies wird seit FastAPI Version `0.115.0` unterstützt. 🤓
///
## Query-Parameter mit einem Pydantic-Modell { #query-parameters-with-a-pydantic-model }
Deklarieren Sie die benötigten **Query-Parameter** in einem **Pydantic-Modell** und dann den Parameter als `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI** wird die Daten für **jedes Feld** aus den **Query-Parametern** des Request extrahieren und Ihnen das definierte Pydantic-Modell bereitstellen.
## Die Dokumentation testen { #check-the-docs }
Sie können die Query-Parameter in der Dokumentations-Oberfläche unter `/docs` einsehen:
## Zusätzliche Query-Parameter verbieten { #forbid-extra-query-parameters }
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die Query-Parameter, die Sie empfangen möchten, **beschränken**.
Sie können die Modellkonfiguration von Pydantic verwenden, um jegliche `extra` Felder zu `verbieten`:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Wenn ein Client versucht, einige **zusätzliche** Daten in den **Query-Parametern** zu senden, erhält er eine **Error-Response**.
Wenn der Client beispielsweise versucht, einen `tool` Query-Parameter mit dem Wert `plumbus` zu senden, wie:
```http
https://example.com/items/?limit=10&tool=plumbus
```
erhält er eine **Error-Response**, die ihm mitteilt, dass der Query-Parameter `tool` nicht erlaubt ist:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Zusammenfassung { #summary }
Sie können **Pydantic-Modelle** verwenden, um **Query-Parameter** in **FastAPI** zu deklarieren. 😎
/// tip | Tipp
Spoiler-Alarm: Sie können auch Pydantic-Modelle verwenden, um Cookies und Header zu deklarieren, aber darüber werden Sie später im Tutorial lesen. 🤫
///
================================================
FILE: docs/de/docs/tutorial/query-params-str-validations.md
================================================
# Query-Parameter und String-Validierungen { #query-parameters-and-string-validations }
**FastAPI** ermöglicht es Ihnen, zusätzliche Informationen und Validierungen für Ihre Parameter zu deklarieren.
Nehmen wir diese Anwendung als Beispiel:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Der Query-Parameter `q` hat den Typ `str | None`, das bedeutet, dass er vom Typ `str` sein kann, aber auch `None`, und tatsächlich ist der Defaultwert `None`, sodass FastAPI weiß, dass er nicht erforderlich ist.
/// note | Hinweis
FastAPI erkennt, dass der Wert von `q` nicht erforderlich ist, aufgrund des Defaultwertes `= None`.
Die Verwendung von `str | None` ermöglicht es Ihrem Editor, Ihnen bessere Unterstützung zu bieten und Fehler zu erkennen.
///
## Zusätzliche Validierung { #additional-validation }
Wir werden sicherstellen, dass, obwohl `q` optional ist, wann immer es bereitgestellt wird, **seine Länge 50 Zeichen nicht überschreitet**.
### `Query` und `Annotated` importieren { #import-query-and-annotated }
Um dies zu erreichen, importieren Sie zuerst:
* `Query` von `fastapi`
* `Annotated` von `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Info
FastAPI hat Unterstützung für `Annotated` hinzugefügt (und begonnen, es zu empfehlen) in der Version 0.95.0.
Wenn Sie eine ältere Version haben, würden Sie Fehler erhalten, beim Versuch, `Annotated` zu verwenden.
Stellen Sie sicher, dass Sie [die FastAPI-Version aktualisieren](../deployment/versions.md#upgrading-the-fastapi-versions), auf mindestens Version 0.95.1, bevor Sie `Annotated` verwenden.
///
## `Annotated` im Typ für den `q`-Parameter verwenden { #use-annotated-in-the-type-for-the-q-parameter }
Erinnern Sie sich, dass ich Ihnen zuvor in [Python-Typen-Intro](../python-types.md#type-hints-with-metadata-annotations) gesagt habe, dass `Annotated` verwendet werden kann, um Metadaten zu Ihren Parametern hinzuzufügen?
Jetzt ist es soweit, dies mit FastAPI zu verwenden. 🚀
Wir hatten diese Typannotation:
```Python
q: str | None = None
```
Was wir tun werden, ist, dies mit `Annotated` zu wrappen, sodass es zu:
```Python
q: Annotated[str | None] = None
```
Beide dieser Versionen bedeuten dasselbe: `q` ist ein Parameter, der ein `str` oder `None` sein kann, und standardmäßig ist er `None`.
Jetzt springen wir zu den spannenden Dingen. 🎉
## `Query` zu `Annotated` im `q`-Parameter hinzufügen { #add-query-to-annotated-in-the-q-parameter }
Da wir nun `Annotated` haben, in das wir mehr Informationen (in diesem Fall einige zusätzliche Validierungen) einfügen können, fügen Sie `Query` innerhalb von `Annotated` hinzu und setzen Sie den Parameter `max_length` auf `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Beachten Sie, dass der Defaultwert weiterhin `None` ist, so dass der Parameter weiterhin optional ist.
Aber jetzt, mit `Query(max_length=50)` innerhalb von `Annotated`, sagen wir FastAPI, dass wir eine **zusätzliche Validierung** für diesen Wert wünschen, wir wollen, dass er maximal 50 Zeichen hat. 😎
/// tip | Tipp
Hier verwenden wir `Query()`, weil dies ein **Query-Parameter** ist. Später werden wir andere wie `Path()`, `Body()`, `Header()`, und `Cookie()` sehen, die auch dieselben Argumente wie `Query()` akzeptieren.
///
FastAPI wird nun:
* Die Daten **validieren**, um sicherzustellen, dass die Länge maximal 50 Zeichen beträgt
* Einen **klaren Fehler** für den Client anzeigen, wenn die Daten ungültig sind
* Den Parameter in der OpenAPI-Schema-*Pfadoperation* **dokumentieren** (sodass er in der **automatischen Dokumentation** angezeigt wird)
## Alternative (alt): `Query` als Defaultwert { #alternative-old-query-as-the-default-value }
Frühere Versionen von FastAPI (vor 0.95.0) erforderten, dass Sie `Query` als den Defaultwert Ihres Parameters verwendeten, anstatt es innerhalb von `Annotated` zu platzieren. Es besteht eine hohe Wahrscheinlichkeit, dass Sie Code sehen, der es so verwendet, also werde ich es Ihnen erklären.
/// tip | Tipp
Für neuen Code und wann immer es möglich ist, verwenden Sie `Annotated` wie oben erklärt. Es gibt mehrere Vorteile (unten erläutert) und keine Nachteile. 🍰
///
So würden Sie `Query()` als den Defaultwert Ihres Funktionsparameters verwenden und den Parameter `max_length` auf 50 setzen:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Da wir in diesem Fall (ohne die Verwendung von `Annotated`) den Defaultwert `None` in der Funktion durch `Query()` ersetzen müssen, müssen wir nun den Defaultwert mit dem Parameter `Query(default=None)` setzen, er erfüllt den gleichen Zweck, diesen Defaultwert zu definieren (zumindest für FastAPI).
Also:
```Python
q: str | None = Query(default=None)
```
... macht den Parameter optional mit einem Defaultwert von `None`, genauso wie:
```Python
q: str | None = None
```
Aber die `Query`-Version deklariert ihn explizit als Query-Parameter.
Dann können wir mehr Parameter an `Query` übergeben. In diesem Fall den `max_length`-Parameter, der auf Strings angewendet wird:
```Python
q: str | None = Query(default=None, max_length=50)
```
Dies wird die Daten validieren, einen klaren Fehler anzeigen, wenn die Daten nicht gültig sind, und den Parameter in der OpenAPI-Schema-*Pfadoperation* dokumentieren.
### `Query` als Defaultwert oder in `Annotated` { #query-as-the-default-value-or-in-annotated }
Beachten Sie, dass wenn Sie `Query` innerhalb von `Annotated` verwenden, Sie den `default`-Parameter für `Query` nicht verwenden dürfen.
Setzen Sie stattdessen den tatsächlichen Defaultwert des Funktionsparameters. Andernfalls wäre es inkonsistent.
Zum Beispiel ist das nicht erlaubt:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
... denn es ist nicht klar, ob der Defaultwert „rick“ oder „morty“ sein soll.
Sie würden also (bevorzugt) schreiben:
```Python
q: Annotated[str, Query()] = "rick"
```
... oder in älteren Codebasen finden Sie:
```Python
q: str = Query(default="rick")
```
### Vorzüge von `Annotated` { #advantages-of-annotated }
**Es wird empfohlen, `Annotated` zu verwenden**, anstelle des Defaultwertes in Funktionsparametern, es ist aus mehreren Gründen **besser**. 🤓
Der **Default**wert des **Funktionsparameters** ist der **tatsächliche Default**wert, das ist in der Regel intuitiver mit Python. 😌
Sie könnten **diese gleiche Funktion** in **anderen Stellen** ohne FastAPI **aufrufen**, und es würde **wie erwartet funktionieren**. Wenn es einen **erforderlichen** Parameter gibt (ohne Defaultwert), wird Ihr **Editor** Ihnen dies mit einem Fehler mitteilen, außerdem wird **Python** sich beschweren, wenn Sie es ausführen, ohne den erforderlichen Parameter zu übergeben.
Wenn Sie `Annotated` nicht verwenden und stattdessen die **(alte) Defaultwert-Stilform** verwenden, müssen Sie sich daran **erinnern**, die Argumente der Funktion zu übergeben, wenn Sie diese Funktion ohne FastAPI in **anderen Stellen** aufrufen. Ansonsten sind die Werte anders als erwartet (z. B. `QueryInfo` oder etwas Ähnliches statt `str`). Ihr Editor kann Ihnen nicht helfen, und Python wird die Funktion ohne Klagen ausführen und sich nur beschweren wenn die Operationen innerhalb auf einen Fehler stoßen.
Da `Annotated` mehr als eine Metadaten-Annotation haben kann, könnten Sie dieselbe Funktion sogar mit anderen Tools verwenden, wie z. B. [Typer](https://typer.tiangolo.com/). 🚀
## Mehr Validierungen hinzufügen { #add-more-validations }
Sie können auch einen `min_length`-Parameter hinzufügen:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Reguläre Ausdrücke hinzufügen { #add-regular-expressions }
Sie können einen regulären Ausdruck `pattern` definieren, mit dem der Parameter übereinstimmen muss:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Dieses spezielle Suchmuster im regulären Ausdruck überprüft, dass der erhaltene Parameterwert:
* `^`: mit den nachfolgenden Zeichen beginnt, keine Zeichen davor hat.
* `fixedquery`: den exakten Text `fixedquery` hat.
* `$`: dort endet, keine weiteren Zeichen nach `fixedquery` hat.
Wenn Sie sich mit all diesen **„regulärer Ausdruck“**-Ideen verloren fühlen, keine Sorge. Sie sind ein schwieriges Thema für viele Menschen. Sie können noch viele Dinge tun, ohne reguläre Ausdrücke direkt zu benötigen.
Aber nun wissen Sie, dass Sie sie in **FastAPI** immer dann verwenden können, wenn Sie sie brauchen.
## Defaultwerte { #default-values }
Natürlich können Sie Defaultwerte verwenden, die nicht `None` sind.
Nehmen wir an, Sie möchten, dass der `q` Query-Parameter eine `min_length` von `3` hat und einen Defaultwert von „fixedquery“:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Hinweis
Ein Defaultwert irgendeines Typs, einschließlich `None`, macht den Parameter optional (nicht erforderlich).
///
## Erforderliche Parameter { #required-parameters }
Wenn wir keine weiteren Validierungen oder Metadaten deklarieren müssen, können wir den `q` Query-Parameter erforderlich machen, indem wir einfach keinen Defaultwert deklarieren, wie:
```Python
q: str
```
statt:
```Python
q: str | None = None
```
Aber jetzt deklarieren wir es mit `Query`, zum Beispiel so:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Wenn Sie einen Wert als erforderlich deklarieren müssen, während Sie `Query` verwenden, deklarieren Sie einfach keinen Defaultwert:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Erforderlich, kann `None` sein { #required-can-be-none }
Sie können deklarieren, dass ein Parameter `None` akzeptieren kann, aber trotzdem erforderlich ist. Dadurch müssten Clients den Wert senden, selbst wenn der Wert `None` ist.
Um das zu tun, können Sie deklarieren, dass `None` ein gültiger Typ ist, einfach indem Sie keinen Defaultwert deklarieren:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Query-Parameter-Liste / Mehrere Werte { #query-parameter-list-multiple-values }
Wenn Sie einen Query-Parameter explizit mit `Query` definieren, können Sie ihn auch so deklarieren, dass er eine Liste von Werten empfängt, oder anders gesagt, dass er mehrere Werte empfangen kann.
Um zum Beispiel einen Query-Parameter `q` zu deklarieren, der mehrmals in der URL vorkommen kann, schreiben Sie:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Dann, mit einer URL wie:
```
http://localhost:8000/items/?q=foo&q=bar
```
würden Sie die mehreren `q`-*Query-Parameter*-Werte (`foo` und `bar`) in einer Python-`list` in Ihrer *Pfadoperation-Funktion* im *Funktionsparameter* `q` erhalten.
So wäre die Response zu dieser URL:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Tipp
Um einen Query-Parameter mit einem Typ `list` zu deklarieren, wie im obigen Beispiel, müssen Sie explizit `Query` verwenden, da er andernfalls als Requestbody interpretiert würde.
///
Die interaktive API-Dokumentation wird entsprechend aktualisiert, um mehrere Werte zu erlauben:
### Query-Parameter-Liste / Mehrere Werte mit Defaults { #query-parameter-list-multiple-values-with-defaults }
Sie können auch eine Default-`list` von Werten definieren, wenn keine bereitgestellt werden:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Wenn Sie zu:
```
http://localhost:8000/items/
```
gehen, wird der Default für `q` sein: `["foo", "bar"]`, und Ihre Response wird sein:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Nur `list` verwenden { #using-just-list }
Sie können auch `list` direkt verwenden, anstelle von `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Hinweis
Beachten Sie, dass FastAPI in diesem Fall den Inhalt der Liste nicht überprüft.
Zum Beispiel würde `list[int]` überprüfen (und dokumentieren), dass der Inhalt der Liste Ganzzahlen sind. Aber `list` alleine würde das nicht.
///
## Mehr Metadaten deklarieren { #declare-more-metadata }
Sie können mehr Informationen über den Parameter hinzufügen.
Diese Informationen werden in das generierte OpenAPI aufgenommen und von den Dokumentationsoberflächen und externen Tools verwendet.
/// note | Hinweis
Beachten Sie, dass verschiedene Tools möglicherweise unterschiedliche Unterstützungslevels für OpenAPI haben.
Einige davon könnten noch nicht alle zusätzlichen Informationen anzuzeigen, die Sie erklärten, obwohl in den meisten Fällen die fehlende Funktionalität bereits in der Entwicklung geplant ist.
///
Sie können einen `title` hinzufügen:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
Und eine `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Alias-Parameter { #alias-parameters }
Stellen Sie sich vor, Sie möchten, dass der Parameter `item-query` ist.
Wie in:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Aber `item-query` ist kein gültiger Name für eine Variable in Python.
Der am ähnlichsten wäre `item_query`.
Aber Sie benötigen dennoch, dass er genau `item-query` ist ...
Dann können Sie ein `alias` deklarieren, und dieser Alias wird verwendet, um den Parameterwert zu finden:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Parameter als deprecatet ausweisen { #deprecating-parameters }
Nehmen wir an, Ihnen gefällt dieser Parameter nicht mehr.
Sie müssen ihn eine Weile dort belassen, da es Clients gibt, die ihn verwenden, aber Sie möchten, dass die Dokumentation ihn klar als deprecatet anzeigt.
Dann übergeben Sie den Parameter `deprecated=True` an `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
Die Dokumentation wird es so anzeigen:
## Parameter von OpenAPI ausschließen { #exclude-parameters-from-openapi }
Um einen Query-Parameter aus dem generierten OpenAPI-Schema auszuschließen (und somit aus den automatischen Dokumentationssystemen), setzen Sie den Parameter `include_in_schema` von `Query` auf `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Benutzerdefinierte Validierung { #custom-validation }
Es kann Fälle geben, in denen Sie eine **benutzerdefinierte Validierung** durchführen müssen, die nicht mit den oben gezeigten Parametern durchgeführt werden kann.
In diesen Fällen können Sie eine **benutzerdefinierte Validierungsfunktion** verwenden, die nach der normalen Validierung angewendet wird (z. B. nach der Validierung, dass der Wert ein `str` ist).
Sie können dies mit [Pydantics `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) innerhalb von `Annotated` erreichen.
/// tip | Tipp
Pydantic unterstützt auch [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) und andere. 🤓
///
Zum Beispiel überprüft dieser benutzerdefinierte Validator, ob die Artikel-ID mit `isbn-` für eine ISBN-Buchnummer oder mit `imdb-` für eine IMDB-Film-URL-ID beginnt:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Info
Dies ist verfügbar seit Pydantic Version 2 oder höher. 😎
///
/// tip | Tipp
Wenn Sie irgendeine Art von Validierung durchführen müssen, die eine Kommunikation mit einer **externen Komponente** erfordert, wie z. B. einer Datenbank oder einer anderen API, sollten Sie stattdessen **FastAPI-Abhängigkeiten** verwenden. Sie werden diese später kennenlernen.
Diese benutzerdefinierten Validatoren sind für Dinge gedacht, die einfach mit denselben **Daten** überprüft werden können, die im Request bereitgestellt werden.
///
### Dieses Codebeispiel verstehen { #understand-that-code }
Der wichtige Punkt ist einfach die Verwendung von **`AfterValidator` mit einer Funktion innerhalb von `Annotated`**. Fühlen Sie sich frei, diesen Teil zu überspringen. 🤸
---
Aber wenn Sie neugierig auf dieses spezielle Codebeispiel sind und immer noch Spaß haben, hier sind einige zusätzliche Details.
#### Zeichenkette mit `value.startswith()` { #string-with-value-startswith }
Haben Sie bemerkt? Eine Zeichenkette mit `value.startswith()` kann ein Tuple übernehmen, und es wird jeden Wert im Tuple überprüfen:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Ein zufälliges Item { #a-random-item }
Mit `data.items()` erhalten wir ein iterierbares Objekt mit Tupeln, die Schlüssel und Wert für jedes Dictionary-Element enthalten.
Wir konvertieren dieses iterierbare Objekt mit `list(data.items())` in eine richtige `list`.
Dann können wir mit `random.choice()` einen **zufälligen Wert** aus der Liste erhalten, also bekommen wir ein Tuple mit `(id, name)`. Es wird etwas wie `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")` sein.
Dann **weisen wir diese beiden Werte** des Tupels den Variablen `id` und `name` zu.
Wenn der Benutzer also keine Artikel-ID bereitgestellt hat, erhält er trotzdem einen zufälligen Vorschlag.
... wir tun all dies in einer **einzelnen einfachen Zeile**. 🤯 Lieben Sie nicht auch Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Zusammenfassung { #recap }
Sie können zusätzliche Validierungen und Metadaten für Ihre Parameter deklarieren.
Allgemeine Validierungen und Metadaten:
* `alias`
* `title`
* `description`
* `deprecated`
Validierungen, die spezifisch für Strings sind:
* `min_length`
* `max_length`
* `pattern`
Benutzerdefinierte Validierungen mit `AfterValidator`.
In diesen Beispielen haben Sie gesehen, wie Sie Validierungen für `str`-Werte deklarieren.
Sehen Sie sich die nächsten Kapitel an, um zu erfahren, wie Sie Validierungen für andere Typen, wie z. B. Zahlen, deklarieren.
================================================
FILE: docs/de/docs/tutorial/query-params.md
================================================
# Query-Parameter { #query-parameters }
Wenn Sie in Ihrer Funktion andere Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert.
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
Die Query ist die Menge von Schlüssel-Wert-Paaren, die nach dem `?` in einer URL folgen und durch `&`-Zeichen getrennt sind.
Zum Beispiel sind in der URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
... die Query-Parameter:
* `skip`: mit dem Wert `0`
* `limit`: mit dem Wert `10`
Da sie Teil der URL sind, sind sie „naturgemäß“ Strings.
Aber wenn Sie sie mit Python-Typen deklarieren (im obigen Beispiel als `int`), werden sie zu diesem Typ konvertiert und gegen diesen validiert.
Die gleichen Prozesse, die für Pfad-Parameter gelten, werden auch auf Query-Parameter angewendet:
* Editor Unterstützung (natürlich)
* Daten-„Parsen“
* Datenvalidierung
* Automatische Dokumentation
## Defaultwerte { #defaults }
Da Query-Parameter kein fester Teil eines Pfades sind, können sie optional sein und Defaultwerte haben.
Im obigen Beispiel haben sie die Defaultwerte `skip=0` und `limit=10`.
Wenn Sie also zur URL:
```
http://127.0.0.1:8000/items/
```
gehen, so ist das das gleiche wie die URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Aber wenn Sie zum Beispiel zu:
```
http://127.0.0.1:8000/items/?skip=20
```
gehen, werden die Parameterwerte Ihrer Funktion sein:
* `skip=20`: da Sie das in der URL gesetzt haben
* `limit=10`: weil das der Defaultwert ist
## Optionale Parameter { #optional-parameters }
Auf die gleiche Weise können Sie optionale Query-Parameter deklarieren, indem Sie deren Defaultwert auf `None` setzen:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
In diesem Fall wird der Funktionsparameter `q` optional und standardmäßig `None` sein.
/// check | Testen
Beachten Sie auch, dass **FastAPI** intelligent genug ist, um zu erkennen, dass `item_id` ein Pfad-Parameter ist und `q` keiner, daher muss letzteres ein Query-Parameter sein.
///
## Query-Parameter Typkonvertierung { #query-parameter-type-conversion }
Sie können auch `bool`-Typen deklarieren, und sie werden konvertiert:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Wenn Sie nun zu:
```
http://127.0.0.1:8000/items/foo?short=1
```
oder
```
http://127.0.0.1:8000/items/foo?short=True
```
oder
```
http://127.0.0.1:8000/items/foo?short=true
```
oder
```
http://127.0.0.1:8000/items/foo?short=on
```
oder
```
http://127.0.0.1:8000/items/foo?short=yes
```
gehen, oder zu irgendeiner anderen Variante der Groß-/Kleinschreibung (Alles groß, Anfangsbuchstabe groß, usw.), dann wird Ihre Funktion den Parameter `short` mit dem `bool`-Wert `True` sehen, ansonsten mit dem Wert `False`.
## Mehrere Pfad- und Query-Parameter { #multiple-path-and-query-parameters }
Sie können mehrere Pfad-Parameter und Query-Parameter gleichzeitig deklarieren, **FastAPI** weiß, welches welcher ist.
Und Sie müssen sie auch nicht in einer spezifischen Reihenfolge deklarieren.
Parameter werden anhand ihres Namens erkannt:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Erforderliche Query-Parameter { #required-query-parameters }
Wenn Sie einen Defaultwert für Nicht-Pfad-Parameter deklarieren (Bis jetzt haben wir nur Query-Parameter gesehen), dann ist der Parameter nicht erforderlich.
Wenn Sie keinen spezifischen Wert haben wollen, sondern der Parameter einfach optional sein soll, dann setzen Sie den Defaultwert auf `None`.
Aber wenn Sie wollen, dass ein Query-Parameter erforderlich ist, vergeben Sie einfach keinen Defaultwert:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Hier ist `needy` ein erforderlicher Query-Parameter vom Typ `str`.
Wenn Sie in Ihrem Browser eine URL wie:
```
http://127.0.0.1:8000/items/foo-item
```
... öffnen, ohne den benötigten Parameter `needy`, dann erhalten Sie einen Fehler wie den folgenden:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Da `needy` ein erforderlicher Parameter ist, müssen Sie ihn in der URL setzen:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
... Das funktioniert:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
Und natürlich können Sie einige Parameter als erforderlich, einige mit Defaultwert, und einige als vollständig optional definieren:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
In diesem Fall gibt es drei Query-Parameter:
* `needy`, ein erforderlicher `str`.
* `skip`, ein `int` mit einem Defaultwert `0`.
* `limit`, ein optionales `int`.
/// tip | Tipp
Sie können auch `Enum`s verwenden, auf die gleiche Weise wie mit [Pfad-Parametern](path-params.md#predefined-values).
///
================================================
FILE: docs/de/docs/tutorial/request-files.md
================================================
# Dateien im Request { #request-files }
Sie können Dateien, die vom Client hochgeladen werden, mithilfe von `File` definieren.
/// info | Info
Um hochgeladene Dateien zu empfangen, installieren Sie zuerst [`python-multipart`](https://github.com/Kludex/python-multipart).
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und dann das Paket installieren, zum Beispiel:
```console
$ pip install python-multipart
```
Das liegt daran, dass hochgeladene Dateien als „Formulardaten“ gesendet werden.
///
## `File` importieren { #import-file }
Importieren Sie `File` und `UploadFile` von `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## `File`-Parameter definieren { #define-file-parameters }
Erstellen Sie Datei-Parameter, so wie Sie es auch mit `Body` und `Form` machen würden:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Info
`File` ist eine Klasse, die direkt von `Form` erbt.
Aber erinnern Sie sich, dass, wenn Sie `Query`, `Path`, `File` und andere von `fastapi` importieren, diese tatsächlich Funktionen sind, welche spezielle Klassen zurückgeben.
///
/// tip | Tipp
Um Dateibodys zu deklarieren, müssen Sie `File` verwenden, da diese Parameter sonst als Query-Parameter oder Body (JSON)-Parameter interpretiert werden würden.
///
Die Dateien werden als „Formulardaten“ hochgeladen.
Wenn Sie den Typ Ihrer *Pfadoperation-Funktion* als `bytes` deklarieren, wird **FastAPI** die Datei für Sie auslesen, und Sie erhalten den Inhalt als `bytes`.
Bedenken Sie, dass das bedeutet, dass sich der gesamte Inhalt der Datei im Arbeitsspeicher befindet. Das wird für kleinere Dateien gut funktionieren.
Aber es gibt viele Fälle, in denen Sie davon profitieren, `UploadFile` zu verwenden.
## Datei-Parameter mit `UploadFile` { #file-parameters-with-uploadfile }
Definieren Sie einen Datei-Parameter mit dem Typ `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
`UploadFile` zu verwenden, hat mehrere Vorzüge gegenüber `bytes`:
* Sie müssen `File()` nicht als Parameter-Defaultwert verwenden.
* Es wird eine „gespoolte“ Datei verwendet:
* Eine Datei, die bis zu einem bestimmten Größen-Limit im Arbeitsspeicher behalten wird, und wenn das Limit überschritten wird, auf der Festplatte gespeichert wird.
* Das bedeutet, es wird für große Dateien wie Bilder, Videos, große Binärdateien, usw. gut funktionieren, ohne den ganzen Arbeitsspeicher aufzubrauchen.
* Sie können Metadaten aus der hochgeladenen Datei auslesen.
* Es hat eine [dateiartige](https://docs.python.org/3/glossary.html#term-file-like-object) `async`hrone Schnittstelle.
* Es stellt ein tatsächliches Python-[`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile)-Objekt bereit, welches Sie direkt anderen Bibliotheken übergeben können, die ein dateiartiges Objekt erwarten.
### `UploadFile` { #uploadfile }
`UploadFile` hat die folgenden Attribute:
* `filename`: Ein `str` mit dem ursprünglichen Namen der hochgeladenen Datei (z. B. `meinbild.jpg`).
* `content_type`: Ein `str` mit dem Inhaltstyp (MIME-Typ / Medientyp) (z. B. `image/jpeg`).
* `file`: Ein [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (ein [dateiartiges](https://docs.python.org/3/glossary.html#term-file-like-object) Objekt). Das ist das tatsächliche Python-Objekt, das Sie direkt anderen Funktionen oder Bibliotheken übergeben können, welche ein „file-like“-Objekt erwarten.
`UploadFile` hat die folgenden `async`hronen Methoden. Sie alle rufen die entsprechenden Methoden des darunterliegenden Datei-Objekts auf (wobei intern `SpooledTemporaryFile` verwendet wird).
* `write(daten)`: Schreibt `daten` (`str` oder `bytes`) in die Datei.
* `read(anzahl)`: Liest `anzahl` (`int`) bytes/Zeichen aus der Datei.
* `seek(versatz)`: Geht zur Position `versatz` (`int`) in der Datei.
* z. B. würde `await myfile.seek(0)` zum Anfang der Datei gehen.
* Das ist besonders dann nützlich, wenn Sie `await myfile.read()` einmal ausführen und dann diese Inhalte erneut auslesen müssen.
* `close()`: Schließt die Datei.
Da alle diese Methoden `async`hron sind, müssen Sie sie „await“en („erwarten“).
Zum Beispiel können Sie innerhalb einer `async` *Pfadoperation-Funktion* den Inhalt wie folgt auslesen:
```Python
contents = await myfile.read()
```
Wenn Sie sich innerhalb einer normalen `def`-*Pfadoperation-Funktion* befinden, können Sie direkt auf `UploadFile.file` zugreifen, zum Beispiel:
```Python
contents = myfile.file.read()
```
/// note | Technische Details zu `async`
Wenn Sie die `async`-Methoden verwenden, führt **FastAPI** die Datei-Methoden in einem Threadpool aus und erwartet sie.
///
/// note | Technische Details zu Starlette
FastAPIs `UploadFile` erbt direkt von Starlettes `UploadFile`, fügt aber ein paar notwendige Teile hinzu, um es kompatibel mit **Pydantic** und anderen Teilen von FastAPI zu machen.
///
## Was sind „Formulardaten“ { #what-is-form-data }
Der Weg, wie HTML-Formulare (``) die Daten zum Server senden, verwendet normalerweise eine „spezielle“ Kodierung für diese Daten. Diese unterscheidet sich von JSON.
**FastAPI** stellt sicher, dass diese Daten korrekt ausgelesen werden, statt JSON zu erwarten.
/// note | Technische Details
Daten aus Formularen werden, wenn es keine Dateien sind, normalerweise mit dem „media type“ `application/x-www-form-urlencoded` kodiert.
Sollte das Formular aber Dateien enthalten, dann werden diese mit `multipart/form-data` kodiert. Wenn Sie `File` verwenden, wird **FastAPI** wissen, dass es die Dateien vom korrekten Teil des Bodys holen muss.
Wenn Sie mehr über diese Kodierungen und Formularfelder lesen möchten, besuchen Sie die [MDN-Webdokumentation für `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Achtung
Sie können mehrere `File`- und `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `multipart/form-data` statt `application/json` kodiert.
Das ist keine Limitation von **FastAPI**, sondern Teil des HTTP-Protokolls.
///
## Optionaler Datei-Upload { #optional-file-upload }
Sie können eine Datei optional machen, indem Sie Standard-Typannotationen verwenden und den Defaultwert auf `None` setzen:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` mit zusätzlichen Metadaten { #uploadfile-with-additional-metadata }
Sie können auch `File()` mit `UploadFile` verwenden, um zum Beispiel zusätzliche Metadaten zu setzen:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Mehrere Datei-Uploads { #multiple-file-uploads }
Es ist auch möglich, mehrere Dateien gleichzeitig hochzuladen.
Diese werden demselben Formularfeld zugeordnet, welches mit den Formulardaten gesendet wird.
Um das zu machen, deklarieren Sie eine Liste von `bytes` oder `UploadFile`s:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Sie erhalten, wie deklariert, eine `list` von `bytes` oder `UploadFile`s.
/// note | Technische Details
Sie können auch `from starlette.responses import HTMLResponse` verwenden.
**FastAPI** bietet dieselben `starlette.responses` auch via `fastapi.responses` an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.
///
### Mehrere Datei-Uploads mit zusätzlichen Metadaten { #multiple-file-uploads-with-additional-metadata }
Und so wie zuvor können Sie `File()` verwenden, um zusätzliche Parameter zu setzen, sogar für `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Zusammenfassung { #recap }
Verwenden Sie `File`, `bytes` und `UploadFile`, um hochladbare Dateien im Request zu deklarieren, die als Formulardaten gesendet werden.
================================================
FILE: docs/de/docs/tutorial/request-form-models.md
================================================
# Formularmodelle { #form-models }
Sie können **Pydantic-Modelle** verwenden, um **Formularfelder** in FastAPI zu deklarieren.
/// info | Info
Um Formulare zu verwenden, installieren Sie zuerst [`python-multipart`](https://github.com/Kludex/python-multipart).
Stellen Sie sicher, dass Sie eine [Virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und es dann installieren, zum Beispiel:
```console
$ pip install python-multipart
```
///
/// note | Hinweis
Dies wird seit FastAPI Version `0.113.0` unterstützt. 🤓
///
## Pydantic-Modelle für Formulare { #pydantic-models-for-forms }
Sie müssen nur ein **Pydantic-Modell** mit den Feldern deklarieren, die Sie als **Formularfelder** erhalten möchten, und dann den Parameter als `Form` deklarieren:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** wird die Daten für **jedes Feld** aus den **Formulardaten** im Request **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell übergeben.
## Die Dokumentation testen { #check-the-docs }
Sie können dies in der Dokumentations-UI unter `/docs` testen:
## Zusätzliche Formularfelder verbieten { #forbid-extra-form-fields }
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die Formularfelder auf nur diejenigen beschränken, die im Pydantic-Modell deklariert sind, und jegliche **zusätzlichen** Felder **verbieten**.
/// note | Hinweis
Dies wird seit FastAPI Version `0.114.0` unterstützt. 🤓
///
Sie können die Modellkonfiguration von Pydantic verwenden, um jegliche `extra` Felder zu `verbieten`:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Wenn ein Client versucht, einige zusätzliche Daten zu senden, erhält er eine **Error-Response**.
Zum Beispiel, wenn der Client versucht, folgende Formularfelder zu senden:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
erhält er eine Error-Response, die ihm mitteilt, dass das Feld `extra` nicht erlaubt ist:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Zusammenfassung { #summary }
Sie können Pydantic-Modelle verwenden, um Formularfelder in FastAPI zu deklarieren. 😎
================================================
FILE: docs/de/docs/tutorial/request-forms-and-files.md
================================================
# Formulardaten und Dateien im Request { #request-forms-and-files }
Sie können gleichzeitig Dateien und Formulardaten mit `File` und `Form` definieren.
/// info | Info
Um hochgeladene Dateien und/oder Formulardaten zu empfangen, installieren Sie zuerst [`python-multipart`](https://github.com/Kludex/python-multipart).
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, diese aktivieren und es dann installieren, z. B.:
```console
$ pip install python-multipart
```
///
## `File` und `Form` importieren { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## `File` und `Form`-Parameter definieren { #define-file-and-form-parameters }
Erstellen Sie Datei- und Formularparameter, so wie Sie es auch mit `Body` oder `Query` machen würden:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Die Datei- und Formularfelder werden als Formulardaten hochgeladen, und Sie erhalten diese Dateien und Formularfelder.
Und Sie können einige der Dateien als `bytes` und einige als `UploadFile` deklarieren.
/// warning | Achtung
Sie können mehrere `File`- und `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht auch `Body`-Felder deklarieren, die Sie als JSON erwarten, da der Body des Request mittels `multipart/form-data` statt `application/json` kodiert sein wird.
Das ist keine Limitation von **FastAPI**, sondern Teil des HTTP-Protokolls.
///
## Zusammenfassung { #recap }
Verwenden Sie `File` und `Form` zusammen, wenn Sie Daten und Dateien zusammen im selben Request empfangen müssen.
================================================
FILE: docs/de/docs/tutorial/request-forms.md
================================================
# Formulardaten { #form-data }
Wenn Sie Felder aus Formularen statt JSON empfangen müssen, können Sie `Form` verwenden.
/// info | Info
Um Formulare zu verwenden, installieren Sie zuerst [`python-multipart`](https://github.com/Kludex/python-multipart).
Erstellen Sie unbedingt eine [virtuelle Umgebung](../virtual-environments.md), aktivieren Sie diese und installieren Sie dann das Paket, zum Beispiel:
```console
$ pip install python-multipart
```
///
## `Form` importieren { #import-form }
Importieren Sie `Form` von `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## `Form`-Parameter definieren { #define-form-parameters }
Erstellen Sie Formular-Parameter, so wie Sie es auch mit `Body` und `Query` machen würden:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Zum Beispiel stellt eine der Möglichkeiten, die OAuth2-Spezifikation zu verwenden (genannt „password flow“), die Bedingung, einen `username` und ein `password` als Formularfelder zu senden.
Die Spezifikation erfordert, dass die Felder exakt `username` und `password` genannt werden und als Formularfelder, nicht JSON, gesendet werden.
Mit `Form` haben Sie die gleichen Konfigurationsmöglichkeiten wie mit `Body` (und `Query`, `Path`, `Cookie`), inklusive Validierung, Beispielen, einem Alias (z. B. `user-name` statt `username`), usw.
/// info | Info
`Form` ist eine Klasse, die direkt von `Body` erbt.
///
/// tip | Tipp
Um Formularbodys zu deklarieren, verwenden Sie explizit `Form`, da diese Parameter sonst als Query-Parameter oder Body (JSON)-Parameter interpretiert werden würden.
///
## Über „Formularfelder“ { #about-form-fields }
HTML-Formulare (``) senden die Daten in einer „speziellen“ Kodierung zum Server, die sich von JSON unterscheidet.
**FastAPI** stellt sicher, dass diese Daten korrekt ausgelesen werden, statt JSON zu erwarten.
/// note | Technische Details
Daten aus Formularen werden normalerweise mit dem „media type“ `application/x-www-form-urlencoded` kodiert.
Wenn das Formular stattdessen Dateien enthält, werden diese mit `multipart/form-data` kodiert. Im nächsten Kapitel erfahren Sie mehr über die Handhabung von Dateien.
Wenn Sie mehr über Formularfelder und ihre Kodierungen lesen möchten, besuchen Sie die [MDN-Webdokumentation für `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Achtung
Sie können mehrere `Form`-Parameter in einer *Pfadoperation* deklarieren, aber Sie können nicht gleichzeitig auch `Body`-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels `application/x-www-form-urlencoded` statt `application/json` kodiert.
Das ist keine Limitation von **FastAPI**, sondern Teil des HTTP-Protokolls.
///
## Zusammenfassung { #recap }
Verwenden Sie `Form`, um Eingabe-Parameter für Formulardaten zu deklarieren.
================================================
FILE: docs/de/docs/tutorial/response-model.md
================================================
# Responsemodell – Rückgabetyp { #response-model-return-type }
Sie können den Typ der Response deklarieren, indem Sie den **Rückgabetyp** der *Pfadoperation* annotieren.
Hierbei können Sie **Typannotationen** genauso verwenden, wie Sie es bei Werten von Funktions-**Parametern** machen; verwenden Sie Pydantic-Modelle, Listen, Dicts und skalare Werte wie Nummern, Booleans, usw.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI wird diesen Rückgabetyp verwenden, um:
* Die zurückzugebenden Daten zu **validieren**.
* Wenn die Daten ungültig sind (Sie haben z. B. ein Feld vergessen), bedeutet das, *Ihr* Anwendungscode ist fehlerhaft, er gibt nicht zurück, was er sollte, und daher wird ein Server-Error ausgegeben, statt falscher Daten. So können Sie und Ihre Clients sicher sein, dass diese die erwarteten Daten, in der richtigen Form erhalten.
* In der OpenAPI *Pfadoperation* ein **JSON-Schema** für die Response hinzuzufügen.
* Dieses wird von der **automatischen Dokumentation** verwendet.
* Es wird auch von automatisch Client-Code-generierenden Tools verwendet.
* Die zurückgegebenen Daten mit Pydantic zu **serialisieren** (zu JSON). Pydantic ist in **Rust** geschrieben und daher **viel schneller**.
Aber am wichtigsten:
* Es wird die Ausgabedaten auf das **limitieren und filtern**, was im Rückgabetyp definiert ist.
* Das ist insbesondere für die **Sicherheit** wichtig, mehr dazu unten.
## `response_model`-Parameter { #response-model-parameter }
Es gibt Fälle, da möchten oder müssen Sie Daten zurückgeben, die nicht genau dem entsprechen, was der Typ deklariert.
Zum Beispiel könnten Sie **ein Dictionary zurückgeben** wollen, oder ein Datenbank-Objekt, aber **es als Pydantic-Modell deklarieren**. Auf diese Weise übernimmt das Pydantic-Modell alle Datendokumentation, -validierung, usw. für das Objekt, welches Sie zurückgeben (z. B. ein Dictionary oder ein Datenbank-Objekt).
Würden Sie eine hierfür eine Rückgabetyp-Annotation verwenden, dann würden Tools und Editoren (korrekterweise) Fehler ausgeben, die Ihnen sagen, dass Ihre Funktion einen Typ zurückgibt (z. B. ein Dict), der sich unterscheidet von dem, was Sie deklariert haben (z. B. ein Pydantic-Modell).
In solchen Fällen können Sie statt des Rückgabetyps den **Pfadoperation-Dekorator**-Parameter `response_model` verwenden.
Sie können `response_model` in jeder möglichen *Pfadoperation* verwenden:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* usw.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Hinweis
Beachten Sie, dass `response_model` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, so wie die anderen Parameter und der Body.
///
`response_model` nimmt denselben Typ entgegen, den Sie auch für ein Pydantic-Modellfeld deklarieren würden, also etwa ein Pydantic-Modell, aber es kann auch z. B. eine `list`e von Pydantic-Modellen sein, wie etwa `List[Item]`.
FastAPI wird dieses `response_model` nehmen, um die Daten zu dokumentieren, validieren, usw. und auch, um **die Ausgabedaten** entsprechend der Typdeklaration **zu konvertieren und filtern**.
/// tip | Tipp
Wenn Sie in Ihrem Editor strikte Typchecks haben, mypy, usw., können Sie den Funktions-Rückgabetyp als `Any` deklarieren.
So sagen Sie dem Editor, dass Sie absichtlich *irgendetwas* zurückgeben. Aber FastAPI wird trotzdem die Dokumentation, Validierung, Filterung, usw. der Daten übernehmen, via `response_model`.
///
### `response_model`-Priorität { #response-model-priority }
Wenn sowohl Rückgabetyp als auch `response_model` deklariert sind, hat `response_model` die Priorität und wird von FastAPI bevorzugt verwendet.
So können Sie korrekte Typannotationen zu Ihrer Funktion hinzufügen, die von Ihrem Editor und Tools wie mypy verwendet werden. Und dennoch übernimmt FastAPI die Validierung und Dokumentation, usw., der Daten anhand von `response_model`.
Sie können auch `response_model=None` verwenden, um das Erstellen eines Responsemodells für diese *Pfadoperation* zu unterbinden. Sie könnten das tun wollen, wenn Sie Dinge annotieren, die nicht gültige Pydantic-Felder sind. Ein Beispiel dazu werden Sie in einer der Abschnitte unten sehen.
## Dieselben Eingabedaten zurückgeben { #return-the-same-input-data }
Im Folgenden deklarieren wir ein `UserIn`-Modell; es enthält ein Klartext-Passwort:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Info
Um `EmailStr` zu verwenden, installieren Sie zuerst [`email-validator`](https://github.com/JoshData/python-email-validator).
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und es dann installieren, zum Beispiel:
```console
$ pip install email-validator
```
oder mit:
```console
$ pip install "pydantic[email]"
```
///
Wir verwenden dieses Modell, um sowohl unsere Eingabe- als auch Ausgabedaten zu deklarieren:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Immer wenn jetzt ein Browser einen Benutzer mit Passwort erzeugt, gibt die API dasselbe Passwort in der Response zurück.
Hier ist das möglicherweise kein Problem, da es derselbe Benutzer ist, der das Passwort sendet.
Aber wenn wir dasselbe Modell für eine andere *Pfadoperation* verwenden, könnten wir das Passwort dieses Benutzers zu jedem Client schicken.
/// danger | Gefahr
Speichern Sie niemals das Klartext-Passwort eines Benutzers, oder versenden Sie es in einer Response wie dieser, wenn Sie sich nicht der resultierenden Gefahren bewusst sind und nicht wissen, was Sie tun.
///
## Ausgabemodell hinzufügen { #add-an-output-model }
Wir können stattdessen ein Eingabemodell mit dem Klartext-Passwort, und ein Ausgabemodell ohne das Passwort erstellen:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Obwohl unsere *Pfadoperation-Funktion* hier denselben `user` von der Eingabe zurückgibt, der das Passwort enthält:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
... haben wir deklariert, dass `response_model` das Modell `UserOut` ist, welches das Passwort nicht enthält:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Darum wird **FastAPI** sich darum kümmern, dass alle Daten, die nicht im Ausgabemodell deklariert sind, herausgefiltert werden (mittels Pydantic).
### `response_model` oder Rückgabewert { #response-model-or-return-type }
Da unsere zwei Modelle in diesem Fall unterschiedlich sind, würde, wenn wir den Rückgabewert der Funktion als `UserOut` deklarieren, der Editor sich beschweren, dass wir einen ungültigen Typ zurückgeben, weil das unterschiedliche Klassen sind.
Darum müssen wir es in diesem Fall im `response_model`-Parameter deklarieren.
... aber lesen Sie weiter, um zu sehen, wie man das anders lösen kann.
## Rückgabewert und Datenfilterung { #return-type-and-data-filtering }
Führen wir unser vorheriges Beispiel fort. Wir wollten **die Funktion mit einem Typ annotieren**, aber wir wollten in der Funktion tatsächlich etwas zurückgeben, das **mehr Daten** enthält.
Wir möchten, dass FastAPI die Daten weiterhin mithilfe des Responsemodells **filtert**. Selbst wenn die Funktion mehr Daten zurückgibt, soll die Response nur die Felder enthalten, die im Responsemodell deklariert sind.
Im vorherigen Beispiel mussten wir den `response_model`-Parameter verwenden, weil die Klassen unterschiedlich waren. Das bedeutet aber auch, wir bekommen keine Unterstützung vom Editor und anderen Tools, die den Funktions-Rückgabewert überprüfen.
Aber in den meisten Fällen, wenn wir so etwas machen, wollen wir nur, dass das Modell einige der Daten **filtert/entfernt**, so wie in diesem Beispiel.
Und in solchen Fällen können wir Klassen und Vererbung verwenden, um Vorteil aus den Typannotationen in der Funktion zu ziehen, was vom Editor und von Tools besser unterstützt wird, während wir gleichzeitig FastAPIs **Datenfilterung** behalten.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Damit erhalten wir Tool-Unterstützung, vom Editor und mypy, da dieser Code hinsichtlich der Typen korrekt ist, aber wir erhalten auch die Datenfilterung von FastAPI.
Wie funktioniert das? Schauen wir uns das mal an. 🤓
### Typannotationen und Tooling { #type-annotations-and-tooling }
Sehen wir uns zunächst an, wie Editor, mypy und andere Tools dies sehen würden.
`BaseUser` verfügt über die Basis-Felder. Dann erbt `UserIn` von `BaseUser` und fügt das Feld `password` hinzu, sodass es nun alle Felder beider Modelle hat.
Wir annotieren den Funktionsrückgabetyp als `BaseUser`, geben aber tatsächlich eine `UserIn`-Instanz zurück.
Für den Editor, mypy und andere Tools ist das kein Problem, da `UserIn` eine Unterklasse von `BaseUser` ist (Salopp: `UserIn` ist ein `BaseUser`). Es handelt sich um einen *gültigen* Typ, solange irgendetwas überreicht wird, das ein `BaseUser` ist.
### FastAPI Datenfilterung { #fastapi-data-filtering }
FastAPI seinerseits wird den Rückgabetyp sehen und sicherstellen, dass das, was zurückgegeben wird, **nur** diejenigen Felder enthält, welche im Typ deklariert sind.
FastAPI macht intern mehrere Dinge mit Pydantic, um sicherzustellen, dass obige Ähnlichkeitsregeln der Klassenvererbung nicht auf die Filterung der zurückgegebenen Daten angewendet werden, sonst könnten Sie am Ende mehr Daten zurückgeben als gewollt.
Auf diese Weise erhalten Sie das beste beider Welten: Sowohl Typannotationen mit **Tool-Unterstützung** als auch **Datenfilterung**.
## Anzeige in der Dokumentation { #see-it-in-the-docs }
Wenn Sie sich die automatische Dokumentation betrachten, können Sie sehen, dass Eingabe- und Ausgabemodell beide ihr eigenes JSON-Schema haben:
Und beide Modelle werden auch in der interaktiven API-Dokumentation verwendet:
## Andere Rückgabetyp-Annotationen { #other-return-type-annotations }
Es kann Fälle geben, bei denen Sie etwas zurückgeben, das kein gültiges Pydantic-Feld ist, und Sie annotieren es in der Funktion nur, um Unterstützung von Tools zu erhalten (Editor, mypy, usw.).
### Eine Response direkt zurückgeben { #return-a-response-directly }
Der häufigste Anwendungsfall ist, wenn Sie [eine Response direkt zurückgeben, wie es später im Handbuch für fortgeschrittene Benutzer erläutert wird](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Dieser einfache Anwendungsfall wird automatisch von FastAPI gehandhabt, weil die Annotation des Rückgabetyps die Klasse (oder eine Unterklasse von) `Response` ist.
Und Tools werden auch glücklich sein, weil sowohl `RedirectResponse` als auch `JSONResponse` Unterklassen von `Response` sind, die Typannotation ist daher korrekt.
### Eine Unterklasse von Response annotieren { #annotate-a-response-subclass }
Sie können auch eine Unterklasse von `Response` in der Typannotation verwenden.
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Das wird ebenfalls funktionieren, weil `RedirectResponse` eine Unterklasse von `Response` ist, und FastAPI sich um diesen einfachen Anwendungsfall automatisch kümmert.
### Ungültige Rückgabetyp-Annotationen { #invalid-return-type-annotations }
Aber wenn Sie ein beliebiges anderes Objekt zurückgeben, das kein gültiger Pydantic-Typ ist (z. B. ein Datenbank-Objekt), und Sie annotieren es so in der Funktion, wird FastAPI versuchen, ein Pydantic-Responsemodell von dieser Typannotation zu erstellen, und scheitern.
Das gleiche wird passieren, wenn Sie eine Union mehrerer Typen haben, und einer oder mehrere sind nicht gültige Pydantic-Typen. Zum Beispiel funktioniert folgendes nicht 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
... das scheitert, da die Typannotation kein Pydantic-Typ ist, und auch keine einzelne `Response`-Klasse, oder -Unterklasse, es ist eine Union (eines von beiden) von `Response` und `dict`.
### Responsemodell deaktivieren { #disable-response-model }
Beim Beispiel oben fortsetzend, mögen Sie vielleicht die standardmäßige Datenvalidierung, -Dokumentation, -Filterung, usw., die von FastAPI durchgeführt wird, nicht haben.
Aber Sie möchten dennoch den Rückgabetyp in der Funktion annotieren, um Unterstützung von Editoren und Typcheckern (z. B. mypy) zu erhalten.
In diesem Fall können Sie die Generierung des Responsemodells abschalten, indem Sie `response_model=None` setzen:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Das bewirkt, dass FastAPI die Generierung des Responsemodells unterlässt, und damit können Sie jede gewünschte Rückgabetyp-Annotation haben, ohne dass es Ihre FastAPI-Anwendung beeinflusst. 🤓
## Parameter für die Enkodierung des Responsemodells { #response-model-encoding-parameters }
Ihr Responsemodell könnte Defaultwerte haben, wie:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (oder `str | None = None` in Python 3.10) hat einen Defaultwert `None`.
* `tax: float = 10.5` hat einen Defaultwert `10.5`.
* `tags: List[str] = []` hat eine leere Liste als Defaultwert: `[]`.
Aber Sie möchten diese vielleicht vom Resultat ausschließen, wenn Sie gar nicht gesetzt wurden.
Wenn Sie zum Beispiel Modelle mit vielen optionalen Attributen in einer NoSQL-Datenbank haben, und Sie möchten nicht ellenlange JSON-Responses voller Defaultwerte senden.
### Den `response_model_exclude_unset`-Parameter verwenden { #use-the-response-model-exclude-unset-parameter }
Sie können den *Pfadoperation-Dekorator*-Parameter `response_model_exclude_unset=True` setzen:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
Die Defaultwerte werden dann nicht in der Response enthalten sein, sondern nur die tatsächlich gesetzten Werte.
Wenn Sie also den Artikel mit der ID `foo` bei der *Pfadoperation* anfragen, wird (ohne die Defaultwerte) die Response sein:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Info
Sie können auch:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
verwenden, wie in der [Pydantic-Dokumentation](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) für `exclude_defaults` und `exclude_none` beschrieben.
///
#### Daten mit Werten für Felder mit Defaultwerten { #data-with-values-for-fields-with-defaults }
Aber wenn Ihre Daten Werte für Modellfelder mit Defaultwerten haben, wie etwa der Artikel mit der ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
dann werden diese Werte in der Response enthalten sein.
#### Daten mit den gleichen Werten wie die Defaultwerte { #data-with-the-same-values-as-the-defaults }
Wenn Daten die gleichen Werte haben wie ihre Defaultwerte, wie etwa der Artikel mit der ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
dann ist FastAPI klug genug (tatsächlich ist Pydantic klug genug) zu erkennen, dass, obwohl `description`, `tax`, und `tags` die gleichen Werte haben wie ihre Defaultwerte, sie explizit gesetzt wurden (statt dass sie von den Defaultwerten genommen wurden).
Diese Felder werden also in der JSON-Response enthalten sein.
/// tip | Tipp
Beachten Sie, dass Defaultwerte alles Mögliche sein können, nicht nur `None`.
Sie können eine Liste (`[]`), ein `float` `10.5`, usw. sein.
///
### `response_model_include` und `response_model_exclude` { #response-model-include-and-response-model-exclude }
Sie können auch die Parameter `response_model_include` und `response_model_exclude` im **Pfadoperation-Dekorator** verwenden.
Diese nehmen ein `set` von `str`s entgegen, welches Namen von Attributen sind, die eingeschlossen (ohne die Anderen) oder ausgeschlossen (nur die Anderen) werden sollen.
Das kann als schnelle Abkürzung verwendet werden, wenn Sie nur ein Pydantic-Modell haben und ein paar Daten von der Ausgabe ausschließen wollen.
/// tip | Tipp
Es wird dennoch empfohlen, dass Sie die Ideen von oben verwenden, also mehrere Klassen statt dieser Parameter.
Der Grund ist, dass das das generierte JSON-Schema in der OpenAPI Ihrer Anwendung (und deren Dokumentation) dennoch das komplette Modell abbildet, selbst wenn Sie `response_model_include` oder `response_model_exclude` verwenden, um einige Attribute auszuschließen.
Das trifft auch auf `response_model_by_alias` zu, welches ähnlich funktioniert.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Tipp
Die Syntax `{"name", "description"}` erzeugt ein `set` mit diesen zwei Werten.
Äquivalent zu `set(["name", "description"])`.
///
#### `list`en statt `set`s verwenden { #using-lists-instead-of-sets }
Wenn Sie vergessen, ein `set` zu verwenden, und stattdessen eine `list`e oder ein `tuple` übergeben, wird FastAPI die dennoch in ein `set` konvertieren, und es wird korrekt funktionieren:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Zusammenfassung { #recap }
Verwenden Sie den Parameter `response_model` im *Pfadoperation-Dekorator*, um Responsemodelle zu definieren, und besonders, um private Daten herauszufiltern.
Verwenden Sie `response_model_exclude_unset`, um nur explizit gesetzte Werte zurückzugeben.
================================================
FILE: docs/de/docs/tutorial/response-status-code.md
================================================
# Response-Statuscode { #response-status-code }
Genauso wie Sie ein Responsemodell angeben können, können Sie auch den HTTP-Statuscode für die Response mit dem Parameter `status_code` in jeder der *Pfadoperationen* deklarieren:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* usw.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Hinweis
Beachten Sie, dass `status_code` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, wie alle anderen Parameter und der Body.
///
Dem `status_code`-Parameter wird eine Zahl mit dem HTTP-Statuscode übergeben.
/// info | Info
Alternativ kann `status_code` auch ein `IntEnum` erhalten, wie etwa Pythons [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus).
///
Dies wird:
* Diesen Statuscode mit der Response zurücksenden.
* Diesen im OpenAPI-Schema dokumentieren (und somit in den Benutzeroberflächen):
/// note | Hinweis
Einige Responsecodes (siehe nächsten Abschnitt) kennzeichnen, dass die Response keinen Body hat.
FastAPI erkennt dies und erstellt eine OpenAPI-Dokumentation, die zeigt, dass es keinen Responsebody gibt.
///
## Über HTTP-Statuscodes { #about-http-status-codes }
/// note | Hinweis
Wenn Sie bereits wissen, was HTTP-Statuscodes sind, können Sie diesen Abschnitt überspringen und mit dem nächsten fortfahren.
///
In HTTP senden Sie einen numerischen Statuscode mit 3 Ziffern als Teil der Response.
Diese Statuscodes haben einen zugeordneten Namen, um sie leichter zu erkennen, aber der wichtige Teil ist die Zahl.
Kurz gefasst:
* `100 - 199` stehen für „Information“. Sie verwenden diese selten direkt. Responses mit diesen Statuscodes dürfen keinen Body haben.
* **`200 - 299`** stehen für „Successful“-Responses („Erfolgreich“). Diese werden Sie am häufigsten verwenden.
* `200` ist der Default-Statuscode, was bedeutet, alles ist „OK“.
* Ein weiteres Beispiel wäre `201`, „Created“ („Erzeugt“). Dieser wird üblicherweise verwendet, nachdem ein neuer Datensatz in der Datenbank erstellt wurde.
* Ein spezieller Fall ist `204`, „No Content“ („Kein Inhalt“). Diese Response wird verwendet, wenn es keinen Inhalt gibt, der an den Client zurückgeschickt werden soll, und diese Response darf daher keinen Body haben.
* **`300 - 399`** stehen für „Redirection“ („Umleitung“). Responses mit diesen Statuscodes können einen Body haben oder nicht, außer bei `304`, „Not Modified“ („Nicht verändert“), die keinen haben darf.
* **`400 - 499`** stehen für „Client error“-Responses („Client-Fehler“). Diese sind die zweithäufigsten, die Sie vermutlich verwenden werden.
* Ein Beispiel ist `404`, für eine „Not Found“-Response („Nicht gefunden“).
* Für allgemeine Fehler beim Client können Sie einfach `400` verwenden.
* `500 - 599` stehen für Server-Fehler. Diese verwenden Sie fast nie direkt. Wenn in Ihrem Anwendungscode oder Server etwas schiefgeht, wird automatisch einer dieser Fehler-Statuscodes zurückgegeben.
/// tip | Tipp
Um mehr über die einzelnen Statuscodes zu erfahren und welcher wofür verwendet wird, sehen Sie sich die [MDN Dokumentation über HTTP-Statuscodes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) an.
///
## Abkürzung zur Erinnerung an die Namen { #shortcut-to-remember-the-names }
Lassen Sie uns das vorherige Beispiel noch einmal anschauen:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` ist der Statuscode für „Created“ („Erzeugt“).
Aber Sie müssen sich nicht merken, was jeder dieser Codes bedeutet.
Sie können die Annehmlichkeit von Variablen aus `fastapi.status` nutzen.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Diese sind nur eine Annehmlichkeit, sie enthalten dieselbe Zahl, aber so können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
/// note | Technische Details
Sie könnten auch `from starlette import status` verwenden.
**FastAPI** bietet dieselben `starlette.status`-Codes auch via `fastapi.status` an, rein zu Ihrer Annehmlichkeit als Entwickler. Aber sie stammen direkt von Starlette.
///
## Den Defaultwert ändern { #changing-the-default }
Später im [Handbuch für fortgeschrittene Benutzer](../advanced/response-change-status-code.md) werden Sie sehen, wie Sie einen anderen Statuscode zurückgeben können, als den Default, den Sie hier deklarieren.
================================================
FILE: docs/de/docs/tutorial/schema-extra-example.md
================================================
# Beispiel-Request-Daten deklarieren { #declare-request-example-data }
Sie können Beispiele für die Daten deklarieren, die Ihre App empfangen kann.
Hier sind mehrere Möglichkeiten, das zu tun.
## Zusätzliche JSON-Schemadaten in Pydantic-Modellen { #extra-json-schema-data-in-pydantic-models }
Sie können `examples` („Beispiele“) für ein Pydantic-Modell deklarieren, welche dem generierten JSON-Schema hinzugefügt werden.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Diese zusätzlichen Informationen werden unverändert zum für dieses Modell ausgegebenen **JSON-Schema** hinzugefügt und in der API-Dokumentation verwendet.
Sie können das Attribut `model_config` verwenden, das ein `dict` akzeptiert, wie beschrieben in [Pydantic-Dokumentation: Configuration](https://docs.pydantic.dev/latest/api/config/).
Sie können `json_schema_extra` setzen, mit einem `dict`, das alle zusätzlichen Daten enthält, die im generierten JSON-Schema angezeigt werden sollen, einschließlich `examples`.
/// tip | Tipp
Mit derselben Technik können Sie das JSON-Schema erweitern und Ihre eigenen benutzerdefinierten Zusatzinformationen hinzufügen.
Sie könnten das beispielsweise verwenden, um Metadaten für eine Frontend-Benutzeroberfläche usw. hinzuzufügen.
///
/// info | Info
OpenAPI 3.1.0 (verwendet seit FastAPI 0.99.0) hat Unterstützung für `examples` hinzugefügt, was Teil des **JSON Schema** Standards ist.
Zuvor unterstützte es nur das Schlüsselwort `example` mit einem einzigen Beispiel. Dieses wird weiterhin von OpenAPI 3.1.0 unterstützt, ist jedoch deprecatet und nicht Teil des JSON Schema Standards. Wir empfehlen Ihnen daher, von `example` nach `examples` zu migrieren. 🤓
Mehr erfahren Sie am Ende dieser Seite.
///
## Zusätzliche Argumente für `Field` { #field-additional-arguments }
Wenn Sie `Field()` mit Pydantic-Modellen verwenden, können Sie ebenfalls zusätzliche `examples` deklarieren:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` im JSON-Schema – OpenAPI { #examples-in-json-schema-openapi }
Bei Verwendung von:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
können Sie auch eine Gruppe von `examples` mit zusätzlichen Informationen deklarieren, die zu ihren **JSON-Schemas** innerhalb von **OpenAPI** hinzugefügt werden.
### `Body` mit `examples` { #body-with-examples }
Hier übergeben wir `examples`, welches ein einzelnes Beispiel für die in `Body()` erwarteten Daten enthält:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Beispiel in der Dokumentations-Benutzeroberfläche { #example-in-the-docs-ui }
Mit jeder der oben genannten Methoden würde es in `/docs` so aussehen:
### `Body` mit mehreren `examples` { #body-with-multiple-examples }
Sie können natürlich auch mehrere `examples` übergeben:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Wenn Sie das tun, werden die Beispiele Teil des internen **JSON-Schemas** für diese Body-Daten.
Nichtsdestotrotz unterstützt Swagger UI, das für die Anzeige der Dokumentations-Benutzeroberfläche zuständige Tool, zum Zeitpunkt der Erstellung nicht die Anzeige mehrerer Beispiele für die Daten in **JSON Schema**. Aber lesen Sie unten für einen Workaround weiter.
### OpenAPI-spezifische `examples` { #openapi-specific-examples }
Schon bevor **JSON Schema** `examples` unterstützte, unterstützte OpenAPI ein anderes Feld, das auch `examples` genannt wurde.
Diese **OpenAPI-spezifischen** `examples` finden sich in einem anderen Abschnitt der OpenAPI-Spezifikation. Sie sind **Details für jede *Pfadoperation***, nicht für jedes JSON-Schema.
Und Swagger UI unterstützt dieses spezielle Feld `examples` schon seit einiger Zeit. Sie können es also verwenden, um verschiedene **Beispiele in der Benutzeroberfläche der Dokumentation anzuzeigen**.
Das Format dieses OpenAPI-spezifischen Felds `examples` ist ein `dict` mit **mehreren Beispielen** (anstelle einer `list`), jedes mit zusätzlichen Informationen, die auch zu **OpenAPI** hinzugefügt werden.
Dies erfolgt nicht innerhalb jedes in OpenAPI enthaltenen JSON-Schemas, sondern außerhalb, in der *Pfadoperation*.
### Verwendung des Parameters `openapi_examples` { #using-the-openapi-examples-parameter }
Sie können die OpenAPI-spezifischen `examples` in FastAPI mit dem Parameter `openapi_examples` deklarieren, für:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
Die Schlüssel des `dict` identifizieren jedes Beispiel, und jeder Wert ist ein weiteres `dict`.
Jedes spezifische Beispiel-`dict` in den `examples` kann Folgendes enthalten:
* `summary`: Kurze Beschreibung für das Beispiel.
* `description`: Eine lange Beschreibung, die Markdown-Text enthalten kann.
* `value`: Dies ist das tatsächlich angezeigte Beispiel, z. B. ein `dict`.
* `externalValue`: Alternative zu `value`, eine URL, die auf das Beispiel verweist. Allerdings wird dies möglicherweise nicht von so vielen Tools unterstützt wie `value`.
Sie können es so verwenden:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### OpenAPI-Beispiele in der Dokumentations-Benutzeroberfläche { #openapi-examples-in-the-docs-ui }
Wenn `openapi_examples` zu `Body()` hinzugefügt wird, würde `/docs` so aussehen:
## Technische Details { #technical-details }
/// tip | Tipp
Wenn Sie bereits **FastAPI** Version **0.99.0 oder höher** verwenden, können Sie diese Details wahrscheinlich **überspringen**.
Sie sind für ältere Versionen relevanter, bevor OpenAPI 3.1.0 verfügbar war.
Sie können dies als eine kurze **Geschichtsstunde** zu OpenAPI und JSON Schema betrachten. 🤓
///
/// warning | Achtung
Dies sind sehr technische Details zu den Standards **JSON Schema** und **OpenAPI**.
Wenn die oben genannten Ideen bereits für Sie funktionieren, reicht das möglicherweise aus und Sie benötigen diese Details wahrscheinlich nicht, überspringen Sie sie gerne.
///
Vor OpenAPI 3.1.0 verwendete OpenAPI eine ältere und modifizierte Version von **JSON Schema**.
JSON Schema hatte keine `examples`, daher fügte OpenAPI seiner eigenen modifizierten Version ein eigenes `example`-Feld hinzu.
OpenAPI fügte auch die Felder `example` und `examples` zu anderen Teilen der Spezifikation hinzu:
* [`Parameter Object` (in der Spezifikation)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object), das verwendet wurde von FastAPIs:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object` im Feld `content` des `Media Type Object`s (in der Spezifikation)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object), das verwendet wurde von FastAPIs:
* `Body()`
* `File()`
* `Form()`
/// info | Info
Dieser alte, OpenAPI-spezifische `examples`-Parameter heißt seit FastAPI `0.103.0` jetzt `openapi_examples`.
///
### JSON Schemas Feld `examples` { #json-schemas-examples-field }
Aber dann fügte JSON Schema ein [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5)-Feld zu einer neuen Version der Spezifikation hinzu.
Und dann basierte das neue OpenAPI 3.1.0 auf der neuesten Version (JSON Schema 2020-12), die dieses neue Feld `examples` enthielt.
Und jetzt hat dieses neue `examples`-Feld Vorrang vor dem alten (und benutzerdefinierten) `example`-Feld, im Singular, das jetzt deprecatet ist.
Dieses neue `examples`-Feld in JSON Schema ist **nur eine `list`** von Beispielen, kein Dict mit zusätzlichen Metadaten wie an den anderen Stellen in OpenAPI (oben beschrieben).
/// info | Info
Selbst, nachdem OpenAPI 3.1.0 veröffentlicht wurde, mit dieser neuen, einfacheren Integration mit JSON Schema, unterstützte Swagger UI, das Tool, das die automatische Dokumentation bereitstellt, eine Zeit lang OpenAPI 3.1.0 nicht (das tut es seit Version 5.0.0 🎉).
Aus diesem Grund verwendeten Versionen von FastAPI vor 0.99.0 immer noch Versionen von OpenAPI vor 3.1.0.
///
### Pydantic- und FastAPI-`examples` { #pydantic-and-fastapi-examples }
Wenn Sie `examples` innerhalb eines Pydantic-Modells hinzufügen, indem Sie `schema_extra` oder `Field(examples=["something"])` verwenden, wird dieses Beispiel dem **JSON-Schema** für dieses Pydantic-Modell hinzugefügt.
Und dieses **JSON-Schema** des Pydantic-Modells ist in der **OpenAPI** Ihrer API enthalten und wird dann in der Benutzeroberfläche der Dokumentation verwendet.
In Versionen von FastAPI vor 0.99.0 (0.99.0 und höher verwenden das neuere OpenAPI 3.1.0), wenn Sie `example` oder `examples` mit einem der anderen Werkzeuge (`Query()`, `Body()`, usw.) verwendet haben, wurden diese Beispiele nicht zum JSON-Schema hinzugefügt, das diese Daten beschreibt (nicht einmal zur OpenAPI-eigenen Version von JSON Schema), sondern direkt zur *Pfadoperation*-Deklaration in OpenAPI (außerhalb der Teile von OpenAPI, die JSON Schema verwenden).
Aber jetzt, da FastAPI 0.99.0 und höher, OpenAPI 3.1.0 verwendet, das JSON Schema 2020-12 verwendet, und Swagger UI 5.0.0 und höher, ist alles konsistenter und die Beispiele sind in JSON Schema enthalten.
### Swagger-Benutzeroberfläche und OpenAPI-spezifische `examples` { #swagger-ui-and-openapi-specific-examples }
Da die Swagger-Benutzeroberfläche derzeit nicht mehrere JSON Schema Beispiele unterstützt (Stand: 26.08.2023), hatten Benutzer keine Möglichkeit, mehrere Beispiele in der Dokumentation anzuzeigen.
Um dieses Problem zu lösen, hat FastAPI `0.103.0` **Unterstützung** für die Deklaration desselben alten **OpenAPI-spezifischen** `examples`-Felds mit dem neuen Parameter `openapi_examples` hinzugefügt. 🤓
### Zusammenfassung { #summary }
Ich habe immer gesagt, dass ich Geschichte nicht so sehr mag ... und jetzt schauen Sie mich an, wie ich „Technikgeschichte“-Unterricht gebe. 😅
Kurz gesagt: **Aktualisieren Sie auf FastAPI 0.99.0 oder höher**, und die Dinge sind viel **einfacher, konsistenter und intuitiver**, und Sie müssen nicht alle diese historischen Details kennen. 😎
================================================
FILE: docs/de/docs/tutorial/security/first-steps.md
================================================
# Sicherheit – Erste Schritte { #security-first-steps }
Stellen wir uns vor, dass Sie Ihre **Backend**-API auf einer Domain haben.
Und Sie haben ein **Frontend** auf einer anderen Domain oder in einem anderen Pfad derselben Domain (oder in einer Mobile-Anwendung).
Und Sie möchten eine Möglichkeit haben, dass sich das Frontend mithilfe eines **Benutzernamens** und eines **Passworts** beim Backend authentisieren kann.
Wir können **OAuth2** verwenden, um das mit **FastAPI** zu erstellen.
Aber ersparen wir Ihnen die Zeit, die gesamte lange Spezifikation zu lesen, nur um die kleinen Informationen zu finden, die Sie benötigen.
Lassen Sie uns die von **FastAPI** bereitgestellten Tools verwenden, um Sicherheit zu gewährleisten.
## Wie es aussieht { #how-it-looks }
Lassen Sie uns zunächst einfach den Code verwenden und sehen, wie er funktioniert, und dann kommen wir zurück, um zu verstehen, was passiert.
## `main.py` erstellen { #create-main-py }
Kopieren Sie das Beispiel in eine Datei `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Ausführen { #run-it }
/// info | Info
Das Paket [`python-multipart`](https://github.com/Kludex/python-multipart) wird automatisch mit **FastAPI** installiert, wenn Sie den Befehl `pip install "fastapi[standard]"` ausführen.
Wenn Sie jedoch den Befehl `pip install fastapi` verwenden, ist das Paket `python-multipart` nicht standardmäßig enthalten.
Um es manuell zu installieren, stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../../virtual-environments.md) erstellen, sie aktivieren und es dann mit:
```console
$ pip install python-multipart
```
installieren.
Das liegt daran, dass **OAuth2** „Formulardaten“ zum Senden von `username` und `password` verwendet.
///
Führen Sie das Beispiel aus mit:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Es testen { #check-it }
Gehen Sie zu der interaktiven Dokumentation unter: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Sie werden etwa Folgendes sehen:
/// check | Authorize-Button!
Sie haben bereits einen glänzenden, neuen „Authorize“-Button.
Und Ihre *Pfadoperation* hat in der oberen rechten Ecke ein kleines Schloss, auf das Sie klicken können.
///
Und wenn Sie darauf klicken, erhalten Sie ein kleines Anmeldeformular zur Eingabe eines `username` und `password` (und anderer optionaler Felder):
/// note | Hinweis
Es spielt keine Rolle, was Sie in das Formular eingeben, es wird noch nicht funktionieren. Wir kommen dahin.
///
Dies ist natürlich nicht das Frontend für die Endbenutzer, aber es ist ein großartiges automatisches Tool, um Ihre gesamte API interaktiv zu dokumentieren.
Es kann vom Frontend-Team verwendet werden (das auch Sie selbst sein können).
Es kann von Anwendungen und Systemen Dritter verwendet werden.
Und es kann auch von Ihnen selbst verwendet werden, um dieselbe Anwendung zu debuggen, zu prüfen und zu testen.
## Der `password`-Flow { #the-password-flow }
Lassen Sie uns nun etwas zurückgehen und verstehen, was das alles ist.
Der `password`-„Flow“ ist eine der in OAuth2 definierten Wege („Flows“) zur Handhabung von Sicherheit und Authentifizierung.
OAuth2 wurde so konzipiert, dass das Backend oder die API unabhängig vom Server sein kann, der den Benutzer authentifiziert.
In diesem Fall handhabt jedoch dieselbe **FastAPI**-Anwendung sowohl die API als auch die Authentifizierung.
Betrachten wir es also aus dieser vereinfachten Sicht:
* Der Benutzer gibt den `username` und das `password` im Frontend ein und drückt `Enter`.
* Das Frontend (das im Browser des Benutzers läuft) sendet diesen `username` und das `password` an eine bestimmte URL in unserer API (deklariert mit `tokenUrl="token"`).
* Die API überprüft den `username` und das `password` und antwortet mit einem „Token“ (wir haben davon noch nichts implementiert).
* Ein „Token“ ist lediglich ein String mit einem Inhalt, den wir später verwenden können, um diesen Benutzer zu verifizieren.
* Normalerweise läuft ein Token nach einiger Zeit ab.
* Daher muss sich der Benutzer irgendwann später erneut anmelden.
* Und wenn der Token gestohlen wird, ist das Risiko geringer. Es handelt sich nicht um einen dauerhaften Schlüssel, der (in den meisten Fällen) für immer funktioniert.
* Das Frontend speichert diesen Token vorübergehend irgendwo.
* Der Benutzer klickt im Frontend, um zu einem anderen Abschnitt der Frontend-Web-Anwendung zu gelangen.
* Das Frontend muss weitere Daten von der API abrufen.
* Es benötigt jedoch eine Authentifizierung für diesen bestimmten Endpunkt.
* Um sich also bei unserer API zu authentifizieren, sendet es einen `Authorization`-Header mit dem Wert `Bearer ` plus dem Token.
* Wenn der Token `foobar` enthielte, wäre der Inhalt des `Authorization`-Headers: `Bearer foobar`.
## **FastAPI**s `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
**FastAPI** bietet mehrere Tools auf unterschiedlichen Abstraktionsebenen zur Implementierung dieser Sicherheitsfunktionen.
In diesem Beispiel verwenden wir **OAuth2** mit dem **Password**-Flow und einem **Bearer**-Token. Wir machen das mit der Klasse `OAuth2PasswordBearer`.
/// info | Info
Ein „Bearer“-Token ist nicht die einzige Option.
Aber es ist die beste für unseren Anwendungsfall.
Und es ist wahrscheinlich auch für die meisten anderen Anwendungsfälle die beste, es sei denn, Sie sind ein OAuth2-Experte und wissen genau, warum es eine andere Option gibt, die Ihren Anforderungen besser entspricht.
In dem Fall gibt Ihnen **FastAPI** ebenfalls die Tools, die Sie zum Erstellen brauchen.
///
Wenn wir eine Instanz der Klasse `OAuth2PasswordBearer` erstellen, übergeben wir den Parameter `tokenUrl`. Dieser Parameter enthält die URL, die der Client (das Frontend, das im Browser des Benutzers ausgeführt wird) verwendet, wenn er den `username` und das `password` sendet, um einen Token zu erhalten.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Tipp
Hier bezieht sich `tokenUrl="token"` auf eine relative URL `token`, die wir noch nicht erstellt haben. Da es sich um eine relative URL handelt, entspricht sie `./token`.
Da wir eine relative URL verwenden, würde sich das, wenn sich Ihre API unter `https://example.com/` befindet, auf `https://example.com/token` beziehen. Wenn sich Ihre API jedoch unter `https://example.com/api/v1/` befände, würde es sich auf `https://example.com/api/v1/token` beziehen.
Die Verwendung einer relativen URL ist wichtig, um sicherzustellen, dass Ihre Anwendung auch in einem fortgeschrittenen Anwendungsfall, wie [Hinter einem Proxy](../../advanced/behind-a-proxy.md), weiterhin funktioniert.
///
Dieser Parameter erstellt nicht diesen Endpunkt / diese *Pfadoperation*, sondern deklariert, dass die URL `/token` diejenige sein wird, die der Client verwenden soll, um den Token abzurufen. Diese Information wird in OpenAPI und dann in den interaktiven API-Dokumentationssystemen verwendet.
Wir werden demnächst auch die eigentliche Pfadoperation erstellen.
/// info | Info
Wenn Sie ein sehr strenger „Pythonista“ sind, missfällt Ihnen möglicherweise die Schreibweise des Parameternamens `tokenUrl` anstelle von `token_url`.
Das liegt daran, dass FastAPI denselben Namen wie in der OpenAPI-Spezifikation verwendet. Sodass Sie, wenn Sie mehr über eines dieser Sicherheitsschemas herausfinden möchten, den Namen einfach kopieren und einfügen können, um weitere Informationen darüber zu erhalten.
///
Die Variable `oauth2_scheme` ist eine Instanz von `OAuth2PasswordBearer`, aber auch ein „Callable“.
Es könnte wie folgt aufgerufen werden:
```Python
oauth2_scheme(some, parameters)
```
Es kann also mit `Depends` verwendet werden.
### Verwenden { #use-it }
Jetzt können Sie dieses `oauth2_scheme` als Abhängigkeit `Depends` übergeben.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Diese Abhängigkeit stellt einen `str` bereit, der dem Parameter `token` der *Pfadoperation-Funktion* zugewiesen wird.
**FastAPI** weiß, dass es diese Abhängigkeit verwenden kann, um ein „Sicherheitsschema“ im OpenAPI-Schema (und der automatischen API-Dokumentation) zu definieren.
/// info | Technische Details
**FastAPI** weiß, dass es die Klasse `OAuth2PasswordBearer` (deklariert in einer Abhängigkeit) verwenden kann, um das Sicherheitsschema in OpenAPI zu definieren, da es von `fastapi.security.oauth2.OAuth2` erbt, das wiederum von `fastapi.security.base.SecurityBase` erbt.
Alle Sicherheits-Werkzeuge, die in OpenAPI integriert sind (und die automatische API-Dokumentation), erben von `SecurityBase`, so weiß **FastAPI**, wie es sie in OpenAPI integrieren muss.
///
## Was es macht { #what-it-does }
FastAPI wird im Request nach diesem `Authorization`-Header suchen, prüfen, ob der Wert `Bearer ` plus ein Token ist, und den Token als `str` zurückgeben.
Wenn es keinen `Authorization`-Header sieht, oder der Wert keinen `Bearer `-Token hat, antwortet es direkt mit einem 401-Statuscode-Error (`UNAUTHORIZED`).
Sie müssen nicht einmal prüfen, ob der Token existiert, um einen Fehler zurückzugeben. Seien Sie sicher, dass Ihre Funktion, wenn sie ausgeführt wird, ein `str` in diesem Token enthält.
Sie können das bereits in der interaktiven Dokumentation ausprobieren:
Wir überprüfen im Moment noch nicht die Gültigkeit des Tokens, aber das ist bereits ein Anfang.
## Zusammenfassung { #recap }
Mit nur drei oder vier zusätzlichen Zeilen haben Sie so bereits eine primitive Form der Sicherheit.
================================================
FILE: docs/de/docs/tutorial/security/get-current-user.md
================================================
# Aktuellen Benutzer abrufen { #get-current-user }
Im vorherigen Kapitel hat das Sicherheitssystem (das auf dem Dependency Injection System basiert) der *Pfadoperation-Funktion* einen `token` vom Typ `str` überreicht:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Aber das ist immer noch nicht so nützlich.
Lassen wir es uns den aktuellen Benutzer überreichen.
## Ein Benutzermodell erstellen { #create-a-user-model }
Erstellen wir zunächst ein Pydantic-Benutzermodell.
So wie wir Pydantic zum Deklarieren von Bodys verwenden, können wir es auch überall sonst verwenden:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Eine `get_current_user`-Abhängigkeit erstellen { #create-a-get-current-user-dependency }
Erstellen wir eine Abhängigkeit `get_current_user`.
Erinnern Sie sich, dass Abhängigkeiten Unterabhängigkeiten haben können?
`get_current_user` wird seinerseits von `oauth2_scheme` abhängen, das wir zuvor erstellt haben.
So wie wir es zuvor in der *Pfadoperation* direkt gemacht haben, erhält unsere neue Abhängigkeit `get_current_user` von der Unterabhängigkeit `oauth2_scheme` einen `token` vom Typ `str`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Den Benutzer abrufen { #get-the-user }
`get_current_user` wird eine von uns erstellte (gefakte) Hilfsfunktion verwenden, welche einen Token vom Typ `str` entgegennimmt und unser Pydantic-`User`-Modell zurückgibt:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Den aktuellen Benutzer einfügen { #inject-the-current-user }
Und jetzt können wir wiederum `Depends` mit unserem `get_current_user` in der *Pfadoperation* verwenden:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Beachten Sie, dass wir als Typ von `current_user` das Pydantic-Modell `User` deklarieren.
Das wird uns innerhalb der Funktion bei Codevervollständigung und Typprüfungen helfen.
/// tip | Tipp
Sie erinnern sich vielleicht, dass Requestbodys ebenfalls mit Pydantic-Modellen deklariert werden.
Weil Sie `Depends` verwenden, wird **FastAPI** hier aber nicht verwirrt.
///
/// check | Testen
Die Art und Weise, wie dieses System von Abhängigkeiten konzipiert ist, ermöglicht es uns, verschiedene Abhängigkeiten (verschiedene „Dependables“) zu haben, die alle ein `User`-Modell zurückgeben.
Wir sind nicht darauf beschränkt, nur eine Abhängigkeit zu haben, die diesen Typ von Daten zurückgeben kann.
///
## Andere Modelle { #other-models }
Sie können jetzt den aktuellen Benutzer direkt in den *Pfadoperation-Funktionen* abrufen und die Sicherheitsmechanismen auf **Dependency Injection** Ebene handhaben, mittels `Depends`.
Und Sie können alle Modelle und Daten für die Sicherheitsanforderungen verwenden (in diesem Fall ein Pydantic-Modell `User`).
Sie sind jedoch nicht auf die Verwendung von bestimmten Datenmodellen, Klassen, oder Typen beschränkt.
Möchten Sie eine `id` und eine `email` und keinen `username` in Ihrem Modell haben? Kein Problem. Sie können dieselben Tools verwenden.
Möchten Sie nur ein `str` haben? Oder nur ein `dict`? Oder direkt eine Instanz eines Modells einer Datenbank-Klasse? Es funktioniert alles auf die gleiche Weise.
Sie haben eigentlich keine Benutzer, die sich bei Ihrer Anwendung anmelden, sondern Roboter, Bots oder andere Systeme, die nur über einen Zugriffstoken verfügen? Auch hier funktioniert alles gleich.
Verwenden Sie einfach jede Art von Modell, jede Art von Klasse, jede Art von Datenbank, die Sie für Ihre Anwendung benötigen. **FastAPI** deckt das alles mit seinem Dependency Injection System ab.
## Codegröße { #code-size }
Dieses Beispiel mag ausführlich erscheinen. Bedenken Sie, dass wir Sicherheit, Datenmodelle, Hilfsfunktionen und *Pfadoperationen* in derselben Datei vermischen.
Aber hier ist der entscheidende Punkt.
Der Code für Sicherheit und Dependency Injection wird einmal geschrieben.
Sie können es so komplex gestalten, wie Sie möchten. Und dennoch haben Sie es nur einmal geschrieben, an einer einzigen Stelle. Mit all der Flexibilität.
Aber Sie können Tausende von Endpunkten (*Pfadoperationen*) haben, die dasselbe Sicherheitssystem verwenden.
Und alle (oder beliebige Teile davon) können Vorteil ziehen aus der Wiederverwendung dieser und anderer von Ihnen erstellter Abhängigkeiten.
Und alle diese Tausenden von *Pfadoperationen* können nur drei Zeilen lang sein:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Zusammenfassung { #recap }
Sie können jetzt den aktuellen Benutzer direkt in Ihrer *Pfadoperation-Funktion* abrufen.
Wir haben bereits die Hälfte geschafft.
Wir müssen jetzt nur noch eine *Pfadoperation* hinzufügen, mittels der der Benutzer/Client tatsächlich seinen `username` und `password` senden kann.
Das kommt als nächstes.
================================================
FILE: docs/de/docs/tutorial/security/index.md
================================================
# Sicherheit { #security }
Es gibt viele Wege, Sicherheit, Authentifizierung und Autorisierung zu handhaben.
Und normalerweise ist es ein komplexes und „schwieriges“ Thema.
In vielen Frameworks und Systemen erfordert allein die Handhabung von Sicherheit und Authentifizierung viel Aufwand und Code (in vielen Fällen kann er 50 % oder mehr des gesamten geschriebenen Codes ausmachen).
**FastAPI** bietet mehrere Tools, die Ihnen helfen, schnell und auf standardisierte Weise mit **Sicherheit** umzugehen, ohne alle Sicherheits-Spezifikationen studieren und erlernen zu müssen.
Aber schauen wir uns zunächst ein paar kleine Konzepte an.
## In Eile? { #in-a-hurry }
Wenn Ihnen diese Begriffe egal sind und Sie einfach *jetzt* Sicherheit mit Authentifizierung basierend auf Benutzername und Passwort hinzufügen müssen, fahren Sie mit den nächsten Kapiteln fort.
## OAuth2 { #oauth2 }
OAuth2 ist eine Spezifikation, die verschiedene Möglichkeiten zur Handhabung von Authentifizierung und Autorisierung definiert.
Es handelt sich um eine recht umfangreiche Spezifikation, und sie deckt mehrere komplexe Anwendungsfälle ab.
Sie umfasst Möglichkeiten zur Authentifizierung mithilfe eines „Dritten“.
Das ist es, was alle diese „Login mit Facebook, Google, X (Twitter), GitHub“-Systeme unter der Haube verwenden.
### OAuth 1 { #oauth-1 }
Es gab ein OAuth 1, das sich stark von OAuth2 unterscheidet und komplexer ist, da es direkte Spezifikationen enthält, wie die Kommunikation verschlüsselt wird.
Heutzutage ist es nicht sehr populär und wird kaum verwendet.
OAuth2 spezifiziert nicht, wie die Kommunikation verschlüsselt werden soll, sondern erwartet, dass Ihre Anwendung mit HTTPS bereitgestellt wird.
/// tip | Tipp
Im Abschnitt über **Deployment** erfahren Sie, wie Sie HTTPS mithilfe von Traefik und Let's Encrypt kostenlos einrichten.
///
## OpenID Connect { #openid-connect }
OpenID Connect ist eine weitere Spezifikation, die auf **OAuth2** basiert.
Sie erweitert lediglich OAuth2, indem sie einige Dinge spezifiziert, die in OAuth2 relativ mehrdeutig sind, um zu versuchen, es interoperabler zu machen.
Beispielsweise verwendet der Google Login OpenID Connect (welches seinerseits OAuth2 verwendet).
Aber der Facebook Login unterstützt OpenID Connect nicht. Es hat seine eigene Variante von OAuth2.
### OpenID (nicht „OpenID Connect“) { #openid-not-openid-connect }
Es gab auch eine „OpenID“-Spezifikation. Sie versuchte das Gleiche zu lösen wie **OpenID Connect**, basierte aber nicht auf OAuth2.
Es handelte sich also um ein komplett zusätzliches System.
Heutzutage ist es nicht sehr populär und wird kaum verwendet.
## OpenAPI { #openapi }
OpenAPI (früher bekannt als Swagger) ist die offene Spezifikation zum Erstellen von APIs (jetzt Teil der Linux Foundation).
**FastAPI** basiert auf **OpenAPI**.
Das ist es, was erlaubt, mehrere automatische interaktive Dokumentations-Oberflächen, Codegenerierung, usw. zu haben.
OpenAPI bietet die Möglichkeit, mehrere Sicherheits„systeme“ zu definieren.
Durch deren Verwendung können Sie alle diese Standards-basierten Tools nutzen, einschließlich dieser interaktiven Dokumentationssysteme.
OpenAPI definiert die folgenden Sicherheitsschemas:
* `apiKey`: ein anwendungsspezifischer Schlüssel, der stammen kann von:
* Einem Query-Parameter.
* Einem Header.
* Einem Cookie.
* `http`: Standard-HTTP-Authentifizierungssysteme, einschließlich:
* `bearer`: ein Header `Authorization` mit dem Wert `Bearer ` plus einem Token. Dies wird von OAuth2 geerbt.
* HTTP Basic Authentication.
* HTTP Digest, usw.
* `oauth2`: Alle OAuth2-Methoden zum Umgang mit Sicherheit (genannt „Flows“).
* Mehrere dieser Flows eignen sich zum Aufbau eines OAuth 2.0-Authentifizierungsanbieters (wie Google, Facebook, X (Twitter), GitHub usw.):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Es gibt jedoch einen bestimmten „Flow“, der perfekt für die direkte Abwicklung der Authentifizierung in derselben Anwendung verwendet werden kann:
* `password`: Einige der nächsten Kapitel werden Beispiele dafür behandeln.
* `openIdConnect`: bietet eine Möglichkeit, zu definieren, wie OAuth2-Authentifizierungsdaten automatisch ermittelt werden können.
* Diese automatische Erkennung ist es, die in der OpenID Connect Spezifikation definiert ist.
/// tip | Tipp
Auch die Integration anderer Authentifizierungs-/Autorisierungsanbieter wie Google, Facebook, X (Twitter), GitHub, usw. ist möglich und relativ einfach.
Das komplexeste Problem besteht darin, einen Authentifizierungs-/Autorisierungsanbieter wie solche aufzubauen, aber **FastAPI** reicht Ihnen die Tools, das einfach zu erledigen, während Ihnen die schwere Arbeit abgenommen wird.
///
## **FastAPI** Tools { #fastapi-utilities }
FastAPI stellt für jedes dieser Sicherheitsschemas im Modul `fastapi.security` verschiedene Tools bereit, die die Verwendung dieser Sicherheitsmechanismen vereinfachen.
In den nächsten Kapiteln erfahren Sie, wie Sie mit diesen von **FastAPI** bereitgestellten Tools Sicherheit zu Ihrer API hinzufügen.
Und Sie werden auch sehen, wie dies automatisch in das interaktive Dokumentationssystem integriert wird.
================================================
FILE: docs/de/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 mit Passwort (und Hashing), Bearer mit JWT-Tokens { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Da wir nun über den gesamten Sicherheitsablauf verfügen, machen wir die Anwendung tatsächlich sicher, indem wir JWT-Tokens und sicheres Passwort-Hashing verwenden.
Diesen Code können Sie tatsächlich in Ihrer Anwendung verwenden, die Passwort-Hashes in Ihrer Datenbank speichern, usw.
Wir bauen auf dem vorherigen Kapitel auf.
## Über JWT { #about-jwt }
JWT bedeutet „JSON Web Tokens“.
Es ist ein Standard, um ein JSON-Objekt in einem langen, kompakten String ohne Leerzeichen zu kodieren. Das sieht so aus:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Da er nicht verschlüsselt ist, kann jeder die Informationen aus dem Inhalt wiederherstellen.
Aber er ist signiert. Wenn Sie also einen von Ihnen gesendeten Token zurückerhalten, können Sie überprüfen, ob Sie ihn tatsächlich gesendet haben.
Auf diese Weise können Sie einen Token mit einer Gültigkeitsdauer von beispielsweise einer Woche erstellen. Und wenn der Benutzer am nächsten Tag mit dem Token zurückkommt, wissen Sie, dass der Benutzer immer noch bei Ihrem System angemeldet ist.
Nach einer Woche läuft der Token ab und der Benutzer wird nicht autorisiert und muss sich erneut anmelden, um einen neuen Token zu erhalten. Und wenn der Benutzer (oder ein Dritter) versuchen würde, den Token zu ändern, um das Ablaufdatum zu ändern, würden Sie das entdecken, weil die Signaturen nicht übereinstimmen würden.
Wenn Sie mit JWT-Tokens spielen und sehen möchten, wie sie funktionieren, schauen Sie sich [https://jwt.io](https://jwt.io/) an.
## `PyJWT` installieren { #install-pyjwt }
Wir müssen `PyJWT` installieren, um die JWT-Tokens in Python zu generieren und zu verifizieren.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../../virtual-environments.md) erstellen, sie aktivieren und dann `pyjwt` installieren:
```console
$ pip install pyjwt
---> 100%
```
/// info | Info
Wenn Sie planen, digitale Signaturalgorithmen wie RSA oder ECDSA zu verwenden, sollten Sie die Kryptografie-Abhängigkeit `pyjwt[crypto]` installieren.
Weitere Informationen finden Sie in der [PyJWT-Installationsdokumentation](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Passwort-Hashing { #password-hashing }
„Hashing“ bedeutet: Konvertieren eines Inhalts (in diesem Fall eines Passworts) in eine Folge von Bytes (ein schlichter String), die wie Kauderwelsch aussieht.
Immer wenn Sie genau den gleichen Inhalt (genau das gleiche Passwort) übergeben, erhalten Sie genau den gleichen Kauderwelsch.
Sie können jedoch nicht vom Kauderwelsch zurück zum Passwort konvertieren.
### Warum Passwort-Hashing verwenden { #why-use-password-hashing }
Wenn Ihre Datenbank gestohlen wird, hat der Dieb nicht die Klartext-Passwörter Ihrer Benutzer, sondern nur die Hashes.
Der Dieb kann also nicht versuchen, die gleichen Passwörter in einem anderen System zu verwenden (da viele Benutzer überall das gleiche Passwort verwenden, wäre dies gefährlich).
## `pwdlib` installieren { #install-pwdlib }
pwdlib ist ein großartiges Python-Package, um Passwort-Hashes zu handhaben.
Es unterstützt viele sichere Hashing-Algorithmen und Werkzeuge, um mit diesen zu arbeiten.
Der empfohlene Algorithmus ist „Argon2“.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../../virtual-environments.md) erstellen, sie aktivieren, und installieren Sie dann pwdlib mit Argon2:
/// tip | Tipp
Mit `pwdlib` können Sie sogar konfigurieren, Passwörter zu lesen, die von **Django**, einem **Flask**-Sicherheits-Plugin, oder vielen anderen erstellt wurden.
So könnten Sie beispielsweise die gleichen Daten aus einer Django-Anwendung in einer Datenbank mit einer FastAPI-Anwendung teilen. Oder schrittweise eine Django-Anwendung migrieren, während Sie dieselbe Datenbank verwenden.
Und Ihre Benutzer könnten sich gleichzeitig über Ihre Django-Anwendung oder Ihre **FastAPI**-Anwendung anmelden.
///
## Die Passwörter hashen und überprüfen { #hash-and-verify-the-passwords }
Importieren Sie die benötigten Tools aus `pwdlib`.
Erstellen Sie eine PasswordHash-Instanz mit empfohlenen Einstellungen – sie wird für das Hashen und Verifizieren von Passwörtern verwendet.
/// tip | Tipp
pwdlib unterstützt auch den bcrypt-Hashing-Algorithmus, enthält jedoch keine Legacy-Algorithmen – für die Arbeit mit veralteten Hashes wird die Verwendung der Bibliothek passlib empfohlen.
Sie könnten sie beispielsweise verwenden, um von einem anderen System (wie Django) generierte Passwörter zu lesen und zu verifizieren, aber alle neuen Passwörter mit einem anderen Algorithmus wie Argon2 oder Bcrypt zu hashen.
Und mit allen gleichzeitig kompatibel sein.
///
Erstellen Sie eine Hilfsfunktion, um ein vom Benutzer stammendes Passwort zu hashen.
Und eine weitere, um zu überprüfen, ob ein empfangenes Passwort mit dem gespeicherten Hash übereinstimmt.
Und noch eine, um einen Benutzer zu authentifizieren und zurückzugeben.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Wenn `authenticate_user` mit einem Benutzernamen aufgerufen wird, der in der Datenbank nicht existiert, führen wir dennoch `verify_password` gegen einen Dummy-Hash aus.
So stellt man sicher, dass der Endpunkt ungefähr gleich viel Zeit für die Antwort benötigt, unabhängig davon, ob der Benutzername gültig ist oder nicht. Dadurch werden Timing-Angriffe verhindert, mit denen vorhandene Benutzernamen ermittelt werden könnten.
/// note | Hinweis
Wenn Sie sich die neue (gefakte) Datenbank `fake_users_db` anschauen, sehen Sie, wie das gehashte Passwort jetzt aussieht: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## JWT-Token verarbeiten { #handle-jwt-tokens }
Importieren Sie die installierten Module.
Erstellen Sie einen zufälligen geheimen Schlüssel, der zum Signieren der JWT-Tokens verwendet wird.
Um einen sicheren zufälligen geheimen Schlüssel zu generieren, verwenden Sie den folgenden Befehl:
Und kopieren Sie die Ausgabe in die Variable `SECRET_KEY` (verwenden Sie nicht die im Beispiel).
Erstellen Sie eine Variable `ALGORITHM` für den Algorithmus, der zum Signieren des JWT-Tokens verwendet wird, und setzen Sie sie auf `"HS256"`.
Erstellen Sie eine Variable für das Ablaufdatum des Tokens.
Definieren Sie ein Pydantic-Modell, das im Token-Endpunkt für die Response verwendet wird.
Erstellen Sie eine Hilfsfunktion, um einen neuen Zugriffstoken zu generieren.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Die Abhängigkeiten aktualisieren { #update-the-dependencies }
Aktualisieren Sie `get_current_user`, um den gleichen Token wie zuvor zu erhalten, dieses Mal jedoch unter Verwendung von JWT-Tokens.
Dekodieren Sie den empfangenen Token, validieren Sie ihn und geben Sie den aktuellen Benutzer zurück.
Wenn der Token ungültig ist, geben Sie sofort einen HTTP-Fehler zurück.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Die *Pfadoperation* `/token` aktualisieren { #update-the-token-path-operation }
Erstellen Sie ein `timedelta` mit der Ablaufzeit des Tokens.
Erstellen Sie einen echten JWT-Zugriffstoken und geben Sie ihn zurück.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Technische Details zum JWT-„Subjekt“ `sub` { #technical-details-about-the-jwt-subject-sub }
Die JWT-Spezifikation besagt, dass es einen Schlüssel `sub` mit dem Subjekt des Tokens gibt.
Die Verwendung ist optional, aber dort würden Sie die Identifikation des Benutzers speichern, daher verwenden wir das hier.
JWT kann auch für andere Dinge verwendet werden, abgesehen davon, einen Benutzer zu identifizieren und ihm zu erlauben, Operationen direkt auf Ihrer API auszuführen.
Sie könnten beispielsweise ein „Auto“ oder einen „Blog-Beitrag“ identifizieren.
Anschließend könnten Sie Berechtigungen für diese Entität hinzufügen, etwa „Fahren“ (für das Auto) oder „Bearbeiten“ (für den Blog).
Und dann könnten Sie diesen JWT-Token einem Benutzer (oder Bot) geben und dieser könnte ihn verwenden, um diese Aktionen auszuführen (das Auto fahren oder den Blog-Beitrag bearbeiten), ohne dass er überhaupt ein Konto haben müsste, einfach mit dem JWT-Token, den Ihre API dafür generiert hat.
Mit diesen Ideen kann JWT für weitaus anspruchsvollere Szenarien verwendet werden.
In diesen Fällen könnten mehrere dieser Entitäten die gleiche ID haben, sagen wir `foo` (ein Benutzer `foo`, ein Auto `foo` und ein Blog-Beitrag `foo`).
Deshalb, um ID-Kollisionen zu vermeiden, könnten Sie beim Erstellen des JWT-Tokens für den Benutzer, dem Wert des `sub`-Schlüssels ein Präfix, z. B. `username:` voranstellen. In diesem Beispiel hätte der Wert von `sub` also auch `username:johndoe` sein können.
Der wesentliche Punkt ist, dass der `sub`-Schlüssel in der gesamten Anwendung eine eindeutige Kennung haben sollte, und er sollte ein String sein.
## Es testen { #check-it }
Führen Sie den Server aus und gehen Sie zur Dokumentation: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Die Benutzeroberfläche sieht wie folgt aus:
Melden Sie sich bei der Anwendung auf die gleiche Weise wie zuvor an.
Verwenden Sie die Anmeldeinformationen:
Benutzername: `johndoe`
Passwort: `secret`
/// check | Testen
Beachten Sie, dass im Code nirgendwo das Klartext-Passwort „`secret`“ steht, wir haben nur die gehashte Version.
///
Rufen Sie den Endpunkt `/users/me/` auf, Sie erhalten die Response:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Wenn Sie die Developer Tools öffnen, können Sie sehen, dass die gesendeten Daten nur den Token enthalten. Das Passwort wird nur beim ersten Request gesendet, um den Benutzer zu authentisieren und diesen Zugriffstoken zu erhalten, aber nicht mehr danach:
/// note | Hinweis
Beachten Sie den Header `Authorization` mit einem Wert, der mit `Bearer ` beginnt.
///
## Fortgeschrittene Verwendung mit `scopes` { #advanced-usage-with-scopes }
OAuth2 hat ein Konzept von „Scopes“.
Sie können diese verwenden, um einem JWT-Token einen bestimmten Satz von Berechtigungen zu übergeben.
Anschließend können Sie diesen Token einem Benutzer direkt oder einem Dritten geben, damit diese mit einer Reihe von Einschränkungen mit Ihrer API interagieren können.
Wie Sie sie verwenden und wie sie in **FastAPI** integriert sind, erfahren Sie später im **Handbuch für fortgeschrittene Benutzer**.
## Zusammenfassung { #recap }
Mit dem, was Sie bis hier gesehen haben, können Sie eine sichere **FastAPI**-Anwendung mithilfe von Standards wie OAuth2 und JWT einrichten.
In fast jedem Framework wird die Handhabung der Sicherheit recht schnell zu einem ziemlich komplexen Thema.
Viele Packages, die es stark vereinfachen, müssen viele Kompromisse beim Datenmodell, der Datenbank und den verfügbaren Funktionen eingehen. Und einige dieser Pakete, die die Dinge zu sehr vereinfachen, weisen tatsächlich Sicherheitslücken auf.
---
**FastAPI** geht bei keiner Datenbank, keinem Datenmodell oder Tool Kompromisse ein.
Es gibt Ihnen die volle Flexibilität, diejenigen auszuwählen, die am besten zu Ihrem Projekt passen.
Und Sie können viele gut gepflegte und weit verbreitete Packages wie `pwdlib` und `PyJWT` direkt verwenden, da **FastAPI** keine komplexen Mechanismen zur Integration externer Pakete erfordert.
Aber es bietet Ihnen die Werkzeuge, um den Prozess so weit wie möglich zu vereinfachen, ohne Kompromisse bei Flexibilität, Robustheit oder Sicherheit einzugehen.
Und Sie können sichere Standardprotokolle wie OAuth2 auf relativ einfache Weise verwenden und implementieren.
Im **Handbuch für fortgeschrittene Benutzer** erfahren Sie mehr darüber, wie Sie OAuth2-„Scopes“ für ein feingranuliertes Berechtigungssystem verwenden, das denselben Standards folgt. OAuth2 mit Scopes ist der Mechanismus, der von vielen großen Authentifizierungsanbietern wie Facebook, Google, GitHub, Microsoft, X (Twitter), usw. verwendet wird, um Drittanbieteranwendungen zu autorisieren, im Namen ihrer Benutzer mit ihren APIs zu interagieren.
================================================
FILE: docs/de/docs/tutorial/security/simple-oauth2.md
================================================
# Einfaches OAuth2 mit Password und Bearer { #simple-oauth2-with-password-and-bearer }
Lassen Sie uns nun auf dem vorherigen Kapitel aufbauen und die fehlenden Teile hinzufügen, um einen vollständigen Sicherheits-Flow zu erhalten.
## `username` und `password` entgegennehmen { #get-the-username-and-password }
Wir werden **FastAPIs** Sicherheits-Werkzeuge verwenden, um den `username` und das `password` entgegenzunehmen.
OAuth2 spezifiziert, dass der Client/Benutzer bei Verwendung des „Password Flow“ (den wir verwenden) die Felder `username` und `password` als Formulardaten senden muss.
Und die Spezifikation sagt, dass die Felder so benannt werden müssen. `user-name` oder `email` würde also nicht funktionieren.
Aber keine Sorge, Sie können sie Ihren Endbenutzern im Frontend so anzeigen, wie Sie möchten.
Und Ihre Datenbankmodelle können beliebige andere Namen verwenden.
Aber für die Login-*Pfadoperation* müssen wir diese Namen verwenden, um mit der Spezifikation kompatibel zu sein (und beispielsweise das integrierte API-Dokumentationssystem verwenden zu können).
Die Spezifikation besagt auch, dass `username` und `password` als Formulardaten gesendet werden müssen (hier also kein JSON).
### `scope` { #scope }
Ferner sagt die Spezifikation, dass der Client ein weiteres Formularfeld "`scope`" („Geltungsbereich“) senden kann.
Der Name des Formularfelds lautet `scope` (im Singular), tatsächlich handelt es sich jedoch um einen langen String mit durch Leerzeichen getrennten „Scopes“.
Jeder „Scope“ ist nur ein String (ohne Leerzeichen).
Diese werden normalerweise verwendet, um bestimmte Sicherheitsberechtigungen zu deklarieren, zum Beispiel:
* `users:read` oder `users:write` sind gängige Beispiele.
* `instagram_basic` wird von Facebook / Instagram verwendet.
* `https://www.googleapis.com/auth/drive` wird von Google verwendet.
/// info | Info
In OAuth2 ist ein „Scope“ nur ein String, der eine bestimmte erforderliche Berechtigung deklariert.
Es spielt keine Rolle, ob er andere Zeichen wie `:` enthält oder ob es eine URL ist.
Diese Details sind implementierungsspezifisch.
Für OAuth2 sind es einfach nur Strings.
///
## Code, um `username` und `password` entgegenzunehmen { #code-to-get-the-username-and-password }
Lassen Sie uns nun die von **FastAPI** bereitgestellten Werkzeuge verwenden, um das zu erledigen.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Importieren Sie zunächst `OAuth2PasswordRequestForm` und verwenden Sie es als Abhängigkeit mit `Depends` in der *Pfadoperation* für `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` ist eine Klassenabhängigkeit, die einen Formularbody deklariert mit:
* Dem `username`.
* Dem `password`.
* Einem optionalen `scope`-Feld als langem String, bestehend aus durch Leerzeichen getrennten Strings.
* Einem optionalen `grant_type`.
/// tip | Tipp
Die OAuth2-Spezifikation *erfordert* tatsächlich ein Feld `grant_type` mit dem festen Wert `password`, aber `OAuth2PasswordRequestForm` erzwingt dies nicht.
Wenn Sie es erzwingen müssen, verwenden Sie `OAuth2PasswordRequestFormStrict` anstelle von `OAuth2PasswordRequestForm`.
///
* Eine optionale `client_id` (benötigen wir für unser Beispiel nicht).
* Ein optionales `client_secret` (benötigen wir für unser Beispiel nicht).
/// info | Info
`OAuth2PasswordRequestForm` ist keine spezielle Klasse für **FastAPI**, so wie `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` lässt **FastAPI** wissen, dass es sich um ein Sicherheitsschema handelt. Daher wird es auf diese Weise zu OpenAPI hinzugefügt.
Aber `OAuth2PasswordRequestForm` ist nur eine Klassenabhängigkeit, die Sie selbst hätten schreiben können, oder Sie hätten `Form`ular-Parameter direkt deklarieren können.
Da es sich jedoch um einen häufigen Anwendungsfall handelt, wird er zur Vereinfachung direkt von **FastAPI** bereitgestellt.
///
### Die Formulardaten verwenden { #use-the-form-data }
/// tip | Tipp
Die Instanz der Klassenabhängigkeit `OAuth2PasswordRequestForm` verfügt, statt eines Attributs `scope` mit dem durch Leerzeichen getrennten langen String, über das Attribut `scopes` mit einer tatsächlichen Liste von Strings, einem für jeden gesendeten Scope.
In diesem Beispiel verwenden wir keine `scopes`, aber die Funktionalität ist vorhanden, wenn Sie sie benötigen.
///
Rufen Sie nun die Benutzerdaten aus der (gefakten) Datenbank ab, für diesen `username` aus dem Formularfeld.
Wenn es keinen solchen Benutzer gibt, geben wir die Fehlermeldung „Incorrect username or password“ zurück.
Für den Fehler verwenden wir die Exception `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Das Passwort überprüfen { #check-the-password }
Zu diesem Zeitpunkt liegen uns die Benutzerdaten aus unserer Datenbank vor, das Passwort haben wir jedoch noch nicht überprüft.
Lassen Sie uns diese Daten zunächst in das Pydantic-Modell `UserInDB` einfügen.
Sie sollten niemals Klartext-Passwörter speichern, daher verwenden wir ein (gefaktes) Passwort-Hashing-System.
Wenn die Passwörter nicht übereinstimmen, geben wir denselben Fehler zurück.
#### Passwort-Hashing { #password-hashing }
„Hashing“ bedeutet: Konvertieren eines Inhalts (in diesem Fall eines Passworts) in eine Folge von Bytes (ein schlichter String), die wie Kauderwelsch aussieht.
Immer wenn Sie genau den gleichen Inhalt (genau das gleiche Passwort) übergeben, erhalten Sie genau den gleichen Kauderwelsch.
Sie können jedoch nicht vom Kauderwelsch zurück zum Passwort konvertieren.
##### Warum Passwort-Hashing verwenden? { #why-use-password-hashing }
Wenn Ihre Datenbank gestohlen wird, hat der Dieb nicht die Klartext-Passwörter Ihrer Benutzer, sondern nur die Hashes.
Der Dieb kann also nicht versuchen, die gleichen Passwörter in einem anderen System zu verwenden (da viele Benutzer überall das gleiche Passwort verwenden, wäre dies gefährlich).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Über `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` bedeutet:
*Übergib die Schlüssel und Werte des `user_dict` direkt als Schlüssel-Wert-Argumente, äquivalent zu:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Info
Eine ausführlichere Erklärung von `**user_dict` finden Sie in [der Dokumentation für **Extra Modelle**](../extra-models.md#about-user-in-dict).
///
## Den Token zurückgeben { #return-the-token }
Die Response des `token`-Endpunkts muss ein JSON-Objekt sein.
Es sollte einen `token_type` haben. Da wir in unserem Fall „Bearer“-Token verwenden, sollte der Token-Typ „`bearer`“ sein.
Und es sollte einen `access_token` haben, mit einem String, der unseren Zugriffstoken enthält.
In diesem einfachen Beispiel gehen wir einfach völlig unsicher vor und geben denselben `username` wie der Token zurück.
/// tip | Tipp
Im nächsten Kapitel sehen Sie eine wirklich sichere Implementierung mit Passwort-Hashing und JWT-Tokens.
Aber konzentrieren wir uns zunächst auf die spezifischen Details, die wir benötigen.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Tipp
Gemäß der Spezifikation sollten Sie ein JSON mit einem `access_token` und einem `token_type` zurückgeben, genau wie in diesem Beispiel.
Das müssen Sie selbst in Ihrem Code tun und sicherstellen, dass Sie diese JSON-Schlüssel verwenden.
Es ist fast das Einzige, woran Sie denken müssen, es selbst richtigzumachen und die Spezifikationen einzuhalten.
Den Rest erledigt **FastAPI** für Sie.
///
## Die Abhängigkeiten aktualisieren { #update-the-dependencies }
Jetzt werden wir unsere Abhängigkeiten aktualisieren.
Wir möchten den `current_user` *nur* erhalten, wenn dieser Benutzer aktiv ist.
Daher erstellen wir eine zusätzliche Abhängigkeit `get_current_active_user`, die wiederum `get_current_user` als Abhängigkeit verwendet.
Beide Abhängigkeiten geben nur dann einen HTTP-Error zurück, wenn der Benutzer nicht existiert oder inaktiv ist.
In unserem Endpunkt erhalten wir also nur dann einen Benutzer, wenn der Benutzer existiert, korrekt authentifiziert wurde und aktiv ist:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Info
Der zusätzliche Header `WWW-Authenticate` mit dem Wert `Bearer`, den wir hier zurückgeben, ist ebenfalls Teil der Spezifikation.
Jeder HTTP-(Fehler-)Statuscode 401 „UNAUTHORIZED“ soll auch einen `WWW-Authenticate`-Header zurückgeben.
Im Fall von Bearer-Tokens (in unserem Fall) sollte der Wert dieses Headers `Bearer` lauten.
Sie können diesen zusätzlichen Header tatsächlich weglassen und es würde trotzdem funktionieren.
Aber er wird hier bereitgestellt, um den Spezifikationen zu entsprechen.
Außerdem gibt es möglicherweise Tools, die ihn erwarten und verwenden (jetzt oder in der Zukunft) und das könnte für Sie oder Ihre Benutzer jetzt oder in der Zukunft nützlich sein.
Das ist der Vorteil von Standards ...
///
## Es in Aktion sehen { #see-it-in-action }
Öffnen Sie die interaktive Dokumentation: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Authentifizieren { #authenticate }
Klicken Sie auf den Button „Authorize“.
Verwenden Sie die Anmeldedaten:
Benutzer: `johndoe`
Passwort: `secret`.
Nach der Authentifizierung im System sehen Sie Folgendes:
### Die eigenen Benutzerdaten ansehen { #get-your-own-user-data }
Verwenden Sie nun die Operation `GET` mit dem Pfad `/users/me`.
Sie erhalten Ihre Benutzerdaten:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Wenn Sie auf das Schlosssymbol klicken und sich abmelden und dann den gleichen Vorgang nochmal versuchen, erhalten Sie einen HTTP 401 Error:
```JSON
{
"detail": "Not authenticated"
}
```
### Inaktiver Benutzer { #inactive-user }
Versuchen Sie es nun mit einem inaktiven Benutzer und authentisieren Sie sich mit:
Benutzer: `alice`.
Passwort: `secret2`.
Und versuchen Sie, die Operation `GET` mit dem Pfad `/users/me` zu verwenden.
Sie erhalten die Fehlermeldung „Inactive user“:
```JSON
{
"detail": "Inactive user"
}
```
## Zusammenfassung { #recap }
Sie verfügen jetzt über die Tools, um ein vollständiges Sicherheitssystem basierend auf `username` und `password` für Ihre API zu implementieren.
Mit diesen Tools können Sie das Sicherheitssystem mit jeder Datenbank und jedem Benutzer oder Datenmodell kompatibel machen.
Das einzige fehlende Detail ist, dass es noch nicht wirklich „sicher“ ist.
Im nächsten Kapitel erfahren Sie, wie Sie eine sichere Passwort-Hashing-Bibliothek und JWT-Token verwenden.
================================================
FILE: docs/de/docs/tutorial/server-sent-events.md
================================================
# Server-Sent Events (SSE) { #server-sent-events-sse }
Sie können Daten mithilfe von **Server-Sent Events** (SSE) an den Client streamen.
Das ist ähnlich wie [JSON Lines streamen](stream-json-lines.md), verwendet aber das Format `text/event-stream`, das von Browsern nativ mit der [die `EventSource`-API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) unterstützt wird.
/// info | Info
Hinzugefügt in FastAPI 0.135.0.
///
## Was sind Server-Sent Events? { #what-are-server-sent-events }
SSE ist ein Standard zum Streamen von Daten vom Server zum Client über HTTP.
Jedes Event ist ein kleiner Textblock mit „Feldern“ wie `data`, `event`, `id` und `retry`, getrennt durch Leerzeilen.
Das sieht so aus:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE wird häufig für KI-Chat-Streaming, Live-Benachrichtigungen, Logs und Observability sowie andere Fälle verwendet, in denen der Server Updates an den Client pusht.
/// tip | Tipp
Wenn Sie Binärdaten streamen wollen, z. B. Video oder Audio, sehen Sie im fortgeschrittenen Handbuch nach: [Daten streamen](../advanced/stream-data.md).
///
## SSE mit FastAPI streamen { #stream-sse-with-fastapi }
Um SSE mit FastAPI zu streamen, verwenden Sie `yield` in Ihrer *Pfadoperation-Funktion* und setzen Sie `response_class=EventSourceResponse`.
Importieren Sie `EventSourceResponse` aus `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Jedes mit `yield` zurückgegebene Element wird als JSON kodiert und im Feld `data:` eines SSE-Events gesendet.
Wenn Sie den Rückgabetyp als `AsyncIterable[Item]` deklarieren, verwendet FastAPI ihn, um die Daten mit Pydantic zu **validieren**, zu **dokumentieren** und zu **serialisieren**.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Tipp
Da Pydantic es auf der **Rust**-Seite serialisiert, erhalten Sie eine deutlich höhere **Leistung**, als wenn Sie keinen Rückgabetyp deklarieren.
///
### Nicht-async-*Pfadoperation-Funktionen* { #non-async-path-operation-functions }
Sie können auch normale `def`-Funktionen (ohne `async`) verwenden und `yield` genauso einsetzen.
FastAPI stellt sicher, dass sie korrekt ausgeführt wird, sodass sie die Event Loop nicht blockiert.
Da die Funktion in diesem Fall nicht async ist, wäre der passende Rückgabetyp `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Kein Rückgabetyp { #no-return-type }
Sie können den Rückgabetyp auch weglassen. FastAPI verwendet dann den [`jsonable_encoder`](./encoder.md), um die Daten zu konvertieren und zu senden.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Wenn Sie SSE-Felder wie `event`, `id`, `retry` oder `comment` setzen müssen, können Sie statt reiner Daten `ServerSentEvent`-Objekte yielden.
Importieren Sie `ServerSentEvent` aus `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
Das Feld `data` wird immer als JSON kodiert. Sie können jeden Wert übergeben, der als JSON serialisierbar ist, einschließlich Pydantic-Modellen.
## Rohdaten { #raw-data }
Wenn Sie Daten **ohne** JSON-Kodierung senden müssen, verwenden Sie `raw_data` statt `data`.
Das ist nützlich zum Senden vorformatierter Texte, Logzeilen oder spezieller „Sentinel“-Werte wie `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Hinweis
`data` und `raw_data` schließen sich gegenseitig aus. Sie können pro `ServerSentEvent` nur eines von beiden setzen.
///
## Mit `Last-Event-ID` fortsetzen { #resuming-with-last-event-id }
Wenn ein Browser nach einem Verbindungsabbruch erneut verbindet, sendet er die zuletzt empfangene `id` im Header `Last-Event-ID`.
Sie können ihn als Header-Parameter einlesen und verwenden, um den Stream dort fortzusetzen, wo der Client aufgehört hat:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE mit POST { #sse-with-post }
SSE funktioniert mit **jedem HTTP-Method**, nicht nur mit `GET`.
Das ist nützlich für Protokolle wie [MCP](https://modelcontextprotocol.io), die SSE über `POST` streamen:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Technische Details { #technical-details }
FastAPI implementiert einige bewährte SSE-Praktiken direkt out of the box.
- Alle 15 Sekunden, wenn keine Nachricht gesendet wurde, einen **„keep alive“-`ping`-Kommentar** senden, um zu verhindern, dass einige Proxys die Verbindung schließen, wie in der [HTML-Spezifikation: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes) vorgeschlagen.
- Den Header `Cache-Control: no-cache` setzen, um **Caching** des Streams zu verhindern.
- Einen speziellen Header `X-Accel-Buffering: no` setzen, um **Buffering** in einigen Proxys wie Nginx zu verhindern.
Sie müssen dafür nichts tun, das funktioniert out of the box. 🤓
================================================
FILE: docs/de/docs/tutorial/sql-databases.md
================================================
# SQL (Relationale) Datenbanken { #sql-relational-databases }
**FastAPI** erfordert nicht, dass Sie eine SQL (relationale) Datenbank verwenden. Sondern Sie können **jede beliebige Datenbank** verwenden, die Sie möchten.
Hier werden wir ein Beispiel mit [SQLModel](https://sqlmodel.tiangolo.com/) sehen.
**SQLModel** basiert auf [SQLAlchemy](https://www.sqlalchemy.org/) und Pydantic. Es wurde vom selben Autor wie **FastAPI** entwickelt, um die perfekte Ergänzung für FastAPI-Anwendungen zu sein, die **SQL-Datenbanken** verwenden müssen.
/// tip | Tipp
Sie könnten jede andere SQL- oder NoSQL-Datenbankbibliothek verwenden, die Sie möchten (in einigen Fällen als „ORMs“ bezeichnet), FastAPI zwingt Sie nicht, irgendetwas zu verwenden. 😎
///
Da SQLModel auf SQLAlchemy basiert, können Sie problemlos **jede von SQLAlchemy unterstützte Datenbank** verwenden (was auch bedeutet, dass sie von SQLModel unterstützt werden), wie:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, usw.
In diesem Beispiel verwenden wir **SQLite**, da es eine einzelne Datei verwendet und Python integrierte Unterstützung bietet. Sie können also dieses Beispiel kopieren und direkt ausführen.
Später, für Ihre Produktionsanwendung, möchten Sie möglicherweise einen Datenbankserver wie **PostgreSQL** verwenden.
/// tip | Tipp
Es gibt einen offiziellen Projektgenerator mit **FastAPI** und **PostgreSQL**, einschließlich eines Frontends und weiterer Tools: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Dies ist ein sehr einfaches und kurzes Tutorial. Wenn Sie mehr über Datenbanken im Allgemeinen, über SQL oder fortgeschrittenere Funktionen erfahren möchten, besuchen Sie die [SQLModel-Dokumentation](https://sqlmodel.tiangolo.com/).
## `SQLModel` installieren { #install-sqlmodel }
Stellen Sie zunächst sicher, dass Sie Ihre [virtuelle Umgebung](../virtual-environments.md) erstellen, sie aktivieren und dann `sqlmodel` installieren:
```console
$ pip install sqlmodel
---> 100%
```
## Die App mit einem einzelnen Modell erstellen { #create-the-app-with-a-single-model }
Wir erstellen zuerst die einfachste erste Version der App mit einem einzigen **SQLModel**-Modell.
Später werden wir sie verbessern, indem wir unter der Haube **mehrere Modelle** verwenden, um Sicherheit und Vielseitigkeit zu erhöhen. 🤓
### Modelle erstellen { #create-models }
Importieren Sie `SQLModel` und erstellen Sie ein Datenbankmodell:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Die `Hero`-Klasse ist einem Pydantic-Modell sehr ähnlich (faktisch ist sie darunter tatsächlich *ein Pydantic-Modell*).
Es gibt ein paar Unterschiede:
* `table=True` sagt SQLModel, dass dies ein *Tabellenmodell* ist, es soll eine **Tabelle** in der SQL-Datenbank darstellen, es ist nicht nur ein *Datenmodell* (wie es jede andere reguläre Pydantic-Klasse wäre).
* `Field(primary_key=True)` sagt SQLModel, dass die `id` der **Primärschlüssel** in der SQL-Datenbank ist (Sie können mehr über SQL-Primärschlüssel in der SQLModel-Dokumentation erfahren).
**Hinweis:** Wir verwenden für das Primärschlüsselfeld `int | None`, damit wir im Python-Code *ein Objekt ohne `id` erstellen* können (`id=None`), in der Annahme, dass die Datenbank sie *beim Speichern generiert*. SQLModel versteht, dass die Datenbank die `id` bereitstellt, und *definiert die Spalte im Datenbankschema als ein Nicht-Null-`INTEGER`*. Siehe die [SQLModel-Dokumentation zu Primärschlüsseln](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) für Details.
* `Field(index=True)` sagt SQLModel, dass es einen **SQL-Index** für diese Spalte erstellen soll, was schnelleres Suchen in der Datenbank ermöglicht, wenn Daten mittels dieser Spalte gefiltert werden.
SQLModel wird verstehen, dass etwas, das als `str` deklariert ist, eine SQL-Spalte des Typs `TEXT` (oder `VARCHAR`, abhängig von der Datenbank) sein wird.
### Eine Engine erstellen { #create-an-engine }
Eine SQLModel-`engine` (darunter ist es tatsächlich eine SQLAlchemy-`engine`) ist das, was die **Verbindungen** zur Datenbank hält.
Sie hätten **ein einziges `engine`-Objekt** für Ihren gesamten Code, um sich mit derselben Datenbank zu verbinden.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Die Verwendung von `check_same_thread=False` erlaubt FastAPI, dieselbe SQLite-Datenbank in verschiedenen Threads zu verwenden. Dies ist notwendig, da **ein einzelner Request** **mehr als einen Thread** verwenden könnte (zum Beispiel in Abhängigkeiten).
Keine Sorge, so wie der Code strukturiert ist, werden wir später sicherstellen, dass wir **eine einzige SQLModel-*Session* pro Request** verwenden, das ist eigentlich das, was `check_same_thread` erreichen möchte.
### Die Tabellen erstellen { #create-the-tables }
Dann fügen wir eine Funktion hinzu, die `SQLModel.metadata.create_all(engine)` verwendet, um die **Tabellen für alle *Tabellenmodelle* zu erstellen**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Eine Session-Abhängigkeit erstellen { #create-a-session-dependency }
Eine **`Session`** speichert die **Objekte im Speicher** und verfolgt alle Änderungen, die an den Daten vorgenommen werden müssen, dann **verwendet sie die `engine`**, um mit der Datenbank zu kommunizieren.
Wir werden eine FastAPI **Abhängigkeit** mit `yield` erstellen, die eine neue `Session` für jeden Request bereitstellt. Das ist es, was sicherstellt, dass wir eine einzige Session pro Request verwenden. 🤓
Dann erstellen wir eine `Annotated`-Abhängigkeit `SessionDep`, um den Rest des Codes zu vereinfachen, der diese Abhängigkeit nutzen wird.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Die Datenbanktabellen beim Start erstellen { #create-database-tables-on-startup }
Wir werden die Datenbanktabellen erstellen, wenn die Anwendung startet.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Hier erstellen wir die Tabellen bei einem Anwendungsstart-Event.
Für die Produktion würden Sie wahrscheinlich ein Migrationsskript verwenden, das ausgeführt wird, bevor Sie Ihre App starten. 🤓
/// tip | Tipp
SQLModel wird Migrationstools haben, die Alembic wrappen, aber im Moment können Sie [Alembic](https://alembic.sqlalchemy.org/en/latest/) direkt verwenden.
///
### Einen Helden erstellen { #create-a-hero }
Da jedes SQLModel-Modell auch ein Pydantic-Modell ist, können Sie es in denselben **Typannotationen** verwenden, die Sie für Pydantic-Modelle verwenden könnten.
Wenn Sie beispielsweise einen Parameter vom Typ `Hero` deklarieren, wird er aus dem **JSON-Body** gelesen.
Auf die gleiche Weise können Sie es als **Rückgabetyp** der Funktion deklarieren, und dann wird die Form der Daten in der automatischen API-Dokumentation angezeigt.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Hier verwenden wir die `SessionDep`-Abhängigkeit (eine `Session`), um den neuen `Hero` zur `Session`-Instanz hinzuzufügen, die Änderungen an der Datenbank zu committen, die Daten im `hero` zu aktualisieren und ihn anschließend zurückzugeben.
### Helden lesen { #read-heroes }
Wir können `Hero`s aus der Datenbank mit einem `select()` **lesen**. Wir können ein `limit` und `offset` hinzufügen, um die Ergebnisse zu paginieren.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Einen Helden lesen { #read-one-hero }
Wir können einen einzelnen `Hero` **lesen**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Einen Helden löschen { #delete-a-hero }
Wir können auch einen `Hero` **löschen**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Die App ausführen { #run-the-app }
Sie können die App ausführen:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Gehen Sie dann zur `/docs`-UI, Sie werden sehen, dass **FastAPI** diese **Modelle** verwendet, um die API zu **dokumentieren**, und es wird sie auch verwenden, um die Daten zu **serialisieren** und zu **validieren**.
## Die App mit mehreren Modellen aktualisieren { #update-the-app-with-multiple-models }
Jetzt lassen Sie uns diese App ein wenig **refaktorisieren**, um die **Sicherheit** und **Vielseitigkeit** zu erhöhen.
Wenn Sie die vorherige App überprüfen, können Sie in der UI sehen, dass sie bis jetzt dem Client erlaubt, die `id` des zu erstellenden `Hero` zu bestimmen. 😱
Das sollten wir nicht zulassen, sie könnten eine `id` überschreiben, die wir bereits in der DB zugewiesen haben. Die Entscheidung über die `id` sollte vom **Backend** oder der **Datenbank** getroffen werden, **nicht vom Client**.
Außerdem erstellen wir einen `secret_name` für den Helden, aber bisher geben wir ihn überall zurück, das ist nicht sehr **geheim** ... 😅
Wir werden diese Dinge beheben, indem wir ein paar **zusätzliche Modelle** hinzufügen. Hier wird SQLModel glänzen. ✨
### Mehrere Modelle erstellen { #create-multiple-models }
In **SQLModel** ist jede Modellklasse, die `table=True` hat, ein **Tabellenmodell**.
Und jede Modellklasse, die `table=True` nicht hat, ist ein **Datenmodell**, diese sind tatsächlich nur Pydantic-Modelle (mit ein paar kleinen zusätzlichen Funktionen). 🤓
Mit SQLModel können wir **Vererbung** verwenden, um **doppelte Felder** in allen Fällen zu **vermeiden**.
#### `HeroBase` – die Basisklasse { #herobase-the-base-class }
Fangen wir mit einem `HeroBase`-Modell an, das alle **Felder hat, die von allen Modellen geteilt werden**:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` – das *Tabellenmodell* { #hero-the-table-model }
Dann erstellen wir `Hero`, das tatsächliche *Tabellenmodell*, mit den **zusätzlichen Feldern**, die nicht immer in den anderen Modellen enthalten sind:
* `id`
* `secret_name`
Da `Hero` von `HeroBase` erbt, hat es **auch** die **Felder**, die in `HeroBase` deklariert sind, also sind alle Felder von `Hero`:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` – das öffentliche *Datenmodell* { #heropublic-the-public-data-model }
Als nächstes erstellen wir ein `HeroPublic`-Modell, das an die API-Clients **zurückgegeben** wird.
Es hat dieselben Felder wie `HeroBase`, sodass es `secret_name` nicht enthält.
Endlich ist die Identität unserer Helden geschützt! 🥷
Es deklariert auch `id: int` erneut. Indem wir dies tun, machen wir einen **Vertrag** mit den API-Clients, damit sie immer damit rechnen können, dass die `id` vorhanden ist und ein `int` ist (sie wird niemals `None` sein).
/// tip | Tipp
Es ist sehr nützlich für die API-Clients, wenn das Rückgabemodell sicherstellt, dass ein Wert immer verfügbar und immer `int` (nicht `None`) ist, sie können viel einfacheren Code schreiben, wenn sie diese Sicherheit haben.
Auch **automatisch generierte Clients** werden einfachere Schnittstellen haben, damit die Entwickler, die mit Ihrer API kommunizieren, viel mehr Freude an der Arbeit mit Ihrer API haben können. 😎
///
Alle Felder in `HeroPublic` sind dieselben wie in `HeroBase`, mit `id`, das als `int` (nicht `None`) deklariert ist:
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` – das *Datenmodell* zum Erstellen eines Helden { #herocreate-the-data-model-to-create-a-hero }
Nun erstellen wir ein `HeroCreate`-Modell, das die Daten der Clients **validiert**.
Es hat dieselben Felder wie `HeroBase`, und es hat auch `secret_name`.
Wenn die Clients **einen neuen Helden erstellen**, senden sie jetzt den `secret_name`, er wird in der Datenbank gespeichert, aber diese geheimen Namen werden den API-Clients nicht zurückgegeben.
/// tip | Tipp
So würden Sie **Passwörter** handhaben. Empfangen Sie sie, aber geben Sie sie nicht in der API zurück.
Sie würden auch die Werte der Passwörter **hashen**, bevor Sie sie speichern, und sie **niemals im Klartext** speichern.
///
Die Felder von `HeroCreate` sind:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` – das *Datenmodell* zum Aktualisieren eines Helden { #heroupdate-the-data-model-to-update-a-hero }
In der vorherigen Version der App hatten wir keine Möglichkeit, einen Helden **zu aktualisieren**, aber jetzt mit **mehreren Modellen** können wir es. 🎉
Das `HeroUpdate`-*Datenmodell* ist etwas Besonderes, es hat **die selben Felder**, die benötigt werden, um einen neuen Helden zu erstellen, aber alle Felder sind **optional** (sie haben alle einen Defaultwert). Auf diese Weise, wenn Sie einen Helden aktualisieren, können Sie nur die Felder senden, die Sie aktualisieren möchten.
Da sich tatsächlich **alle Felder ändern** (der Typ enthält jetzt `None` und sie haben jetzt einen Standardwert von `None`), müssen wir sie erneut **deklarieren**.
Wir müssen wirklich nicht von `HeroBase` erben, weil wir alle Felder neu deklarieren. Ich lasse es aus Konsistenzgründen erben, aber das ist nicht notwendig. Es ist mehr eine Frage des persönlichen Geschmacks. 🤷
Die Felder von `HeroUpdate` sind:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Mit `HeroCreate` erstellen und ein `HeroPublic` zurückgeben { #create-with-herocreate-and-return-a-heropublic }
Nun, da wir **mehrere Modelle** haben, können wir die Teile der App aktualisieren, die sie verwenden.
Wir empfangen im Request ein `HeroCreate`-*Datenmodell* und daraus erstellen wir ein `Hero`-*Tabellenmodell*.
Dieses neue *Tabellenmodell* `Hero` wird die vom Client gesendeten Felder haben und zusätzlich eine `id`, die von der Datenbank generiert wird.
Dann geben wir das gleiche *Tabellenmodell* `Hero` von der Funktion zurück. Aber da wir das `response_model` mit dem `HeroPublic`-*Datenmodell* deklarieren, wird **FastAPI** `HeroPublic` verwenden, um die Daten zu validieren und zu serialisieren.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Tipp
Jetzt verwenden wir `response_model=HeroPublic` anstelle der **Rückgabetyp-Annotation** `-> HeroPublic`, weil der Wert, den wir zurückgeben, tatsächlich *kein* `HeroPublic` ist.
Wenn wir `-> HeroPublic` deklariert hätten, würden Ihr Editor und Linter (zu Recht) reklamieren, dass Sie ein `Hero` anstelle eines `HeroPublic` zurückgeben.
Durch die Deklaration in `response_model` sagen wir **FastAPI**, dass es seine Aufgabe erledigen soll, ohne die Typannotationen und die Hilfe von Ihrem Editor und anderen Tools zu beeinträchtigen.
///
### Helden mit `HeroPublic` lesen { #read-heroes-with-heropublic }
Wir können dasselbe wie zuvor tun, um `Hero`s zu **lesen**, und erneut verwenden wir `response_model=list[HeroPublic]`, um sicherzustellen, dass die Daten ordnungsgemäß validiert und serialisiert werden.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Einen einzelnen Helden mit `HeroPublic` lesen { #read-one-hero-with-heropublic }
Wir können einen einzelnen Helden **lesen**:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Einen Helden mit `HeroUpdate` aktualisieren { #update-a-hero-with-heroupdate }
Wir können einen Helden **aktualisieren**. Dafür verwenden wir eine HTTP-`PATCH`-Operation.
Und im Code erhalten wir ein `dict` mit allen Daten, die vom Client gesendet wurden, **nur die Daten, die vom Client gesendet wurden**, unter Ausschluss von Werten, die dort nur als Defaultwerte vorhanden wären. Um dies zu tun, verwenden wir `exclude_unset=True`. Das ist der Haupttrick. 🪄
Dann verwenden wir `hero_db.sqlmodel_update(hero_data)`, um die `hero_db` mit den Daten aus `hero_data` zu aktualisieren.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Einen Helden wieder löschen { #delete-a-hero-again }
Das **Löschen** eines Helden bleibt ziemlich gleich.
Wir werden dieses Mal nicht dem Wunsch nachgeben, alles zu refaktorisieren. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Die App erneut ausführen { #run-the-app-again }
Sie können die App erneut ausführen:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Wenn Sie zur `/docs`-API-UI gehen, werden Sie sehen, dass sie jetzt aktualisiert ist und nicht mehr erwarten wird, die `id` vom Client beim Erstellen eines Helden zu erhalten, usw.
## Zusammenfassung { #recap }
Sie können [**SQLModel**](https://sqlmodel.tiangolo.com/) verwenden, um mit einer SQL-Datenbank zu interagieren und den Code mit *Datenmodellen* und *Tabellenmodellen* zu vereinfachen.
Sie können viel mehr in der **SQLModel**-Dokumentation lernen, es gibt ein längeres Mini-[Tutorial zur Verwendung von SQLModel mit **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/de/docs/tutorial/static-files.md
================================================
# Statische Dateien { #static-files }
Mit `StaticFiles` können Sie statische Dateien aus einem Verzeichnis automatisch bereitstellen.
## `StaticFiles` verwenden { #use-staticfiles }
* Importieren Sie `StaticFiles`.
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Technische Details
Sie könnten auch `from starlette.staticfiles import StaticFiles` verwenden.
**FastAPI** stellt dasselbe `starlette.staticfiles` auch via `fastapi.staticfiles` bereit, als Annehmlichkeit für Sie, den Entwickler. Es kommt aber tatsächlich direkt von Starlette.
///
### Was ist „Mounten“ { #what-is-mounting }
„Mounten“ bedeutet das Hinzufügen einer vollständigen „unabhängigen“ Anwendung an einem bestimmten Pfad, die sich dann um die Handhabung aller Unterpfade kümmert.
Dies unterscheidet sich von der Verwendung eines `APIRouter`, da eine gemountete Anwendung völlig unabhängig ist. Die OpenAPI und Dokumentation Ihrer Hauptanwendung enthalten nichts von der gemounteten Anwendung, usw.
Weitere Informationen hierzu finden Sie im [Handbuch für fortgeschrittene Benutzer](../advanced/index.md).
## Einzelheiten { #details }
Das erste `"/static"` bezieht sich auf den Unterpfad, auf dem diese „Unteranwendung“ „gemountet“ wird. Daher wird jeder Pfad, der mit `"/static"` beginnt, von ihr verarbeitet.
Das `directory="static"` bezieht sich auf den Namen des Verzeichnisses, das Ihre statischen Dateien enthält.
Das `name="static"` gibt dieser Unteranwendung einen Namen, der intern von **FastAPI** verwendet werden kann.
Alle diese Parameter können anders als „`static`“ lauten, passen Sie sie an die Bedürfnisse und spezifischen Details Ihrer eigenen Anwendung an.
## Weitere Informationen { #more-info }
Weitere Details und Optionen finden Sie in [Starlettes Dokumentation zu statischen Dateien](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/de/docs/tutorial/stream-json-lines.md
================================================
# JSON Lines streamen { #stream-json-lines }
Sie könnten eine Folge von Daten haben, die Sie in einem „Stream“ senden möchten, das können Sie mit **JSON Lines** tun.
/// info | Info
Hinzugefügt in FastAPI 0.134.0.
///
## Was ist ein Stream? { #what-is-a-stream }
„Streaming“ von Daten bedeutet, dass Ihre App damit beginnt, Datenelemente an den Client zu senden, ohne darauf zu warten, dass die gesamte Folge von Elementen fertig ist.
Sie sendet also das erste Element, der Client empfängt es und beginnt mit der Verarbeitung, und Sie erzeugen währenddessen möglicherweise bereits das nächste Element.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Es könnte sogar ein unendlicher Stream sein, bei dem Sie kontinuierlich Daten senden.
## JSON Lines { #json-lines }
In diesen Fällen ist es üblich, „JSON Lines“ zu senden, das ist ein Format, bei dem Sie pro Zeile genau ein JSON-Objekt senden.
Eine Response hätte einen Content-Type von `application/jsonl` (anstelle von `application/json`) und der Body sähe etwa so aus:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
Es ist einem JSON-Array (entspricht einer Python-Liste) sehr ähnlich, aber anstatt in `[]` eingeschlossen zu sein und `,` zwischen den Elementen zu haben, gibt es hier **ein JSON-Objekt pro Zeile**, sie sind durch ein Zeilenumbruchzeichen getrennt.
/// info | Info
Der wichtige Punkt ist, dass Ihre App in der Lage ist, jede Zeile der Reihe nach zu erzeugen, während der Client die vorherigen Zeilen konsumiert.
///
/// note | Technische Details
Da jedes JSON-Objekt durch einen Zeilenumbruch getrennt wird, können sie keine wörtlichen Zeilenumbrüche in ihrem Inhalt enthalten, aber sie können escapte Zeilenumbrüche (`\n`) enthalten, das ist Teil des JSON-Standards.
Normalerweise müssen Sie sich darum aber nicht kümmern, das geschieht automatisch, lesen Sie weiter. 🤓
///
## Anwendungsfälle { #use-cases }
Sie könnten dies verwenden, um Daten von einem **AI LLM**-Service, aus **Logs** oder **Telemetrie**, oder aus anderen Typen von Daten zu streamen, die sich in **JSON**-Items strukturieren lassen.
/// tip | Tipp
Wenn Sie Binärdaten streamen möchten, zum Beispiel Video oder Audio, sehen Sie sich den erweiterten Leitfaden an: [Daten streamen](../advanced/stream-data.md).
///
## JSON Lines mit FastAPI streamen { #stream-json-lines-with-fastapi }
Um JSON Lines mit FastAPI zu streamen, können Sie anstelle von `return` in Ihrer *Pfadoperation-Funktion* `yield` verwenden, um jedes Element der Reihe nach zu erzeugen.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Wenn jedes JSON-Item, das Sie zurücksenden möchten, vom Typ `Item` ist (ein Pydantic-Modell) und es sich um eine async-Funktion handelt, können Sie den Rückgabetyp als `AsyncIterable[Item]` deklarieren:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Wenn Sie den Rückgabetyp deklarieren, wird FastAPI ihn verwenden, um die Daten zu **validieren**, sie in OpenAPI zu **dokumentieren**, sie zu **filtern** und sie mit Pydantic zu **serialisieren**.
/// tip | Tipp
Da Pydantic es auf der **Rust**-Seite serialisiert, erhalten Sie eine deutlich höhere **Leistung** als wenn Sie keinen Rückgabetyp deklarieren.
///
### Nicht-async *Pfadoperation-Funktionen* { #non-async-path-operation-functions }
Sie können auch normale `def`-Funktionen (ohne `async`) verwenden und `yield` auf die gleiche Weise einsetzen.
FastAPI stellt sicher, dass sie korrekt ausgeführt werden, sodass der Event Loop nicht blockiert wird.
Da die Funktion in diesem Fall nicht async ist, wäre der richtige Rückgabetyp `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Kein Rückgabetyp { #no-return-type }
Sie können den Rückgabetyp auch weglassen. FastAPI verwendet dann den [`jsonable_encoder`](./encoder.md), um die Daten in etwas zu konvertieren, das zu JSON serialisiert werden kann, und sendet sie anschließend als JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Server-Sent Events (SSE) { #server-sent-events-sse }
FastAPI hat außerdem erstklassige Unterstützung für Server-Sent Events (SSE), die dem sehr ähnlich sind, aber ein paar zusätzliche Details mitbringen. Sie können im nächsten Kapitel mehr darüber lernen: [Server-Sent Events (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/de/docs/tutorial/testing.md
================================================
# Testen { #testing }
Dank [Starlette](https://www.starlette.dev/testclient/) ist das Testen von **FastAPI**-Anwendungen einfach und macht Spaß.
Es basiert auf [HTTPX](https://www.python-httpx.org), welches wiederum auf der Grundlage von Requests konzipiert wurde, es ist also sehr vertraut und intuitiv.
Damit können Sie [pytest](https://docs.pytest.org/) direkt mit **FastAPI** verwenden.
## `TestClient` verwenden { #using-testclient }
/// info | Info
Um `TestClient` zu verwenden, installieren Sie zunächst [`httpx`](https://www.python-httpx.org).
Erstellen Sie eine [virtuelle Umgebung](../virtual-environments.md), aktivieren Sie sie und installieren Sie es dann, z. B.:
```console
$ pip install httpx
```
///
Importieren Sie `TestClient`.
Erstellen Sie einen `TestClient`, indem Sie ihm Ihre **FastAPI**-Anwendung übergeben.
Erstellen Sie Funktionen mit einem Namen, der mit `test_` beginnt (das sind `pytest`-Konventionen).
Verwenden Sie das `TestClient`-Objekt auf die gleiche Weise wie `httpx`.
Schreiben Sie einfache `assert`-Anweisungen mit den Standard-Python-Ausdrücken, die Sie überprüfen müssen (wiederum, Standard-`pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Tipp
Beachten Sie, dass die Testfunktionen normal `def` und nicht `async def` sind.
Und die Anrufe an den Client sind ebenfalls normale Anrufe, die nicht `await` verwenden.
Dadurch können Sie `pytest` ohne Komplikationen direkt nutzen.
///
/// note | Technische Details
Sie könnten auch `from starlette.testclient import TestClient` verwenden.
**FastAPI** stellt denselben `starlette.testclient` auch via `fastapi.testclient` bereit, als Annehmlichkeit für Sie, den Entwickler. Es kommt aber tatsächlich direkt von Starlette.
///
/// tip | Tipp
Wenn Sie in Ihren Tests neben dem Senden von Requests an Ihre FastAPI-Anwendung auch `async`-Funktionen aufrufen möchten (z. B. asynchrone Datenbankfunktionen), werfen Sie einen Blick auf die [Async-Tests](../advanced/async-tests.md) im Handbuch für fortgeschrittene Benutzer.
///
## Tests separieren { #separating-tests }
In einer echten Anwendung würden Sie Ihre Tests wahrscheinlich in einer anderen Datei haben.
Und Ihre **FastAPI**-Anwendung könnte auch aus mehreren Dateien/Modulen, usw. bestehen.
### **FastAPI** Anwendungsdatei { #fastapi-app-file }
Nehmen wir an, Sie haben eine Dateistruktur wie in [Größere Anwendungen](bigger-applications.md) beschrieben:
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
In der Datei `main.py` haben Sie Ihre **FastAPI**-Anwendung:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Testdatei { #testing-file }
Dann könnten Sie eine Datei `test_main.py` mit Ihren Tests haben. Sie könnte sich im selben Python-Package befinden (dasselbe Verzeichnis mit einer `__init__.py`-Datei):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Da sich diese Datei im selben Package befindet, können Sie relative Importe verwenden, um das Objekt `app` aus dem `main`-Modul (`main.py`) zu importieren:
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
... und haben den Code für die Tests wie zuvor.
## Testen: erweitertes Beispiel { #testing-extended-example }
Nun erweitern wir dieses Beispiel und fügen weitere Details hinzu, um zu sehen, wie verschiedene Teile getestet werden.
### Erweiterte **FastAPI**-Anwendungsdatei { #extended-fastapi-app-file }
Fahren wir mit der gleichen Dateistruktur wie zuvor fort:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Nehmen wir an, dass die Datei `main.py` mit Ihrer **FastAPI**-Anwendung jetzt einige andere **Pfadoperationen** hat.
Sie verfügt über eine `GET`-Operation, die einen Fehler zurückgeben könnte.
Sie verfügt über eine `POST`-Operation, die mehrere Fehler zurückgeben könnte.
Beide *Pfadoperationen* erfordern einen `X-Token`-Header.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Erweiterte Testdatei { #extended-testing-file }
Anschließend könnten Sie `test_main.py` mit den erweiterten Tests aktualisieren:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Wenn Sie möchten, dass der Client Informationen im Request übergibt und Sie nicht wissen, wie das geht, können Sie suchen (googeln), wie es mit `httpx` gemacht wird, oder sogar, wie es mit `requests` gemacht wird, da das Design von HTTPX auf dem Design von Requests basiert.
Dann machen Sie in Ihren Tests einfach das gleiche.
Z. B.:
* Um einen *Pfad*- oder *Query*-Parameter zu übergeben, fügen Sie ihn der URL selbst hinzu.
* Um einen JSON-Body zu übergeben, übergeben Sie ein Python-Objekt (z. B. ein `dict`) an den Parameter `json`.
* Wenn Sie *Formulardaten* anstelle von JSON senden müssen, verwenden Sie stattdessen den `data`-Parameter.
* Um *Header* zu übergeben, verwenden Sie ein `dict` im `headers`-Parameter.
* Für *Cookies* ein `dict` im `cookies`-Parameter.
Weitere Informationen zum Übergeben von Daten an das Backend (mithilfe von `httpx` oder dem `TestClient`) finden Sie in der [HTTPX-Dokumentation](https://www.python-httpx.org).
/// info | Info
Beachten Sie, dass der `TestClient` Daten empfängt, die nach JSON konvertiert werden können, keine Pydantic-Modelle.
Wenn Sie ein Pydantic-Modell in Ihrem Test haben und dessen Daten während des Testens an die Anwendung senden möchten, können Sie den `jsonable_encoder` verwenden, der in [JSON-kompatibler Encoder](encoder.md) beschrieben wird.
///
## Tests ausführen { #run-it }
Danach müssen Sie nur noch `pytest` installieren.
Erstellen Sie eine [virtuelle Umgebung](../virtual-environments.md), aktivieren Sie sie und installieren Sie es dann, z. B.:
```console
$ pip install pytest
---> 100%
```
Es erkennt die Dateien und Tests automatisch, führt sie aus und berichtet Ihnen die Ergebnisse.
Führen Sie die Tests aus, mit:
================================================
FILE: docs/de/docs/virtual-environments.md
================================================
# Virtuelle Umgebungen { #virtual-environments }
Wenn Sie an Python-Projekten arbeiten, sollten Sie wahrscheinlich eine **virtuelle Umgebung** (oder einen ähnlichen Mechanismus) verwenden, um die Packages, die Sie für jedes Projekt installieren, zu isolieren.
/// info | Info
Wenn Sie bereits über virtuelle Umgebungen Bescheid wissen, wie man sie erstellt und verwendet, möchten Sie diesen Abschnitt vielleicht überspringen. 🤓
///
/// tip | Tipp
Eine **virtuelle Umgebung** unterscheidet sich von einer **Umgebungsvariable**.
Eine **Umgebungsvariable** ist eine Variable im System, die von Programmen verwendet werden kann.
Eine **virtuelle Umgebung** ist ein Verzeichnis mit einigen Dateien darin.
///
/// info | Info
Diese Seite wird Ihnen beibringen, wie Sie **virtuelle Umgebungen** verwenden und wie sie funktionieren.
Wenn Sie bereit sind, ein **Tool zu verwenden, das alles für Sie verwaltet** (einschließlich der Installation von Python), probieren Sie [uv](https://github.com/astral-sh/uv).
///
## Ein Projekt erstellen { #create-a-project }
Erstellen Sie zuerst ein Verzeichnis für Ihr Projekt.
Was ich normalerweise mache, ist, dass ich ein Verzeichnis namens `code` in meinem Home/Benutzerverzeichnis erstelle.
Und darin erstelle ich ein Verzeichnis pro Projekt.
```console
// Gehe zum Home-Verzeichnis
$ cd
// Erstelle ein Verzeichnis für alle Ihre Code-Projekte
$ mkdir code
// Gehe in dieses Code-Verzeichnis
$ cd code
// Erstelle ein Verzeichnis für dieses Projekt
$ mkdir awesome-project
// Gehe in dieses Projektverzeichnis
$ cd awesome-project
```
## Eine virtuelle Umgebung erstellen { #create-a-virtual-environment }
Wenn Sie zum **ersten Mal** an einem Python-Projekt arbeiten, erstellen Sie eine virtuelle Umgebung **innerhalb Ihres Projekts**.
/// tip | Tipp
Sie müssen dies nur **einmal pro Projekt** tun, nicht jedes Mal, wenn Sie daran arbeiten.
///
//// tab | `venv`
Um eine virtuelle Umgebung zu erstellen, können Sie das `venv`-Modul verwenden, das mit Python geliefert wird.
```console
$ python -m venv .venv
```
/// details | Was dieser Befehl bedeutet
* `python`: das Programm namens `python` verwenden
* `-m`: ein Modul als Skript aufrufen, wir geben als nächstes an, welches Modul
* `venv`: das Modul namens `venv` verwenden, das normalerweise mit Python installiert wird
* `.venv`: die virtuelle Umgebung im neuen Verzeichnis `.venv` erstellen
///
////
//// tab | `uv`
Wenn Sie [`uv`](https://github.com/astral-sh/uv) installiert haben, können Sie es verwenden, um eine virtuelle Umgebung zu erstellen.
```console
$ uv venv
```
/// tip | Tipp
Standardmäßig erstellt `uv` eine virtuelle Umgebung in einem Verzeichnis namens `.venv`.
Aber Sie könnten es anpassen, indem Sie ein zusätzliches Argument mit dem Verzeichnisnamen übergeben.
///
////
Dieser Befehl erstellt eine neue virtuelle Umgebung in einem Verzeichnis namens `.venv`.
/// details | `.venv` oder ein anderer Name
Sie könnten die virtuelle Umgebung in einem anderen Verzeichnis erstellen, aber es ist eine Konvention, sie `.venv` zu nennen.
///
## Die virtuelle Umgebung aktivieren { #activate-the-virtual-environment }
Aktivieren Sie die neue virtuelle Umgebung, damit jeder Python-Befehl, den Sie ausführen oder jedes Paket, das Sie installieren, diese Umgebung verwendet.
/// tip | Tipp
Tun Sie dies **jedes Mal**, wenn Sie eine **neue Terminalsitzung** starten, um an dem Projekt zu arbeiten.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Oder wenn Sie Bash für Windows verwenden (z. B. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | Tipp
Jedes Mal, wenn Sie ein **neues Paket** in dieser Umgebung installieren, aktivieren Sie die Umgebung erneut.
So stellen Sie sicher, dass, wenn Sie ein **Terminalprogramm (CLI)** verwenden, das durch dieses Paket installiert wurde, Sie das aus Ihrer virtuellen Umgebung verwenden und nicht eines, das global installiert ist, wahrscheinlich mit einer anderen Version als der, die Sie benötigen.
///
## Testen, ob die virtuelle Umgebung aktiv ist { #check-the-virtual-environment-is-active }
Testen Sie, dass die virtuelle Umgebung aktiv ist (der vorherige Befehl funktioniert hat).
/// tip | Tipp
Dies ist **optional**, aber es ist eine gute Möglichkeit, **zu überprüfen**, ob alles wie erwartet funktioniert und Sie die beabsichtigte virtuelle Umgebung verwenden.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Wenn es das `python`-Binary in `.venv/bin/python` anzeigt, innerhalb Ihres Projekts (in diesem Fall `awesome-project`), dann hat es funktioniert. 🎉
////
//// tab | Windows PowerShell
Wenn es das `python`-Binary in `.venv\Scripts\python` anzeigt, innerhalb Ihres Projekts (in diesem Fall `awesome-project`), dann hat es funktioniert. 🎉
////
## `pip` aktualisieren { #upgrade-pip }
/// tip | Tipp
Wenn Sie [`uv`](https://github.com/astral-sh/uv) verwenden, würden Sie das verwenden, um Dinge zu installieren anstelle von `pip`, sodass Sie `pip` nicht aktualisieren müssen. 😎
///
Wenn Sie `pip` verwenden, um Pakete zu installieren (es wird standardmäßig mit Python geliefert), sollten Sie es auf die neueste Version **aktualisieren**.
Viele exotische Fehler beim Installieren eines Pakets werden einfach dadurch gelöst, dass zuerst `pip` aktualisiert wird.
/// tip | Tipp
Normalerweise würden Sie dies **einmal** tun, unmittelbar nachdem Sie die virtuelle Umgebung erstellt haben.
///
Stellen Sie sicher, dass die virtuelle Umgebung aktiv ist (mit dem obigen Befehl) und führen Sie dann aus:
/// tip | Tipp
Manchmal kann beim Versuch, `pip` zu aktualisieren, der Fehler **`No module named pip`** auftreten.
Wenn das passiert, installieren und aktualisieren Sie `pip` mit dem folgenden Befehl:
Dieser Befehl installiert `pip`, falls es noch nicht installiert ist, und stellt außerdem sicher, dass die installierte Version von `pip` mindestens so aktuell ist wie die in `ensurepip` verfügbare.
///
## `.gitignore` hinzufügen { #add-gitignore }
Wenn Sie **Git** verwenden (was Sie sollten), fügen Sie eine `.gitignore`-Datei hinzu, um alles in Ihrem `.venv` von Git auszuschließen.
/// tip | Tipp
Wenn Sie [`uv`](https://github.com/astral-sh/uv) verwendet haben, um die virtuelle Umgebung zu erstellen, hat es dies bereits für Sie getan, Sie können diesen Schritt überspringen. 😎
///
/// tip | Tipp
Tun Sie dies **einmal**, unmittelbar nachdem Sie die virtuelle Umgebung erstellt haben.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Was dieser Befehl bedeutet
* `echo "*"`: wird den Text `*` im Terminal „drucken“ (der nächste Teil ändert das ein wenig)
* `>`: alles, was durch den Befehl links von `>` im Terminal ausgegeben wird, sollte nicht gedruckt, sondern stattdessen in die Datei geschrieben werden, die rechts von `>` kommt
* `.gitignore`: der Name der Datei, in die der Text geschrieben werden soll
Und `*` bedeutet für Git „alles“. Also wird alles im `.venv`-Verzeichnis ignoriert.
Dieser Befehl erstellt eine Datei `.gitignore` mit dem Inhalt:
```gitignore
*
```
///
## Pakete installieren { #install-packages }
Nachdem Sie die Umgebung aktiviert haben, können Sie Pakete darin installieren.
/// tip | Tipp
Tun Sie dies **einmal**, wenn Sie die Pakete installieren oder aktualisieren, die Ihr Projekt benötigt.
Wenn Sie eine Version aktualisieren oder ein neues Paket hinzufügen müssen, würden Sie **dies erneut tun**.
///
### Pakete direkt installieren { #install-packages-directly }
Wenn Sie es eilig haben und keine Datei verwenden möchten, um die Paketanforderungen Ihres Projekts zu deklarieren, können Sie sie direkt installieren.
/// tip | Tipp
Es ist eine (sehr) gute Idee, die Pakete und Versionen, die Ihr Programm benötigt, in einer Datei zu speichern (zum Beispiel `requirements.txt` oder `pyproject.toml`).
///
//// tab | `pip`
////
### Installation von `requirements.txt` { #install-from-requirements-txt }
Wenn Sie eine `requirements.txt` haben, können Sie diese nun verwenden, um deren Pakete zu installieren.
//// tab | `pip`
////
/// details | `requirements.txt`
Eine `requirements.txt` mit einigen Paketen könnte folgendermaßen aussehen:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Ihr Programm ausführen { #run-your-program }
Nachdem Sie die virtuelle Umgebung aktiviert haben, können Sie Ihr Programm ausführen, und es wird das Python innerhalb Ihrer virtuellen Umgebung mit den Paketen verwenden, die Sie dort installiert haben.
```console
$ python main.py
Hello World
```
## Ihren Editor konfigurieren { #configure-your-editor }
Sie würden wahrscheinlich einen Editor verwenden, stellen Sie sicher, dass Sie ihn so konfigurieren, dass er dieselbe virtuelle Umgebung verwendet, die Sie erstellt haben (er wird sie wahrscheinlich automatisch erkennen), sodass Sie Autovervollständigungen und Inline-Fehler erhalten können.
Zum Beispiel:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Tipp
Normalerweise müssen Sie dies nur **einmal** tun, wenn Sie die virtuelle Umgebung erstellen.
///
## Die virtuelle Umgebung deaktivieren { #deactivate-the-virtual-environment }
Sobald Sie mit der Arbeit an Ihrem Projekt fertig sind, können Sie die virtuelle Umgebung **deaktivieren**.
```console
$ deactivate
```
Auf diese Weise, wenn Sie `python` ausführen, wird nicht versucht, es aus dieser virtuellen Umgebung mit den dort installierten Paketen auszuführen.
## Bereit zu arbeiten { #ready-to-work }
Jetzt sind Sie bereit, mit Ihrem Projekt zu arbeiten.
/// tip | Tipp
Möchten Sie verstehen, was das alles oben bedeutet?
Lesen Sie weiter. 👇🤓
///
## Warum virtuelle Umgebungen { #why-virtual-environments }
Um mit FastAPI zu arbeiten, müssen Sie [Python](https://www.python.org/) installieren.
Danach müssen Sie FastAPI und alle anderen Pakete, die Sie verwenden möchten, **installieren**.
Um Pakete zu installieren, würden Sie normalerweise den `pip`-Befehl verwenden, der mit Python geliefert wird (oder ähnliche Alternativen).
Wenn Sie jedoch `pip` direkt verwenden, werden die Pakete in Ihrer **globalen Python-Umgebung** (der globalen Installation von Python) installiert.
### Das Problem { #the-problem }
Was ist also das Problem beim Installieren von Paketen in der globalen Python-Umgebung?
Irgendwann werden Sie wahrscheinlich viele verschiedene Programme schreiben, die von **verschiedenen Paketen** abhängen. Und einige dieser Projekte, an denen Sie arbeiten, werden von **verschiedenen Versionen** desselben Pakets abhängen. 😱
Zum Beispiel könnten Sie ein Projekt namens `philosophers-stone` erstellen, dieses Programm hängt von einem anderen Paket namens **`harry`, Version `1`** ab. Also müssen Sie `harry` installieren.
```mermaid
flowchart LR
stone(philosophers-stone) -->|benötigt| harry-1[harry v1]
```
Dann erstellen Sie zu einem späteren Zeitpunkt ein weiteres Projekt namens `prisoner-of-azkaban`, und dieses Projekt hängt ebenfalls von `harry` ab, aber dieses Projekt benötigt **`harry` Version `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |benötigt| harry-3[harry v3]
```
Aber jetzt ist das Problem, wenn Sie die Pakete global (in der globalen Umgebung) installieren anstatt in einer lokalen **virtuellen Umgebung**, müssen Sie wählen, welche Version von `harry` zu installieren ist.
Wenn Sie `philosophers-stone` ausführen möchten, müssen Sie zuerst `harry` Version `1` installieren, zum Beispiel mit:
```console
$ pip install "harry==1"
```
Und dann hätten Sie `harry` Version `1` in Ihrer globalen Python-Umgebung installiert.
```mermaid
flowchart LR
subgraph global[globale Umgebung]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone-Projekt]
stone(philosophers-stone) -->|benötigt| harry-1
end
```
Aber dann, wenn Sie `prisoner-of-azkaban` ausführen möchten, müssen Sie `harry` Version `1` deinstallieren und `harry` Version `3` installieren (oder einfach die Version `3` installieren, was die Version `1` automatisch deinstallieren würde).
```console
$ pip install "harry==3"
```
Und dann hätten Sie `harry` Version `3` in Ihrer globalen Python-Umgebung installiert.
Und wenn Sie versuchen, `philosophers-stone` erneut auszuführen, besteht die Möglichkeit, dass es **nicht funktioniert**, weil es `harry` Version `1` benötigt.
```mermaid
flowchart LR
subgraph global[globale Umgebung]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone-Projekt]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban-Projekt]
azkaban(prisoner-of-azkaban) --> |benötigt| harry-3
end
```
/// tip | Tipp
Es ist sehr üblich in Python-Paketen, alles zu versuchen, **Breaking Changes** in **neuen Versionen** zu vermeiden, aber es ist besser, auf Nummer sicher zu gehen und neue Versionen absichtlich zu installieren und wenn Sie die Tests ausführen können, sicherzustellen, dass alles korrekt funktioniert.
///
Stellen Sie sich das jetzt mit **vielen** anderen **Paketen** vor, von denen alle Ihre **Projekte abhängen**. Das ist sehr schwierig zu verwalten. Und Sie würden wahrscheinlich einige Projekte mit einigen **inkompatiblen Versionen** der Pakete ausführen und nicht wissen, warum etwas nicht funktioniert.
Darüber hinaus könnte es je nach Ihrem Betriebssystem (z. B. Linux, Windows, macOS) bereits mit installiertem Python geliefert worden sein. Und in diesem Fall hatte es wahrscheinlich einige Pakete mit bestimmten Versionen **installiert**, die von Ihrem System benötigt werden. Wenn Sie Pakete in der globalen Python-Umgebung installieren, könnten Sie einige der Programme, die mit Ihrem Betriebssystem geliefert wurden, **kaputtmachen**.
## Wo werden Pakete installiert { #where-are-packages-installed }
Wenn Sie Python installieren, werden einige Verzeichnisse mit einigen Dateien auf Ihrem Rechner erstellt.
Einige dieser Verzeichnisse sind dafür zuständig, alle Pakete, die Sie installieren, aufzunehmen.
Wenn Sie ausführen:
```console
// Führen Sie dies jetzt nicht aus, es ist nur ein Beispiel 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Das lädt eine komprimierte Datei mit dem FastAPI-Code herunter, normalerweise von [PyPI](https://pypi.org/project/fastapi/).
Es wird auch Dateien für andere Pakete **herunterladen**, von denen FastAPI abhängt.
Dann wird es all diese Dateien **extrahieren** und sie in ein Verzeichnis auf Ihrem Rechner legen.
Standardmäßig werden diese heruntergeladenen und extrahierten Dateien in das Verzeichnis gelegt, das mit Ihrer Python-Installation kommt, das ist die **globale Umgebung**.
## Was sind virtuelle Umgebungen { #what-are-virtual-environments }
Die Lösung für die Probleme, alle Pakete in der globalen Umgebung zu haben, besteht darin, eine **virtuelle Umgebung für jedes Projekt** zu verwenden, an dem Sie arbeiten.
Eine virtuelle Umgebung ist ein **Verzeichnis**, sehr ähnlich zu dem globalen, in dem Sie die Pakete für ein Projekt installieren können.
Auf diese Weise hat jedes Projekt seine eigene virtuelle Umgebung (`.venv`-Verzeichnis) mit seinen eigenen Paketen.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone-Projekt]
stone(philosophers-stone) --->|benötigt| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban-Projekt]
azkaban(prisoner-of-azkaban) --->|benötigt| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Was bedeutet das Aktivieren einer virtuellen Umgebung { #what-does-activating-a-virtual-environment-mean }
Wenn Sie eine virtuelle Umgebung aktivieren, zum Beispiel mit:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Oder wenn Sie Bash für Windows verwenden (z. B. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Dieser Befehl erstellt oder ändert einige [Umgebungsvariablen](environment-variables.md), die für die nächsten Befehle verfügbar sein werden.
Eine dieser Variablen ist die `PATH`-Umgebungsvariable.
/// tip | Tipp
Sie können mehr über die `PATH`-Umgebungsvariable im Abschnitt [Umgebungsvariablen](environment-variables.md#path-environment-variable) erfahren.
///
Das Aktivieren einer virtuellen Umgebung fügt deren Pfad `.venv/bin` (auf Linux und macOS) oder `.venv\Scripts` (auf Windows) zur `PATH`-Umgebungsvariable hinzu.
Angenommen, die `PATH`-Umgebungsvariable sah vor dem Aktivieren der Umgebung so aus:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Das bedeutet, dass das System nach Programmen sucht in:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Das bedeutet, dass das System nach Programmen sucht in:
* `C:\Windows\System32`
////
Nach dem Aktivieren der virtuellen Umgebung würde die `PATH`-Umgebungsvariable folgendermaßen aussehen:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Das bedeutet, dass das System nun zuerst nach Programmen sucht in:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
bevor es in den anderen Verzeichnissen sucht.
Wenn Sie also `python` im Terminal eingeben, wird das System das Python-Programm in
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
finden und dieses verwenden.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Das bedeutet, dass das System nun zuerst nach Programmen sucht in:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
bevor es in den anderen Verzeichnissen sucht.
Wenn Sie also `python` im Terminal eingeben, wird das System das Python-Programm in
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
finden und dieses verwenden.
////
Ein wichtiger Punkt ist, dass es den Pfad der virtuellen Umgebung am **Anfang** der `PATH`-Umgebungsvariable platziert. Das System wird es **vor** allen anderen verfügbaren Pythons finden. Auf diese Weise, wenn Sie `python` ausführen, wird das Python **aus der virtuellen Umgebung** verwendet anstelle eines anderen `python` (zum Beispiel, einem `python` aus einer globalen Umgebung).
Das Aktivieren einer virtuellen Umgebung ändert auch ein paar andere Dinge, aber dies ist eines der wichtigsten Dinge, die es tut.
## Testen einer virtuellen Umgebung { #checking-a-virtual-environment }
Wenn Sie testen, ob eine virtuelle Umgebung aktiv ist, zum Beispiel mit:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
bedeutet das, dass das `python`-Programm, das verwendet wird, das in der **virtuellen Umgebung** ist.
Sie verwenden `which` auf Linux und macOS und `Get-Command` in Windows PowerShell.
So funktioniert dieser Befehl: Er wird in der `PATH`-Umgebungsvariable nachsehen und **jeden Pfad in der Reihenfolge durchgehen**, um das Programm namens `python` zu finden. Sobald er es findet, wird er Ihnen **den Pfad** zu diesem Programm anzeigen.
Der wichtigste Punkt ist, dass, wenn Sie `python` aufrufen, genau dieses „`python`“ ausgeführt wird.
So können Sie überprüfen, ob Sie sich in der richtigen virtuellen Umgebung befinden.
/// tip | Tipp
Es ist einfach, eine virtuelle Umgebung zu aktivieren, ein Python zu bekommen und dann **zu einem anderen Projekt zu wechseln**.
Und das zweite Projekt **würde nicht funktionieren**, weil Sie das **falsche Python** verwenden, aus einer virtuellen Umgebung für ein anderes Projekt.
Es ist nützlich, überprüfen zu können, welches `python` verwendet wird. 🤓
///
## Warum eine virtuelle Umgebung deaktivieren { #why-deactivate-a-virtual-environment }
Zum Beispiel könnten Sie an einem Projekt `philosophers-stone` arbeiten, diese virtuelle Umgebung **aktivieren**, Pakete installieren und mit dieser Umgebung arbeiten.
Und dann möchten Sie an **einem anderen Projekt** `prisoner-of-azkaban` arbeiten.
Sie gehen zu diesem Projekt:
```console
$ cd ~/code/prisoner-of-azkaban
```
Wenn Sie die virtuelle Umgebung für `philosophers-stone` nicht deaktivieren, wird beim Ausführen von `python` im Terminal versucht, das Python von `philosophers-stone` zu verwenden.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Fehler beim Importieren von sirius, es ist nicht installiert 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Wenn Sie jedoch die virtuelle Umgebung deaktivieren und die neue für `prisoner-of-askaban` aktivieren, wird beim Ausführen von `python` das Python aus der virtuellen Umgebung in `prisoner-of-azkaban` verwendet.
```console
$ cd ~/code/prisoner-of-azkaban
// Sie müssen nicht im alten Verzeichnis sein, um zu deaktivieren, Sie können dies überall tun, sogar nachdem Sie zum anderen Projekt gewechselt haben 😎
$ deactivate
// Die virtuelle Umgebung in prisoner-of-azkaban/.venv 🚀 aktivieren
$ source .venv/bin/activate
// Jetzt, wenn Sie python ausführen, wird das Paket sirius in dieser virtuellen Umgebung gefunden ✨
$ python main.py
I solemnly swear 🐺
```
## Alternativen { #alternatives }
Dies ist ein einfacher Leitfaden, um Ihnen den Einstieg zu erleichtern und Ihnen beizubringen, wie alles **unter der Haube** funktioniert.
Es gibt viele **Alternativen** zur Verwaltung von virtuellen Umgebungen, Paketabhängigkeiten (Anforderungen), Projekten.
Sobald Sie bereit sind und ein Tool verwenden möchten, das **das gesamte Projekt verwaltet**, Paketabhängigkeiten, virtuelle Umgebungen usw., würde ich Ihnen vorschlagen, [uv](https://github.com/astral-sh/uv) auszuprobieren.
`uv` kann viele Dinge tun, es kann:
* **Python für Sie installieren**, einschließlich verschiedener Versionen
* Die **virtuelle Umgebung** für Ihre Projekte verwalten
* **Pakete installieren**
* Paket**abhängigkeiten und Versionen** für Ihr Projekt verwalten
* Sicherstellen, dass Sie eine **exakte** Menge an Paketen und Versionen zur Installation haben, einschließlich ihrer Abhängigkeiten, damit Sie sicher sein können, dass Sie Ihr Projekt in der Produktionsumgebung genauso ausführen können wie auf Ihrem Rechner während der Entwicklung, dies wird **Locking** genannt
* Und viele andere Dinge
## Fazit { #conclusion }
Wenn Sie das alles gelesen und verstanden haben, wissen Sie jetzt **viel mehr** über virtuelle Umgebungen als viele Entwickler da draußen. 🤓
Das Wissen über diese Details wird in Zukunft wahrscheinlich nützlich sein, wenn Sie etwas debuggen, das komplex erscheint, aber Sie werden wissen, **wie alles unter der Haube funktioniert**. 😎
================================================
FILE: docs/de/llm-prompt.md
================================================
### Target language
Translate to German (Deutsch).
Language code: de.
### Grammar to use when talking to the reader
Use the formal grammar (use `Sie` instead of `Du`).
### Quotes
1) Convert neutral double quotes (`"`) to German double typographic quotes (`„` and `“`). Convert neutral single quotes (`'`) to German single typographic quotes (`‚` and `‘`).
Do NOT convert quotes in code snippets and code blocks to their German typographic equivalents.
Examples:
Source (English):
```
"Hello world"
“Hello Universe”
"He said: 'Hello'"
“my name is ‘Nils’”
`"__main__"`
`"items"`
```
Result (German):
```
„Hallo Welt“
„Hallo Universum“
„Er sagte: ‚Hallo‘“
„Mein Name ist ‚Nils‘“
`"__main__"`
`"items"`
```
### Ellipsis
- Make sure there is a space between an ellipsis and a word following or preceding the ellipsis.
Examples:
Source (English):
```
...as we intended.
...this would work:
...etc.
others...
More to come...
```
Result (German):
```
... wie wir es beabsichtigt hatten.
... das würde funktionieren:
... usw.
Andere ...
Später mehr ...
```
- This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there.
### Headings
- Translate headings using the infinite form.
Examples:
Source (English):
```
## Create a Project { #create-a-project }
```
Result (German):
```
## Ein Projekt erstellen { #create-a-project }
```
Do NOT translate with (German):
```
## Erstellen Sie ein Projekt { #create-a-project }
```
Source (English):
```
# Install Packages { #install-packages }
```
Translate with (German):
```
# Pakete installieren { #install-packages }
```
Do NOT translate with (German):
```
# Installieren Sie Pakete { #install-packages }
```
Source (English):
```
### Run Your Program { #run-your-program }
```
Translate with (German):
```
### Ihr Programm ausführen { #run-your-program }
```
Do NOT translate with (German):
```
### Führen Sie Ihr Programm aus { #run-your-program }
```
- Make sure that the translated part of the heading does not end with a period.
Example:
Source (English):
```
## Another module with `APIRouter` { #another-module-with-apirouter }
```
Translate with (German):
```
## Ein weiteres Modul mit `APIRouter` { #another-module-with-apirouter }
```
Do NOT translate with (German) – notice the added period:
```
## Ein weiteres Modul mit `APIRouter`. { #another-module-with-apirouter }
```
- Replace occurrences of literal ` - ` (a space followed by a hyphen followed by a space) with ` – ` (a space followed by a dash followed by a space) in the translated part of the heading.
Example:
Source (English):
```
# FastAPI in Containers - Docker { #fastapi-in-containers-docker }
```
Translate with (German) – notice the dash:
```
# FastAPI in Containern – Docker { #fastapi-in-containers-docker }
```
Do NOT translate with (German) – notice the hyphen:
```
# FastAPI in Containern - Docker { #fastapi-in-containers-docker }
```
- Do not apply rule 3 when there is no space before or no space after the hyphen.
Example:
Source (English):
```
## Type hints and annotations { #type-hints-and-annotations }
```
Translate with (German) - notice the hyphen:
```
## Typhinweise und -annotationen { #type-hints-and-annotations }
```
Do NOT translate with (German) - notice the dash:
```
## Typhinweise und –annotationen { #type-hints-and-annotations }
```
- Do not modify the hyphens in the content in headers inside of curly braces, which you shall not translate.
### German instructions, when to use and when not to use hyphens in words (written in first person, which is you).
In der Regel versuche ich so weit wie möglich Worte zusammenzuschreiben, also ohne Bindestrich, es sei denn, es ist Konkretesding-Klassevondingen, etwa «Pydantic-Modell» (aber: «Datenbankmodell»), «Python-Modul» (aber: «Standardmodul»). Ich setze auch einen Bindestrich, wenn er die gleichen Buchstaben verbindet, etwa «Enum-Member», «Cloud-Dienst», «Template-Engine». Oder wenn das Wort sonst einfach zu lang wird, etwa, «Performance-Optimierung». Oder um etwas visuell besser zu dokumentieren, etwa «Pfadoperation-Dekorator», «Pfadoperation-Funktion».
### German instructions about difficult to translate technical terms (written in first person, which is you)
Ich versuche nicht, alles einzudeutschen. Das bezieht sich besonders auf Begriffe aus dem Bereich der Programmierung. Ich wandele zwar korrekt in Großschreibung um und setze Bindestriche, wo notwendig, aber ansonsten lasse ich solch ein Wort unverändert. Beispielsweise wird aus dem englischen Wort «string» in der deutschen Übersetzung «String», aber nicht «Zeichenkette». Oder aus dem englischen Wort «request body» wird in der deutschen Übersetzung «Requestbody», aber nicht «Anfragekörper». Oder aus dem englischen «response» wird im Deutschen «Response», aber nicht «Antwort».
### List of English terms and their preferred German translations
Below is a list of English terms and their preferred German translations, separated by a colon (:). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them. In the below list, a term or a translation may be followed by an explanation in brackets, which explains when to translate the term this way. If a translation is preceded by `NOT`, then that means: do NOT use this translation for this term. English nouns, starting with the word `the`, have the German genus – `der`, `die`, `das` – prepended to their German translation, to help you to grammatically decline them in the translation. They are given in singular case, unless they have `(plural)` attached, which means they are given in plural case. Verbs are given in the full infinitive – starting with the word `to`.
* /// check: /// check | Testen
* /// danger: /// danger | Gefahr
* /// info: /// info | Info
* /// note | Technical Details: /// note | Technische Details
* /// note: /// note | Hinweis
* /// tip: /// tip | Tipp
* /// warning: /// warning | Achtung
* you: Sie
* your: Ihr
* e.g: z. B.
* etc.: usw.
* ref: Ref.
* the Tutorial - User guide: das Tutorial – Benutzerhandbuch
* the Advanced User Guide: das Handbuch für fortgeschrittene Benutzer
* the SQLModel docs: die SQLModel-Dokumentation
* the docs: die Dokumentation (use singular case)
* the env var: die Umgebungsvariable
* the `PATH` environment variable: die `PATH`-Umgebungsvariable
* the `PATH`: der `PATH`
* the `requirements.txt`: die `requirements.txt`
* the API Router: der API-Router
* the Authorization-Header: der Autorisierungsheader
* the `Authorization`-Header: der `Authorization`-Header
* the background task: der Hintergrundtask
* the button: der Button
* the cloud provider: der Cloudanbieter
* the CLI: Das CLI
* the coverage: Die Testabdeckung
* the command line interface: Das Kommandozeileninterface
* the default value: der Defaultwert
* the default value: NOT der Standardwert
* the default declaration: die Default-Deklaration
* the deployment: das Deployment
* the dict: das Dict
* the dictionary: das Dictionary
* the enumeration: die Enumeration
* the enum: das Enum
* the engine: die Engine
* the error response: die Error-Response
* the event: das Event
* the exception: die Exception
* the exception handler: der Exceptionhandler
* the form model: das Formularmodell
* the form body: der Formularbody
* the header: der Header
* the headers (plural): die Header
* in headers (plural): in Headern
* the forwarded header: der Forwarded-Header
* the lifespan event: das Lifespan-Event
* the lock: der Lock
* the locking: das Locking
* the mobile application: die Mobile-Anwendung
* the model object: das Modellobjekt
* the mounting: das Mounten
* mounted: gemountet
* the origin: das Origin
* the override: Die Überschreibung
* the parameter: der Parameter
* the parameters (plural): die Parameter
* the function parameter: der Funktionsparameter
* the default parameter: der Defaultparameter
* the body parameter: der Body-Parameter
* the request body parameter: der Requestbody-Parameter
* the path parameter: der Pfad-Parameter
* the query parameter: der Query-Parameter
* the cookie parameter: der Cookie-Parameter
* the header parameter: der Header-Parameter
* the form parameter: der Formular-Parameter
* the payload: die Payload
* the performance: NOT die Performance
* the query: die Query
* the recap: die Zusammenfassung
* the request (what the client sends to the server): der Request
* the request body: der Requestbody
* the request bodies (plural): die Requestbodys
* the response (what the server sends back to the client): die Response
* the return type: der Rückgabetyp
* the return value: der Rückgabewert
* the startup (the event of the app): der Startup
* the shutdown (the event of the app): der Shutdown
* the startup event: das Startup-Event
* the shutdown event: das Shutdown-Event
* the startup (of the server): das Hochfahren
* the startup (the company): das Startup
* the SDK: das SDK
* the tag: der Tag
* the type annotation: die Typannotation
* the type hint: der Typhinweis
* the wildcard: die Wildcard
* the worker class: die Workerklasse
* the worker class: NOT die Arbeiterklasse
* the worker process: der Workerprozess
* the worker process: NOT der Arbeiterprozess
* to commit: committen
* to deploy (in the cloud): deployen
* to modify: ändern
* to serve (an application): bereitstellen
* to serve (a response): ausliefern
* to serve: NOT bedienen
* to upgrade: aktualisieren
* to wrap: wrappen
* to wrap: NOT hüllen
* `foo` as a `type`: `foo` vom Typ `type`
* `foo` as a `type`: `foo`, ein `type`
* FastAPI's X: FastAPIs X
* Starlette's Y: Starlettes Y
* X is case-sensitive: Groß-/Kleinschreibung ist relevant in X
* X is case-insensitive: Groß-/Kleinschreibung ist nicht relevant in X
* standard Python: Standard-Python
* deprecated: deprecatet
### Other rules
Preserve indentation. Keep emojis. Encode in utf-8. Use Linux line breaks (LF).
================================================
FILE: docs/de/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/en/data/contributors.yml
================================================
tiangolo:
login: tiangolo
count: 942
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
dependabot:
login: dependabot
count: 189
avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4
url: https://github.com/apps/dependabot
YuriiMotov:
login: YuriiMotov
count: 70
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
alejsdev:
login: alejsdev
count: 56
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4
url: https://github.com/alejsdev
pre-commit-ci:
login: pre-commit-ci
count: 50
avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4
url: https://github.com/apps/pre-commit-ci
github-actions:
login: github-actions
count: 26
avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
url: https://github.com/apps/github-actions
Kludex:
login: Kludex
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
svlandeg:
login: svlandeg
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
url: https://github.com/svlandeg
dmontagu:
login: dmontagu
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
url: https://github.com/dmontagu
nilslindemann:
login: nilslindemann
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
url: https://github.com/nilslindemann
euri10:
login: euri10
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
kantandane:
login: kantandane
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/3978368?u=cccc199291f991a73b1ebba5abc735a948e0bd16&v=4
url: https://github.com/kantandane
zhaohan-dong:
login: zhaohan-dong
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/65422392?u=8260f8781f50248410ebfa4c9bf70e143fe5c9f2&v=4
url: https://github.com/zhaohan-dong
mariacamilagl:
login: mariacamilagl
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
url: https://github.com/mariacamilagl
handabaldeep:
login: handabaldeep
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/12239103?u=6c39ef15d14c6d5211f5dd775cc4842f8d7f2f3a&v=4
url: https://github.com/handabaldeep
vishnuvskvkl:
login: vishnuvskvkl
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/84698110?u=8af5de0520dd4fa195f53c2850a26f57c0f6bc64&v=4
url: https://github.com/vishnuvskvkl
alissadb:
login: alissadb
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/96190409?u=be42d85938c241be781505a5a872575be28b2906&v=4
url: https://github.com/alissadb
alv2017:
login: alv2017
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
url: https://github.com/alv2017
wshayes:
login: wshayes
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
samuelcolvin:
login: samuelcolvin
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
url: https://github.com/samuelcolvin
waynerv:
login: waynerv
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
musicinmybrain:
login: musicinmybrain
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/6898909?u=9010312053e7141383b9bdf538036c7f37fbaba0&v=4
url: https://github.com/musicinmybrain
krishnamadhavan:
login: krishnamadhavan
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31798870?u=950693b28f3ae01105fd545c046e46ca3d31ab06&v=4
url: https://github.com/krishnamadhavan
jekirl:
login: jekirl
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4
url: https://github.com/jekirl
hitrust:
login: hitrust
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3360631?u=5fa1f475ad784d64eb9666bdd43cc4d285dcc773&v=4
url: https://github.com/hitrust
ShahriyarR:
login: ShahriyarR
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3852029?u=2dc6402d9053ee53f7afc407089cbab21c68f21d&v=4
url: https://github.com/ShahriyarR
adriangb:
login: adriangb
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
url: https://github.com/adriangb
iudeen:
login: iudeen
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
url: https://github.com/iudeen
philipokiokio:
login: philipokiokio
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/55271518?u=d30994d339aaaf1f6bf1b8fc810132016fbd4fdc&v=4
url: https://github.com/philipokiokio
AlexWendland:
login: AlexWendland
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/3949212?u=c4c0c615e0ea33d00bfe16b779cf6ebc0f58071c&v=4
url: https://github.com/AlexWendland
valentinDruzhinin:
login: valentinDruzhinin
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
divums:
login: divums
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1397556?v=4
url: https://github.com/divums
prostomarkeloff:
login: prostomarkeloff
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=6918e39a1224194ba636e897461a02a20126d7ad&v=4
url: https://github.com/prostomarkeloff
frankie567:
login: frankie567
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=f3e79acfe4ed207e15c2145161a8a9759925fcd2&v=4
url: https://github.com/frankie567
nsidnev:
login: nsidnev
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
pawamoy:
login: pawamoy
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
patrickmckenna:
login: patrickmckenna
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/3589536?u=53aef07250d226d35e526768e26891964907b41a&v=4
url: https://github.com/patrickmckenna
hukkin:
login: hukkin
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/3275109?u=77bb83759127965eacbfe67e2ca983066e964fde&v=4
url: https://github.com/hukkin
marcosmmb:
login: marcosmmb
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/6181089?u=03c50eec631857d84df5232890780d00a3f76903&v=4
url: https://github.com/marcosmmb
Serrones:
login: Serrones
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
url: https://github.com/Serrones
uriyyo:
login: uriyyo
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/32038156?u=c26ca9b821fcf6499b84db75f553d4980bf8d023&v=4
url: https://github.com/uriyyo
andrew222651:
login: andrew222651
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
url: https://github.com/andrew222651
rkbeatss:
login: rkbeatss
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/23391143?u=56ab6bff50be950fa8cae5cf736f2ae66e319ff3&v=4
url: https://github.com/rkbeatss
asheux:
login: asheux
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/22955146?u=4553ebf5b5a7c7fe031a46182083aa224faba2e1&v=4
url: https://github.com/asheux
blkst8:
login: blkst8
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/49960770?u=7d8a6d5f0a75a5e9a865a2527edfd48895ea27ae&v=4
url: https://github.com/blkst8
ghandic:
login: ghandic
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
TeoZosa:
login: TeoZosa
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/13070236?u=96fdae85800ef85dcfcc4b5f8281dc8778c8cb7d&v=4
url: https://github.com/TeoZosa
graingert:
login: graingert
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/413772?v=4
url: https://github.com/graingert
jaystone776:
login: jaystone776
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
url: https://github.com/jaystone776
zanieb:
login: zanieb
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/2586601?u=e5c86f7ff3b859e7e183187ac2b17fd6ee32b3ab&v=4
url: https://github.com/zanieb
MicaelJarniac:
login: MicaelJarniac
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/19514231?u=158c91874ea98d6e9e6f0c6db37ee2ce60c55ff2&v=4
url: https://github.com/MicaelJarniac
papb:
login: papb
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/20914054?u=890511fae7ea90d887e2a65ce44a1775abba38d5&v=4
url: https://github.com/papb
tamird:
login: tamird
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1535036?v=4
url: https://github.com/tamird
Nimitha-jagadeesha:
login: Nimitha-jagadeesha
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/58389915?v=4
url: https://github.com/Nimitha-jagadeesha
lucaromagnoli:
login: lucaromagnoli
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/38782977?u=a09a2e916625fa035f9dfa25ebc58e07aac8ec36&v=4
url: https://github.com/lucaromagnoli
salmantec:
login: salmantec
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/41512228?u=443551b893ff2425c59d5d021644f098cf7c68d5&v=4
url: https://github.com/salmantec
OCE1960:
login: OCE1960
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/45076670?u=0e9a44712b92ffa89ddfbaa83c112f3f8e1d68e2&v=4
url: https://github.com/OCE1960
hamidrasti:
login: hamidrasti
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/43915620?v=4
url: https://github.com/hamidrasti
kkinder:
login: kkinder
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1115018?u=c5e90284a9f5c5049eae1bb029e3655c7dc913ed&v=4
url: https://github.com/kkinder
kabirkhan:
login: kabirkhan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/13891834?u=e0eabf792376443ac853e7dca6f550db4166fe35&v=4
url: https://github.com/kabirkhan
zamiramir:
login: zamiramir
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/40475662?v=4
url: https://github.com/zamiramir
trim21:
login: trim21
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/13553903?u=3cadf0f02095c9621aa29df6875f53a80ca4fbfb&v=4
url: https://github.com/trim21
koxudaxi:
login: koxudaxi
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4
url: https://github.com/koxudaxi
pablogamboa:
login: pablogamboa
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/12892536?u=326a57059ee0c40c4eb1b38413957236841c631b&v=4
url: https://github.com/pablogamboa
dconathan:
login: dconathan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/15098095?v=4
url: https://github.com/dconathan
Jamim:
login: Jamim
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/5607572?u=9ce0b6a6d1a5124e28b3c04d8d26827ca328713a&v=4
url: https://github.com/Jamim
svalouch:
login: svalouch
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/54674660?v=4
url: https://github.com/svalouch
marier-nico:
login: marier-nico
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/30477068?u=c7df6af853c8f4163d1517814f3e9a0715c82713&v=4
url: https://github.com/marier-nico
Dustyposa:
login: Dustyposa
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
aviramha:
login: aviramha
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/41201924?u=ce5d3ea7037c2e6b3f82eff87e2217d4fb63214b&v=4
url: https://github.com/aviramha
iwpnd:
login: iwpnd
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6152183?u=ec59396e9437fff488791c5ecdf6d23f1f1ebf3a&v=4
url: https://github.com/iwpnd
raphaelauv:
login: raphaelauv
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
windson:
login: windson
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1826682?u=8b28dcd716c46289f191f8828e01d74edd058bef&v=4
url: https://github.com/windson
sm-Fifteen:
login: sm-Fifteen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
sattosan:
login: sattosan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
url: https://github.com/sattosan
michaeloliverx:
login: michaeloliverx
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/55017335?u=efb0cb6e261ff64d862fafb91ee80fc2e1f8a2ed&v=4
url: https://github.com/michaeloliverx
voegtlel:
login: voegtlel
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/5764745?u=db8df3d70d427928ab6d7dbfc395a4a7109c1d1b&v=4
url: https://github.com/voegtlel
HarshaLaxman:
login: HarshaLaxman
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/19939186?u=a112f38b0f6b4d4402dc8b51978b5a0b2e5c5970&v=4
url: https://github.com/HarshaLaxman
RunningIkkyu:
login: RunningIkkyu
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
url: https://github.com/RunningIkkyu
cassiobotaro:
login: cassiobotaro
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
url: https://github.com/cassiobotaro
chenl:
login: chenl
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1677651?u=c618508eaad6d596cea36c8ea784b424288f6857&v=4
url: https://github.com/chenl
retnikt:
login: retnikt
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
url: https://github.com/retnikt
yankeexe:
login: yankeexe
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/13623913?u=f970e66421775a8d3cdab89c0c752eaead186f6d&v=4
url: https://github.com/yankeexe
patrickkwang:
login: patrickkwang
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1263870?u=4bf74020e15be490f19ef8322a76eec882220b96&v=4
url: https://github.com/patrickkwang
victorphoenix3:
login: victorphoenix3
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/48182195?u=e4875bd088623cb4ddeb7be194ec54b453aff035&v=4
url: https://github.com/victorphoenix3
davidefiocco:
login: davidefiocco
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4547987?v=4
url: https://github.com/davidefiocco
adriencaccia:
login: adriencaccia
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/19605940?u=9a59081f46bfc9d839886a49d5092cf572879049&v=4
url: https://github.com/adriencaccia
jamescurtin:
login: jamescurtin
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10189269?u=0b491fc600ca51f41cf1d95b49fa32a3eba1de57&v=4
url: https://github.com/jamescurtin
jmriebold:
login: jmriebold
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6983392?u=4efdc97bf2422dcc7e9ff65b9ff80087c8eb2a20&v=4
url: https://github.com/jmriebold
nukopy:
login: nukopy
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/42367320?u=6061be0bd060506f6d564a8df3ae73fab048cdfe&v=4
url: https://github.com/nukopy
imba-tjd:
login: imba-tjd
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/24759802?u=01e901a4fe004b4b126549d3ff1c4000fe3720b5&v=4
url: https://github.com/imba-tjd
johnthagen:
login: johnthagen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10340167?u=47147fc4e4db1f573bee3fe428deeacb3197bc5f&v=4
url: https://github.com/johnthagen
paxcodes:
login: paxcodes
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/13646646?u=e7429cc7ab11211ef762f4cd3efea7db6d9ef036&v=4
url: https://github.com/paxcodes
kaustubhgupta:
login: kaustubhgupta
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/43691873?u=8dd738718ac7ffad4ef31e86b5d780a1141c695d&v=4
url: https://github.com/kaustubhgupta
kinuax:
login: kinuax
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/13321374?u=22dc9873d6d9f2c7e4fc44c6480c3505efb1531f&v=4
url: https://github.com/kinuax
wakabame:
login: wakabame
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/35513518?u=41ef6b0a55076e5c540620d68fb006e386c2ddb0&v=4
url: https://github.com/wakabame
nzig:
login: nzig
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7372858?u=e769add36ed73c778cdb136eb10bf96b1e119671&v=4
url: https://github.com/nzig
kristjanvalur:
login: kristjanvalur
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6009543?u=1419f20bbfff8f031be8cb470962e7e62de2595e&v=4
url: https://github.com/kristjanvalur
yezz123:
login: yezz123
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=21b53ce4115062b1e20cb513e64ca0000c2ef127&v=4
url: https://github.com/yezz123
softwarebloat:
login: softwarebloat
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/16540684?v=4
url: https://github.com/softwarebloat
Lancetnik:
login: Lancetnik
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/44573917?u=6eaa0cdd35259fba40a76b82e4903440cba03fa9&v=4
url: https://github.com/Lancetnik
joakimnordling:
login: joakimnordling
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6637576?u=df5d99db9b899b399effd429f4358baaa6f7199c&v=4
url: https://github.com/joakimnordling
AhsanSheraz:
login: AhsanSheraz
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51913596?u=b5aa3c3a004cc0112e56c54f6901941836d8c26b&v=4
url: https://github.com/AhsanSheraz
yogabonito:
login: yogabonito
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7026269?v=4
url: https://github.com/yogabonito
s111d:
login: s111d
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
url: https://github.com/s111d
estebanx64:
login: estebanx64
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=2ca073ee47a625e495a9573bd374ddcd7be5ec91&v=4
url: https://github.com/estebanx64
ndimares:
login: ndimares
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6267663?u=cfb27efde7a7212be8142abb6c058a1aeadb41b1&v=4
url: https://github.com/ndimares
rabinlamadong:
login: rabinlamadong
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/170439781?v=4
url: https://github.com/rabinlamadong
AyushSinghal1794:
login: AyushSinghal1794
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/89984761?v=4
url: https://github.com/AyushSinghal1794
gsheni:
login: gsheni
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/8726321?u=ee3bd9ff6320f4715d1dd9671a3d55cccb65b984&v=4
url: https://github.com/gsheni
chailandau:
login: chailandau
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/112015853?u=2e6aaf2b1647db43834aabeae8d8282b4ec01873&v=4
url: https://github.com/chailandau
DanielKusyDev:
login: DanielKusyDev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/36250676?u=411f1f5923596480b896d160e23c908318f39003&v=4
url: https://github.com/DanielKusyDev
Viicos:
login: Viicos
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/65306057?u=fcd677dc1b9bef12aa103613e5ccb3f8ce305af9&v=4
url: https://github.com/Viicos
DanielYang59:
login: DanielYang59
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/80093591?u=63873f701c7c74aac83c906800a1dddc0bc8c92f&v=4
url: https://github.com/DanielYang59
blueswen:
login: blueswen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1564148?u=6d6b8cc8f2b5cef715e68d6175154a8a94d518ee&v=4
url: https://github.com/blueswen
Taoup:
login: Taoup
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/22348542?v=4
url: https://github.com/Taoup
savannahostrowski:
login: savannahostrowski
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/8949415?u=e4bb61b9ebaa406274d2f2629cd33179a432a2a7&v=4
url: https://github.com/savannahostrowski
jonathan-fulton:
login: jonathan-fulton
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4665111?u=bda1c12e5137bd7771a6aa24d9515b87c11da150&v=4
url: https://github.com/jonathan-fulton
================================================
FILE: docs/en/data/github_sponsors.yml
================================================
sponsors:
- - login: renderinc
avatarUrl: https://avatars.githubusercontent.com/u/36424661?v=4
url: https://github.com/renderinc
- login: subtotal
avatarUrl: https://avatars.githubusercontent.com/u/176449348?v=4
url: https://github.com/subtotal
- login: greptileai
avatarUrl: https://avatars.githubusercontent.com/u/140149887?v=4
url: https://github.com/greptileai
- login: coderabbitai
avatarUrl: https://avatars.githubusercontent.com/u/132028505?v=4
url: https://github.com/coderabbitai
- login: zuplo
avatarUrl: https://avatars.githubusercontent.com/u/85497839?v=4
url: https://github.com/zuplo
- login: blockbee-io
avatarUrl: https://avatars.githubusercontent.com/u/115143449?u=1b8620c2d6567c4df2111a371b85a51f448f9b85&v=4
url: https://github.com/blockbee-io
- login: andrew-propelauth
avatarUrl: https://avatars.githubusercontent.com/u/89474256?u=c98993dec8553c09d424ede67bbe86e5c35f48c9&v=4
url: https://github.com/andrew-propelauth
- login: railwayapp
avatarUrl: https://avatars.githubusercontent.com/u/66716858?v=4
url: https://github.com/railwayapp
- - login: speakeasy-api
avatarUrl: https://avatars.githubusercontent.com/u/91446104?v=4
url: https://github.com/speakeasy-api
- login: stainless-api
avatarUrl: https://avatars.githubusercontent.com/u/88061651?v=4
url: https://github.com/stainless-api
- login: svix
avatarUrl: https://avatars.githubusercontent.com/u/80175132?v=4
url: https://github.com/svix
- login: permitio
avatarUrl: https://avatars.githubusercontent.com/u/71775833?v=4
url: https://github.com/permitio
- login: databento
avatarUrl: https://avatars.githubusercontent.com/u/64141749?v=4
url: https://github.com/databento
- - login: LambdaTest-Inc
avatarUrl: https://avatars.githubusercontent.com/u/171592363?u=96606606a45fa170427206199014f2a5a2a4920b&v=4
url: https://github.com/LambdaTest-Inc
- login: Ponte-Energy-Partners
avatarUrl: https://avatars.githubusercontent.com/u/114745848?v=4
url: https://github.com/Ponte-Energy-Partners
- login: BoostryJP
avatarUrl: https://avatars.githubusercontent.com/u/57932412?v=4
url: https://github.com/BoostryJP
- login: acsone
avatarUrl: https://avatars.githubusercontent.com/u/7601056?v=4
url: https://github.com/acsone
- - login: scalar
avatarUrl: https://avatars.githubusercontent.com/u/301879?v=4
url: https://github.com/scalar
- login: Trivie
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
url: https://github.com/Trivie
- - login: takashi-yoneya
avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4
url: https://github.com/takashi-yoneya
- login: Doist
avatarUrl: https://avatars.githubusercontent.com/u/2565372?v=4
url: https://github.com/Doist
- - login: mainframeindustries
avatarUrl: https://avatars.githubusercontent.com/u/55092103?v=4
url: https://github.com/mainframeindustries
- - login: alixlahuec
avatarUrl: https://avatars.githubusercontent.com/u/29543316?u=44357eb2a93bccf30fb9d389b8befe94a3d00985&v=4
url: https://github.com/alixlahuec
- - login: primer-io
avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4
url: https://github.com/primer-io
- - login: upciti
avatarUrl: https://avatars.githubusercontent.com/u/43346262?v=4
url: https://github.com/upciti
- login: ChargeStorm
avatarUrl: https://avatars.githubusercontent.com/u/26000165?v=4
url: https://github.com/ChargeStorm
- login: ibrahimpelumi6142
avatarUrl: https://avatars.githubusercontent.com/u/113442282?v=4
url: https://github.com/ibrahimpelumi6142
- login: nilslindemann
avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
url: https://github.com/nilslindemann
- - login: samuelcolvin
avatarUrl: https://avatars.githubusercontent.com/u/4039449?u=42eb3b833047c8c4b4f647a031eaef148c16d93f&v=4
url: https://github.com/samuelcolvin
- login: otosky
avatarUrl: https://avatars.githubusercontent.com/u/42260747?u=69d089387c743d89427aa4ad8740cfb34045a9e0&v=4
url: https://github.com/otosky
- login: ramonalmeidam
avatarUrl: https://avatars.githubusercontent.com/u/45269580?u=3358750b3a5854d7c3ed77aaca7dd20a0f529d32&v=4
url: https://github.com/ramonalmeidam
- login: roboflow
avatarUrl: https://avatars.githubusercontent.com/u/53104118?v=4
url: https://github.com/roboflow
- login: dudikbender
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=3a57542938ebfd57579a0111db2b297e606d9681&v=4
url: https://github.com/dudikbender
- login: ehaca
avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=cec1a3e0643b785288ae8260cc295a85ab344995&v=4
url: https://github.com/ehaca
- login: raphaellaude
avatarUrl: https://avatars.githubusercontent.com/u/28026311?u=91e1c00d9ac4f8045527e13de8050d504531cbc0&v=4
url: https://github.com/raphaellaude
- login: timlrx
avatarUrl: https://avatars.githubusercontent.com/u/28362229?u=9a745ca31372ee324af682715ae88ce8522f9094&v=4
url: https://github.com/timlrx
- login: Leay15
avatarUrl: https://avatars.githubusercontent.com/u/32212558?u=c4aa9c1737e515959382a5515381757b1fd86c53&v=4
url: https://github.com/Leay15
- login: jugeeem
avatarUrl: https://avatars.githubusercontent.com/u/116043716?u=ae590d79c38ac79c91b9c5caa6887d061e865a3d&v=4
url: https://github.com/jugeeem
- login: Karine-Bauch
avatarUrl: https://avatars.githubusercontent.com/u/90465103?u=7feb1018abb1a5631cfd9a91fea723d1ceb5f49b&v=4
url: https://github.com/Karine-Bauch
- login: kaoru0310
avatarUrl: https://avatars.githubusercontent.com/u/80977929?u=1b61d10142b490e56af932ddf08a390fae8ee94f&v=4
url: https://github.com/kaoru0310
- login: chickenandstats
avatarUrl: https://avatars.githubusercontent.com/u/79477966?u=ae2b894aa954070db1d7830dab99b49eba4e4567&v=4
url: https://github.com/chickenandstats
- login: patricioperezv
avatarUrl: https://avatars.githubusercontent.com/u/73832292?u=5f471f156e19ee7920e62ae0f4a47b95580e61cf&v=4
url: https://github.com/patricioperezv
- login: anthonycepeda
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
url: https://github.com/anthonycepeda
- login: AalbatrossGuy
avatarUrl: https://avatars.githubusercontent.com/u/68378354?u=0bdeea9356d24f638244131f6d8d1e2d2f3601ca&v=4
url: https://github.com/AalbatrossGuy
- login: patsatsia
avatarUrl: https://avatars.githubusercontent.com/u/61111267?u=3271b85f7a37b479c8d0ae0a235182e83c166edf&v=4
url: https://github.com/patsatsia
- login: oliverxchen
avatarUrl: https://avatars.githubusercontent.com/u/4471774?u=534191f25e32eeaadda22dfab4b0a428733d5489&v=4
url: https://github.com/oliverxchen
- login: jaredtrog
avatarUrl: https://avatars.githubusercontent.com/u/4381365?v=4
url: https://github.com/jaredtrog
- login: Ryandaydev
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=679ff84cb7b988c5795a5fa583857f574a055763&v=4
url: https://github.com/Ryandaydev
- login: gorhack
avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4
url: https://github.com/gorhack
- login: mj0331
avatarUrl: https://avatars.githubusercontent.com/u/3890353?u=1c627ac1a024515b4871de5c3ebbfaa1a57f65d4&v=4
url: https://github.com/mj0331
- login: anomaly
avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4
url: https://github.com/anomaly
- login: aacayaco
avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4
url: https://github.com/aacayaco
- login: kennywakeland
avatarUrl: https://avatars.githubusercontent.com/u/3631417?u=7c8f743f1ae325dfadea7c62bbf1abd6a824fc55&v=4
url: https://github.com/kennywakeland
- login: zsinx6
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
url: https://github.com/zsinx6
- login: dblackrun
avatarUrl: https://avatars.githubusercontent.com/u/3528486?v=4
url: https://github.com/dblackrun
- login: knallgelb
avatarUrl: https://avatars.githubusercontent.com/u/2358812?u=c48cb6362b309d74cbf144bd6ad3aed3eb443e82&v=4
url: https://github.com/knallgelb
- login: dodo5522
avatarUrl: https://avatars.githubusercontent.com/u/1362607?u=9bf1e0e520cccc547c046610c468ce6115bbcf9f&v=4
url: https://github.com/dodo5522
- login: mintuhouse
avatarUrl: https://avatars.githubusercontent.com/u/769950?u=ecfbd79a97d33177e0d093ddb088283cf7fe8444&v=4
url: https://github.com/mintuhouse
- login: falkben
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
url: https://github.com/falkben
- login: koxudaxi
avatarUrl: https://avatars.githubusercontent.com/u/630670?u=507d8577b4b3670546b449c4c2ccbc5af40d72f7&v=4
url: https://github.com/koxudaxi
- login: wshayes
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: pamelafox
avatarUrl: https://avatars.githubusercontent.com/u/297042?v=4
url: https://github.com/pamelafox
- login: robintw
avatarUrl: https://avatars.githubusercontent.com/u/296686?v=4
url: https://github.com/robintw
- login: jstanden
avatarUrl: https://avatars.githubusercontent.com/u/63288?u=c3658d57d2862c607a0e19c2101c3c51876e36ad&v=4
url: https://github.com/jstanden
- login: RaamEEIL
avatarUrl: https://avatars.githubusercontent.com/u/20320552?v=4
url: https://github.com/RaamEEIL
- login: ashi-agrawal
avatarUrl: https://avatars.githubusercontent.com/u/17105294?u=99c7a854035e5398d8e7b674f2d42baae6c957f8&v=4
url: https://github.com/ashi-agrawal
- login: mjohnsey
avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
url: https://github.com/mjohnsey
- login: khadrawy
avatarUrl: https://avatars.githubusercontent.com/u/13686061?u=59f25ef42ecf04c22657aac4238ce0e2d3d30304&v=4
url: https://github.com/khadrawy
- login: dannywade
avatarUrl: https://avatars.githubusercontent.com/u/13680237?u=418ee985bd41577b20fde81417fb2d901e875e8a&v=4
url: https://github.com/dannywade
- login: jsoques
avatarUrl: https://avatars.githubusercontent.com/u/12414216?u=620921d94196546cc8b9eae2cc4cbc3f95bab42f&v=4
url: https://github.com/jsoques
- login: wdwinslow
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=371272f2c69e680e0559a7b0a57385e83a5dc728&v=4
url: https://github.com/wdwinslow
- login: hiancdtrsnm
avatarUrl: https://avatars.githubusercontent.com/u/7343177?v=4
url: https://github.com/hiancdtrsnm
- login: Rehket
avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
url: https://github.com/Rehket
- login: FernandoCelmer
avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=58ba6d5888fa7f355934e52db19f950e20b38162&v=4
url: https://github.com/FernandoCelmer
- login: eseglem
avatarUrl: https://avatars.githubusercontent.com/u/5920492?u=208d419cf667b8ac594c82a8db01932c7e50d057&v=4
url: https://github.com/eseglem
- login: ternaus
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=513a26b02a39e7a28d587cd37c6cc877ea368e6e&v=4
url: https://github.com/ternaus
- - login: Artur-Galstyan
avatarUrl: https://avatars.githubusercontent.com/u/63471891?u=e8691f386037e51a737cc0ba866cd8c89e5cf109&v=4
url: https://github.com/Artur-Galstyan
- login: manoelpqueiroz
avatarUrl: https://avatars.githubusercontent.com/u/23669137?u=b12e84b28a84369ab5b30bd5a79e5788df5a0756&v=4
url: https://github.com/manoelpqueiroz
- - login: pawamoy
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
url: https://github.com/pawamoy
- login: siavashyj
avatarUrl: https://avatars.githubusercontent.com/u/43583410?u=562005ddc7901cd27a1219a118a2363817b14977&v=4
url: https://github.com/siavashyj
- login: mobyw
avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4
url: https://github.com/mobyw
- login: ArtyomVancyan
avatarUrl: https://avatars.githubusercontent.com/u/44609997?v=4
url: https://github.com/ArtyomVancyan
- login: caviri
avatarUrl: https://avatars.githubusercontent.com/u/45425937?u=5f3d66ea5edea94c028c51ebf1c0f3b37e6c3db5&v=4
url: https://github.com/caviri
- login: hgalytoby
avatarUrl: https://avatars.githubusercontent.com/u/50397689?u=6cc9028f3db63f8f60ad21c17b1ce4b88c4e2e60&v=4
url: https://github.com/hgalytoby
- login: johnl28
avatarUrl: https://avatars.githubusercontent.com/u/54412955?u=47dd06082d1c39caa90c752eb55566e4f3813957&v=4
url: https://github.com/johnl28
- login: danielunderwood
avatarUrl: https://avatars.githubusercontent.com/u/4472301?v=4
url: https://github.com/danielunderwood
- login: hoenie-ams
avatarUrl: https://avatars.githubusercontent.com/u/25708487?u=cda07434f0509ac728d9edf5e681117c0f6b818b&v=4
url: https://github.com/hoenie-ams
- login: joerambo
avatarUrl: https://avatars.githubusercontent.com/u/26282974?v=4
url: https://github.com/joerambo
- login: engineerjoe440
avatarUrl: https://avatars.githubusercontent.com/u/33275230?u=eb223cad27017bb1e936ee9b429b450d092d0236&v=4
url: https://github.com/engineerjoe440
- login: bnkc
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=4771ac4e64066f0847d40e5b29910adabd9b2372&v=4
url: https://github.com/bnkc
- login: petercool
avatarUrl: https://avatars.githubusercontent.com/u/37613029?u=75aa8c6729e6e8f85a300561c4dbeef9d65c8797&v=4
url: https://github.com/petercool
- login: PelicanQ
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
url: https://github.com/PelicanQ
- login: PunRabbit
avatarUrl: https://avatars.githubusercontent.com/u/70463212?u=1a835cfbc99295a60c8282f6aa6199d1b42241a5&v=4
url: https://github.com/PunRabbit
- login: my3
avatarUrl: https://avatars.githubusercontent.com/u/1825270?v=4
url: https://github.com/my3
- login: WillHogan
avatarUrl: https://avatars.githubusercontent.com/u/1661551?u=8a80356e3e7d5a417157aba7ea565dabc8678327&v=4
url: https://github.com/WillHogan
- login: miguelgr
avatarUrl: https://avatars.githubusercontent.com/u/1484589?u=54556072b8136efa12ae3b6902032ea2a39ace4b&v=4
url: https://github.com/miguelgr
- login: tochikuji
avatarUrl: https://avatars.githubusercontent.com/u/851759?v=4
url: https://github.com/tochikuji
- login: ceb10n
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
- login: slafs
avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4
url: https://github.com/slafs
- login: bryanculbertson
avatarUrl: https://avatars.githubusercontent.com/u/144028?u=defda4f90e93429221cc667500944abde60ebe4a&v=4
url: https://github.com/bryanculbertson
- login: ddanier
avatarUrl: https://avatars.githubusercontent.com/u/113563?u=ed1dc79de72f93bd78581f88ebc6952b62f472da&v=4
url: https://github.com/ddanier
- login: nisutec
avatarUrl: https://avatars.githubusercontent.com/u/25281462?u=e562484c451fdfc59053163f64405f8eb262b8b0&v=4
url: https://github.com/nisutec
- login: joshuatz
avatarUrl: https://avatars.githubusercontent.com/u/17817563?u=f1bf05b690d1fc164218f0b420cdd3acb7913e21&v=4
url: https://github.com/joshuatz
- login: TheR1D
avatarUrl: https://avatars.githubusercontent.com/u/16740832?u=b0dfdbdb27b79729430c71c6128962f77b7b53f7&v=4
url: https://github.com/TheR1D
- login: Zuzah
avatarUrl: https://avatars.githubusercontent.com/u/10934846?u=1ef43e075ddc87bd1178372bf4d95ee6175cae27&v=4
url: https://github.com/Zuzah
- login: mntolia
avatarUrl: https://avatars.githubusercontent.com/u/10390224?v=4
url: https://github.com/mntolia
- login: hard-coders
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4
url: https://github.com/hard-coders
- login: DMantis
avatarUrl: https://avatars.githubusercontent.com/u/9536869?u=652dd0d49717803c0cbcbf44f7740e53cf2d4892&v=4
url: https://github.com/DMantis
- login: xncbf
avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=a80a7bb349555b277645632ed66639ff43400614&v=4
url: https://github.com/xncbf
- login: moonape1226
avatarUrl: https://avatars.githubusercontent.com/u/8532038?u=d9f8b855a429fff9397c3833c2ff83849ebf989d&v=4
url: https://github.com/moonape1226
- login: harsh183
avatarUrl: https://avatars.githubusercontent.com/u/7780198?v=4
url: https://github.com/harsh183
- login: katnoria
avatarUrl: https://avatars.githubusercontent.com/u/7674948?u=09767eb13e07e09496c5fee4e5ce21d9eac34a56&v=4
url: https://github.com/katnoria
- login: KentShikama
avatarUrl: https://avatars.githubusercontent.com/u/6329898?u=8b236810db9b96333230430837e1f021f9246da1&v=4
url: https://github.com/KentShikama
- login: Baghdady92
avatarUrl: https://avatars.githubusercontent.com/u/5708590?v=4
url: https://github.com/Baghdady92
- login: sdevkota
avatarUrl: https://avatars.githubusercontent.com/u/5250987?u=4ed9a120c89805a8aefda1cbdc0cf6512e64d1b4&v=4
url: https://github.com/sdevkota
- login: rangulvers
avatarUrl: https://avatars.githubusercontent.com/u/5235430?u=e254d4af4ace5a05fa58372ae677c7d26f0d5a53&v=4
url: https://github.com/rangulvers
- - login: KOZ39
avatarUrl: https://avatars.githubusercontent.com/u/38822500?u=9dfc0a697df1c9628f08e20dc3fb17b1afc4e5a7&v=4
url: https://github.com/KOZ39
- login: rwxd
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
url: https://github.com/rwxd
- login: morzan1001
avatarUrl: https://avatars.githubusercontent.com/u/47593005?u=c30ab7230f82a12a9b938dcb54f84a996931409a&v=4
url: https://github.com/morzan1001
- login: Olegt0rr
avatarUrl: https://avatars.githubusercontent.com/u/25399456?u=3e87b5239a2f4600975ba13be73054f8567c6060&v=4
url: https://github.com/Olegt0rr
- login: larsyngvelundin
avatarUrl: https://avatars.githubusercontent.com/u/34173819?u=74958599695bf83ac9f1addd935a51548a10c6b0&v=4
url: https://github.com/larsyngvelundin
- login: andrecorumba
avatarUrl: https://avatars.githubusercontent.com/u/37807517?u=9b9be3b41da9bda60957da9ef37b50dbf65baa61&v=4
url: https://github.com/andrecorumba
- login: CoderDeltaLAN
avatarUrl: https://avatars.githubusercontent.com/u/152043745?u=4ff541efffb7d134e60c5fcf2dd1e343f90bb782&v=4
url: https://github.com/CoderDeltaLAN
- login: hippoley
avatarUrl: https://avatars.githubusercontent.com/u/135493401?u=1164ef48a645a7c12664fabc1638fbb7e1c459b0&v=4
url: https://github.com/hippoley
- login: nayasinghania
avatarUrl: https://avatars.githubusercontent.com/u/74111380?u=752e99a5e139389fdc0a0677122adc08438eb076&v=4
url: https://github.com/nayasinghania
- login: onestn
avatarUrl: https://avatars.githubusercontent.com/u/62360849?u=746dd21c34e7e06eefb11b03e8bb01aaae3c2a4f&v=4
url: https://github.com/onestn
- login: Toothwitch
avatarUrl: https://avatars.githubusercontent.com/u/1710406?u=5eebb23b46cd26e48643b9e5179536cad491c17a&v=4
url: https://github.com/Toothwitch
- login: andreagrandi
avatarUrl: https://avatars.githubusercontent.com/u/636391?u=13d90cb8ec313593a5b71fbd4e33b78d6da736f5&v=4
url: https://github.com/andreagrandi
- login: msserpa
avatarUrl: https://avatars.githubusercontent.com/u/6334934?u=82c4489eb1559d88d2990d60001901b14f722bbb&v=4
url: https://github.com/msserpa
================================================
FILE: docs/en/data/members.yml
================================================
members:
- login: tiangolo
avatar_url: https://avatars.githubusercontent.com/u/1326112
url: https://github.com/tiangolo
- login: Kludex
avatar_url: https://avatars.githubusercontent.com/u/7353520
url: https://github.com/Kludex
- login: alejsdev
avatar_url: https://avatars.githubusercontent.com/u/90076947
url: https://github.com/alejsdev
- login: svlandeg
avatar_url: https://avatars.githubusercontent.com/u/8796347
url: https://github.com/svlandeg
- login: YuriiMotov
avatar_url: https://avatars.githubusercontent.com/u/109919500
url: https://github.com/YuriiMotov
- login: patrick91
avatar_url: https://avatars.githubusercontent.com/u/667029
url: https://github.com/patrick91
- login: luzzodev
avatar_url: https://avatars.githubusercontent.com/u/27291415
url: https://github.com/luzzodev
================================================
FILE: docs/en/data/people.yml
================================================
maintainers:
- login: tiangolo
answers: 1927
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
experts:
- login: tiangolo
count: 1927
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
- login: YuriiMotov
count: 1164
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
- login: github-actions
count: 770
avatarUrl: https://avatars.githubusercontent.com/in/15368?v=4
url: https://github.com/apps/github-actions
- login: Kludex
count: 657
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
- login: jgould22
count: 263
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
url: https://github.com/jgould22
- login: dmontagu
count: 240
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
url: https://github.com/dmontagu
- login: Mause
count: 219
avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4
url: https://github.com/Mause
- login: ycd
count: 216
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
url: https://github.com/ycd
- login: JarroVGIT
count: 192
avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4
url: https://github.com/JarroVGIT
- login: euri10
count: 153
avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4
url: https://github.com/euri10
- login: iudeen
count: 128
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
url: https://github.com/iudeen
- login: phy25
count: 126
avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4
url: https://github.com/phy25
- login: JavierSanchezCastro
count: 109
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: luzzodev
count: 107
avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
url: https://github.com/luzzodev
- login: raphaelauv
count: 83
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
- login: ArcLightSlavik
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
- login: ghandic
count: 71
avatarUrl: https://avatars.githubusercontent.com/u/23500353?u=e2e1d736f924d9be81e8bfc565b6d8836ba99773&v=4
url: https://github.com/ghandic
- login: n8sty
count: 67
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
url: https://github.com/n8sty
- login: falkben
count: 59
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
url: https://github.com/falkben
- login: yinziyan1206
count: 55
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: acidjunk
count: 50
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
url: https://github.com/acidjunk
- login: sm-Fifteen
count: 49
avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4
url: https://github.com/sm-Fifteen
- login: adriangb
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
url: https://github.com/adriangb
- login: insomnes
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
url: https://github.com/insomnes
- login: Dustyposa
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
url: https://github.com/Dustyposa
- login: frankie567
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=f3e79acfe4ed207e15c2145161a8a9759925fcd2&v=4
url: https://github.com/frankie567
- login: odiseo0
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=75e754b9bd12ce6dfe90ad68e82e6f5255c7ac09&v=4
url: https://github.com/odiseo0
- login: sinisaos
count: 41
avatarUrl: https://avatars.githubusercontent.com/u/30960668?v=4
url: https://github.com/sinisaos
- login: includeamin
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4
url: https://github.com/includeamin
- login: chbndrhnns
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4
url: https://github.com/chbndrhnns
- login: STeveShary
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4
url: https://github.com/STeveShary
- login: krishnardt
count: 35
avatarUrl: https://avatars.githubusercontent.com/u/31960541?u=47f4829c77f4962ab437ffb7995951e41eeebe9b&v=4
url: https://github.com/krishnardt
- login: panla
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/41326348?u=ba2fda6b30110411ecbf406d187907e2b420ac19&v=4
url: https://github.com/panla
- login: valentinDruzhinin
count: 30
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
- login: prostomarkeloff
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=6918e39a1224194ba636e897461a02a20126d7ad&v=4
url: https://github.com/prostomarkeloff
- login: alv2017
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
url: https://github.com/alv2017
- login: hasansezertasan
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=d36995e41a00590da64e6204cfd112e0484ac1ca&v=4
url: https://github.com/hasansezertasan
- login: dbanty
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9d726785d08e50b1e1cd96505800c8ea8405bce2&v=4
url: https://github.com/dbanty
- login: wshayes
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
url: https://github.com/wshayes
- login: SirTelemak
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
- login: connebs
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/9054108?u=e151d5f545a3395136d711c227c22032fda67cfa&v=4
url: https://github.com/connebs
- login: nymous
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
- login: chrisK824
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
url: https://github.com/chrisK824
- login: ceb10n
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
- login: rafsaf
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
url: https://github.com/rafsaf
- login: nsidnev
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
url: https://github.com/nsidnev
- login: chris-allnutt
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
url: https://github.com/chris-allnutt
- login: ebottos94
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=8b91053b3abe4a9209375e3651e1c1ef192d884b&v=4
url: https://github.com/ebottos94
- login: estebanx64
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=2ca073ee47a625e495a9573bd374ddcd7be5ec91&v=4
url: https://github.com/estebanx64
- login: sehraramiz
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/14166324?u=8fac65e84dfff24245d304a5b5b09f7b5bd69dc9&v=4
url: https://github.com/sehraramiz
- login: retnikt
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
url: https://github.com/retnikt
- login: zoliknemet
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
url: https://github.com/zoliknemet
- login: caeser1996
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
url: https://github.com/caeser1996
- login: Hultner
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
url: https://github.com/Hultner
- login: harunyasar
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
url: https://github.com/harunyasar
- login: nkhitrov
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=e19427d8dc296d6950e9c424adacc92d37496fe9&v=4
url: https://github.com/nkhitrov
- login: dstlny
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4
url: https://github.com/dstlny
- login: pythonweb2
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
url: https://github.com/pythonweb2
- login: jonatasoli
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=f601c3f111f2148bd9244c2cb3ebbd57b592e674&v=4
url: https://github.com/jonatasoli
- login: mattmess1221
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/3409962?u=d22ea18aa8ea688af25a45df306134d593621a44&v=4
url: https://github.com/mattmess1221
last_month_experts:
- login: YuriiMotov
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
- login: Firatasi
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/112112161?u=3219914a49a4a604b3626007823db7de049b6d66&v=4
url: https://github.com/Firatasi
- login: ericgitangu
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11472845?u=9d916cf0f5c80e63cb1d753b8b50dcb8ced3b883&v=4
url: https://github.com/ericgitangu
- login: cookesan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6601329?u=7bfc9b017198a9fa50929ae8ae0a787632424ffd&v=4
url: https://github.com/cookesan
- login: coleifer
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/119974?u=b3a546c94ee1105e792e0acad2c4743d800e7975&v=4
url: https://github.com/coleifer
- login: Bahtya
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34988899?u=b8e3c0cf26f4bd1faea265d2f5f66f564af63463&v=4
url: https://github.com/Bahtya
- login: luzzodev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
url: https://github.com/luzzodev
- login: DoctorJohn
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=ec43fe79a98dbc864b428afc7220753e25ca3af2&v=4
url: https://github.com/DoctorJohn
three_months_experts:
- login: YuriiMotov
count: 74
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
- login: Firatasi
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/112112161?u=3219914a49a4a604b3626007823db7de049b6d66&v=4
url: https://github.com/Firatasi
- login: JavierSanchezCastro
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: Toygarmetu
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/92878791?u=538530cb6d5554e71f9c28709d794db9a74d23d9&v=4
url: https://github.com/Toygarmetu
- login: tiangolo
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
- login: luzzodev
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
url: https://github.com/luzzodev
- login: ericgitangu
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11472845?u=9d916cf0f5c80e63cb1d753b8b50dcb8ced3b883&v=4
url: https://github.com/ericgitangu
- login: cookesan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6601329?u=7bfc9b017198a9fa50929ae8ae0a787632424ffd&v=4
url: https://github.com/cookesan
- login: coleifer
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/119974?u=b3a546c94ee1105e792e0acad2c4743d800e7975&v=4
url: https://github.com/coleifer
- login: Bahtya
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34988899?u=b8e3c0cf26f4bd1faea265d2f5f66f564af63463&v=4
url: https://github.com/Bahtya
- login: saitarrun
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/116748905?u=3433afbaf06676a482ebf4ba33b08ddb3fc5c5bf&v=4
url: https://github.com/saitarrun
- login: EmmanuelNiyonshuti
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/142030687?u=ab131d5ad4670280a978f489babe71c9bf9c1097&v=4
url: https://github.com/EmmanuelNiyonshuti
- login: christiansousadev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/103544118?u=690f3f76d1dc4d0929de5020679d5604f860acbc&v=4
url: https://github.com/christiansousadev
- login: DoctorJohn
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=ec43fe79a98dbc864b428afc7220753e25ca3af2&v=4
url: https://github.com/DoctorJohn
- login: gaardhus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/46934916?u=18d7aacc6ce59f054749209645d11cfe77b52f90&v=4
url: https://github.com/gaardhus
- login: valentinDruzhinin
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
six_months_experts:
- login: YuriiMotov
count: 166
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
- login: tiangolo
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
- login: JavierSanchezCastro
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: luzzodev
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
url: https://github.com/luzzodev
- login: Firatasi
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/112112161?u=3219914a49a4a604b3626007823db7de049b6d66&v=4
url: https://github.com/Firatasi
- login: Toygarmetu
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/92878791?u=538530cb6d5554e71f9c28709d794db9a74d23d9&v=4
url: https://github.com/Toygarmetu
- login: ceb10n
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
- login: JunjieAraoXiong
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/167785867?u=b69afe090c8bf5fd73f2d23fc3a887b28f68f192&v=4
url: https://github.com/JunjieAraoXiong
- login: valentinDruzhinin
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
- login: krylosov-aa
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/242901957?u=4c9c7b468203b09bca64936fb464620e32cdd252&v=4
url: https://github.com/krylosov-aa
- login: ericgitangu
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11472845?u=9d916cf0f5c80e63cb1d753b8b50dcb8ced3b883&v=4
url: https://github.com/ericgitangu
- login: EmmanuelNiyonshuti
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/142030687?u=ab131d5ad4670280a978f489babe71c9bf9c1097&v=4
url: https://github.com/EmmanuelNiyonshuti
- login: sachinh35
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/21972708?u=8560b97b8b41e175f476270b56de8a493b84f302&v=4
url: https://github.com/sachinh35
- login: RichieB2B
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1461970?u=edaa57d1077705244ea5c9244f4783d94ff11f12&v=4
url: https://github.com/RichieB2B
- login: cookesan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6601329?u=7bfc9b017198a9fa50929ae8ae0a787632424ffd&v=4
url: https://github.com/cookesan
- login: coleifer
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/119974?u=b3a546c94ee1105e792e0acad2c4743d800e7975&v=4
url: https://github.com/coleifer
- login: Bahtya
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34988899?u=b8e3c0cf26f4bd1faea265d2f5f66f564af63463&v=4
url: https://github.com/Bahtya
- login: saitarrun
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/116748905?u=3433afbaf06676a482ebf4ba33b08ddb3fc5c5bf&v=4
url: https://github.com/saitarrun
- login: cepedus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/26345924?u=38495abbdbb8695dd76478cae5963bf994c498bc&v=4
url: https://github.com/cepedus
- login: christiansousadev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/103544118?u=690f3f76d1dc4d0929de5020679d5604f860acbc&v=4
url: https://github.com/christiansousadev
- login: DoctorJohn
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=ec43fe79a98dbc864b428afc7220753e25ca3af2&v=4
url: https://github.com/DoctorJohn
- login: gaardhus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/46934916?u=18d7aacc6ce59f054749209645d11cfe77b52f90&v=4
url: https://github.com/gaardhus
- login: Kludex
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
- login: y2kbugger
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6101677?u=1d50077e29582dc01fcbdff846f04fe7ec73fe2e&v=4
url: https://github.com/y2kbugger
- login: davidbrochart
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4711805?u=d39696d995a9e02ec3613ffb2f62b20b14f92f26&v=4
url: https://github.com/davidbrochart
- login: CharlieReitzel
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20848272?v=4
url: https://github.com/CharlieReitzel
- login: dotmitsu
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/42657211?u=3bccc9a2f386a3f24230ec393080f8904fe2a5b2&v=4
url: https://github.com/dotmitsu
- login: dolfinus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=ed5ddadcf36d9b943ebe61febe0b96ee34e5425d&v=4
url: https://github.com/dolfinus
- login: florentx
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/142113?u=bf10f10080026346b092633c380977b61cee0d9c&v=4
url: https://github.com/florentx
- login: jc-louis
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51329768?v=4
url: https://github.com/jc-louis
- login: bughuntr7
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/236391583?u=7f51ff690e3a5711f845a115903c39e21c8af938&v=4
url: https://github.com/bughuntr7
- login: CodeKraken-cmd
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/48470371?u=e7c0e7ec8e35ca5fb3ae40a586ed5e788fd0fe6d&v=4
url: https://github.com/CodeKraken-cmd
- login: svlandeg
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
url: https://github.com/svlandeg
- login: jymchng
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/27895426?u=fb88c47775147d62a395fdb895d1af4148c7b566&v=4
url: https://github.com/jymchng
one_year_experts:
- login: YuriiMotov
count: 951
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
- login: luzzodev
count: 48
avatarUrl: https://avatars.githubusercontent.com/u/27291415?u=5607ae1ce75c5f54f09500ca854227f7bfd2033b&v=4
url: https://github.com/luzzodev
- login: tiangolo
count: 30
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
- login: valentinDruzhinin
count: 30
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
- login: JavierSanchezCastro
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
- login: sachinh35
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/21972708?u=8560b97b8b41e175f476270b56de8a493b84f302&v=4
url: https://github.com/sachinh35
- login: Firatasi
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/112112161?u=3219914a49a4a604b3626007823db7de049b6d66&v=4
url: https://github.com/Firatasi
- login: DoctorJohn
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/14076775?u=ec43fe79a98dbc864b428afc7220753e25ca3af2&v=4
url: https://github.com/DoctorJohn
- login: raceychan
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/75417963?u=060c62870ec5a791765e63ac20d8885d11143786&v=4
url: https://github.com/raceychan
- login: yinziyan1206
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
url: https://github.com/yinziyan1206
- login: Toygarmetu
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/92878791?u=538530cb6d5554e71f9c28709d794db9a74d23d9&v=4
url: https://github.com/Toygarmetu
- login: Kludex
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
- login: ceb10n
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
- login: RichieB2B
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/1461970?u=edaa57d1077705244ea5c9244f4783d94ff11f12&v=4
url: https://github.com/RichieB2B
- login: JunjieAraoXiong
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/167785867?u=b69afe090c8bf5fd73f2d23fc3a887b28f68f192&v=4
url: https://github.com/JunjieAraoXiong
- login: svlandeg
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
url: https://github.com/svlandeg
- login: WilliamDEdwards
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/12184311?u=9b29d5d1d71f5f1a7ef9e439963ad3529e3b33a4&v=4
url: https://github.com/WilliamDEdwards
- login: ArmanShirzad
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/68951175?u=1f1efae2fa5d0d17c38a1a8413bedca5e538cedb&v=4
url: https://github.com/ArmanShirzad
- login: CodeKraken-cmd
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/48470371?u=e7c0e7ec8e35ca5fb3ae40a586ed5e788fd0fe6d&v=4
url: https://github.com/CodeKraken-cmd
- login: krylosov-aa
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/242901957?u=4c9c7b468203b09bca64936fb464620e32cdd252&v=4
url: https://github.com/krylosov-aa
- login: isgin01
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=16d6466476cf7dbc55a4cd575b6ea920ebdd81e1&v=4
url: https://github.com/isgin01
- login: ericgitangu
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11472845?u=9d916cf0f5c80e63cb1d753b8b50dcb8ced3b883&v=4
url: https://github.com/ericgitangu
- login: henrymcl
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/26480299?v=4
url: https://github.com/henrymcl
- login: EmmanuelNiyonshuti
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/142030687?u=ab131d5ad4670280a978f489babe71c9bf9c1097&v=4
url: https://github.com/EmmanuelNiyonshuti
- login: dolfinus
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=ed5ddadcf36d9b943ebe61febe0b96ee34e5425d&v=4
url: https://github.com/dolfinus
- login: jymchng
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/27895426?u=fb88c47775147d62a395fdb895d1af4148c7b566&v=4
url: https://github.com/jymchng
- login: simone-trubian
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/5606840?u=65703af3c605feca61ce49e4009bb4e26495b425&v=4
url: https://github.com/simone-trubian
- login: mahimairaja
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/81288263?u=4eef6b4a36b96e84bd666fc1937aa589036ccb9a&v=4
url: https://github.com/mahimairaja
- login: pankeshpatel
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1482917?u=666f39197a88cfa38b8bd78d39ef04d95c948b6b&v=4
url: https://github.com/pankeshpatel
- login: Jelle-tenB
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/210023470?u=c25d66addf36a747bd9fab773c4a6e7b238f45d4&v=4
url: https://github.com/Jelle-tenB
- login: cookesan
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6601329?u=7bfc9b017198a9fa50929ae8ae0a787632424ffd&v=4
url: https://github.com/cookesan
- login: coleifer
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/119974?u=b3a546c94ee1105e792e0acad2c4743d800e7975&v=4
url: https://github.com/coleifer
- login: Bahtya
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34988899?u=b8e3c0cf26f4bd1faea265d2f5f66f564af63463&v=4
url: https://github.com/Bahtya
- login: saitarrun
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/116748905?u=3433afbaf06676a482ebf4ba33b08ddb3fc5c5bf&v=4
url: https://github.com/saitarrun
- login: cepedus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/26345924?u=38495abbdbb8695dd76478cae5963bf994c498bc&v=4
url: https://github.com/cepedus
- login: christiansousadev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/103544118?u=690f3f76d1dc4d0929de5020679d5604f860acbc&v=4
url: https://github.com/christiansousadev
- login: gaardhus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/46934916?u=18d7aacc6ce59f054749209645d11cfe77b52f90&v=4
url: https://github.com/gaardhus
- login: y2kbugger
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6101677?u=1d50077e29582dc01fcbdff846f04fe7ec73fe2e&v=4
url: https://github.com/y2kbugger
- login: Garrett-R
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6614695?u=c128fd775002882f6e391bda5a89d1bdc5bdf45f&v=4
url: https://github.com/Garrett-R
- login: TaigoFr
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/17792131?u=372b27056ec82f1ae03d8b3f37ef55b04a7cfdd1&v=4
url: https://github.com/TaigoFr
- login: stan-dot
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/56644812?u=a7dd773084f1c17c5f05019cc25a984e24873691&v=4
url: https://github.com/stan-dot
- login: Damon0603
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/110039208?u=f24bf5c30317bc4959118d1b919587c473a865b6&v=4
url: https://github.com/Damon0603
- login: huynguyengl99
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/49433085?u=7b626115686c5d97a2a32a03119f5300e425cc9f&v=4
url: https://github.com/huynguyengl99
- login: davidbrochart
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4711805?u=d39696d995a9e02ec3613ffb2f62b20b14f92f26&v=4
url: https://github.com/davidbrochart
- login: CharlieReitzel
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20848272?v=4
url: https://github.com/CharlieReitzel
- login: kiranzo
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1070878?u=68f78a891c9751dd87571ac712a6309090c4bc01&v=4
url: https://github.com/kiranzo
- login: dotmitsu
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/42657211?u=3bccc9a2f386a3f24230ec393080f8904fe2a5b2&v=4
url: https://github.com/dotmitsu
- login: Brikas
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/80290187?u=2b72e497ca4444ecec1f9dc2d1b8d5437a27b83f&v=4
url: https://github.com/Brikas
- login: BloodyRain2k
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1014362?v=4
url: https://github.com/BloodyRain2k
- login: usiqwerty
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/37992525?u=0c6e91d7b3887aa558755f4225ce74a003cbe852&v=4
url: https://github.com/usiqwerty
- login: skion
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/532192?v=4
url: https://github.com/skion
- login: florentx
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/142113?u=bf10f10080026346b092633c380977b61cee0d9c&v=4
url: https://github.com/florentx
- login: jc-louis
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51329768?v=4
url: https://github.com/jc-louis
- login: bughuntr7
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/236391583?u=7f51ff690e3a5711f845a115903c39e21c8af938&v=4
url: https://github.com/bughuntr7
- login: purepani
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7587353?v=4
url: https://github.com/purepani
- login: asmaier
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/3169297?u=84c83cbdb64104331febe16ae232ecf30952d01d&v=4
url: https://github.com/asmaier
- login: davidhuser
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4357648?u=6ed702f8f6d49a8b2a0ed33cbd8ab59c2d7db7f7&v=4
url: https://github.com/davidhuser
- login: XieJiSS
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/24671280?u=7ea0d9bfe46cf762594d62fd2f3c6d3813c3584c&v=4
url: https://github.com/XieJiSS
- login: profatsky
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/92920843?u=81e54bb0b613c171f7cd0ab3cbb58873782c9c9c&v=4
url: https://github.com/profatsky
- login: pythonweb2
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
url: https://github.com/pythonweb2
================================================
FILE: docs/en/data/skip_users.yml
================================================
- tiangolo
- codecov
- github-actions
- pre-commit-ci
- dependabot
================================================
FILE: docs/en/data/sponsors.yml
================================================
keystone:
- url: https://fastapicloud.com
title: FastAPI Cloud. By the same team behind FastAPI. You code. We Cloud.
img: https://fastapi.tiangolo.com/img/sponsors/fastapicloud.png
gold:
- url: https://blockbee.io?ref=fastapi
title: BlockBee Cryptocurrency Payment Gateway
img: https://fastapi.tiangolo.com/img/sponsors/blockbee.png
- url: https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge
title: "Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"
img: https://fastapi.tiangolo.com/img/sponsors/scalar.svg
- url: https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge
title: Auth, user management and more for your B2B product
img: https://fastapi.tiangolo.com/img/sponsors/propelauth.png
- url: https://liblab.com?utm_source=fastapi
title: liblab - Generate SDKs from FastAPI
img: https://fastapi.tiangolo.com/img/sponsors/liblab.png
- url: https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi
title: Deploy & scale any full-stack web app on Render. Focus on building apps, not infra.
img: https://fastapi.tiangolo.com/img/sponsors/render.svg
- url: https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi
title: Cut Code Review Time & Bugs in Half with CodeRabbit
img: https://fastapi.tiangolo.com/img/sponsors/coderabbit.png
- url: https://subtotal.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=open-source
title: The Gold Standard in Retail Account Linking
img: https://fastapi.tiangolo.com/img/sponsors/subtotal.svg
- url: https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi
title: Deploy enterprise applications at startup speed
img: https://fastapi.tiangolo.com/img/sponsors/railway.png
- url: https://serpapi.com/?utm_source=fastapi_website
title: "SerpApi: Web Search API"
img: https://fastapi.tiangolo.com/img/sponsors/serpapi.png
- url: https://www.greptile.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=fastapi_sponsor_page
title: "Greptile: The AI Code Reviewer"
img: https://fastapi.tiangolo.com/img/sponsors/greptile.png
silver:
- url: https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display
title: Pay as you go for market data
img: https://fastapi.tiangolo.com/img/sponsors/databento.svg
- url: https://www.svix.com/
title: Svix - Webhooks as a service
img: https://fastapi.tiangolo.com/img/sponsors/svix.svg
- url: https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral
title: Stainless | Generate best-in-class SDKs
img: https://fastapi.tiangolo.com/img/sponsors/stainless.png
- url: https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi
title: Fine-Grained Authorization for FastAPI
img: https://fastapi.tiangolo.com/img/sponsors/permit.png
- url: https://www.interviewpal.com/?utm_source=fastapi&utm_medium=open-source&utm_campaign=dev-hiring
title: InterviewPal - AI Interview Coach for Engineers and Devs
img: https://fastapi.tiangolo.com/img/sponsors/interviewpal.png
- url: https://dribia.com/en/
title: Dribia - Data Science within your reach
img: https://fastapi.tiangolo.com/img/sponsors/dribia.png
- url: https://talordata.com/?campaignid=oh5dVZ3Zc3YGiAI2&utm_source=fastapi&utm_term=fastapi
title: TalorData SERP API - Multi-Engine Search Results Data
img: https://fastapi.tiangolo.com/img/sponsors/talordata.png
bronze:
- url: https://www.exoflare.com/open-source/?utm_source=FastAPI&utm_campaign=open_source
title: Biosecurity risk assessments made easy.
img: https://fastapi.tiangolo.com/img/sponsors/exoflare.png
# - url: https://testdriven.io/courses/tdd-fastapi/
# title: Learn to build high-quality web apps with best practices
# img: https://fastapi.tiangolo.com/img/sponsors/testdriven.svg
- url: https://www.testmu.ai/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage
title: TestMu AI. The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering.
img: https://fastapi.tiangolo.com/img/sponsors/testmu.png
================================================
FILE: docs/en/data/sponsors_badge.yml
================================================
logins:
- jina-ai
- deta
- investsuite
- mikeckennedy
- deepset-ai
- cryptapi
- xoflare
- DropbaseHQ
- VincentParedes
- BLUE-DEVIL1134
- ObliviousAI
- Doist
- nihpo
- armand-sauzay
- databento-bot
- databento
- nanram22
- Flint-company
- porter-dev
- fern-api
- ndimares
- svixhq
- Alek99
- codacy
- zanfaruqui
- scalar
- bump-sh
- andrew-propelauth
- svix
- zuplo-oss
- zuplo
- Kong
- speakeasy-api
- jess-render
- blockbee-io
- liblaber
- render-sponsorships
- renderinc
- stainless-api
- snapit-cypher
- coderabbitai
- permitio
- LambdaTest-Inc
- dribia
- madisonredtfeldt
- railwayapp
- subtotal
- requestly
- greptileai
- talorelowen
================================================
FILE: docs/en/data/topic_repos.yml
================================================
- name: full-stack-fastapi-template
html_url: https://github.com/fastapi/full-stack-fastapi-template
stars: 42944
owner_login: fastapi
owner_html_url: https://github.com/fastapi
- name: Hello-Python
html_url: https://github.com/mouredev/Hello-Python
stars: 35430
owner_login: mouredev
owner_html_url: https://github.com/mouredev
- name: serve
html_url: https://github.com/jina-ai/serve
stars: 21876
owner_login: jina-ai
owner_html_url: https://github.com/jina-ai
- name: HivisionIDPhotos
html_url: https://github.com/Zeyi-Lin/HivisionIDPhotos
stars: 21054
owner_login: Zeyi-Lin
owner_html_url: https://github.com/Zeyi-Lin
- name: sqlmodel
html_url: https://github.com/fastapi/sqlmodel
stars: 17886
owner_login: fastapi
owner_html_url: https://github.com/fastapi
- name: Douyin_TikTok_Download_API
html_url: https://github.com/Evil0ctal/Douyin_TikTok_Download_API
stars: 17546
owner_login: Evil0ctal
owner_html_url: https://github.com/Evil0ctal
- name: fastapi-best-practices
html_url: https://github.com/zhanymkanov/fastapi-best-practices
stars: 17138
owner_login: zhanymkanov
owner_html_url: https://github.com/zhanymkanov
- name: SurfSense
html_url: https://github.com/MODSetter/SurfSense
stars: 14045
owner_login: MODSetter
owner_html_url: https://github.com/MODSetter
- name: machine-learning-zoomcamp
html_url: https://github.com/DataTalksClub/machine-learning-zoomcamp
stars: 13015
owner_login: DataTalksClub
owner_html_url: https://github.com/DataTalksClub
- name: fastapi_mcp
html_url: https://github.com/tadata-org/fastapi_mcp
stars: 11837
owner_login: tadata-org
owner_html_url: https://github.com/tadata-org
- name: awesome-fastapi
html_url: https://github.com/mjhea0/awesome-fastapi
stars: 11315
owner_login: mjhea0
owner_html_url: https://github.com/mjhea0
- name: XHS-Downloader
html_url: https://github.com/JoeanAmier/XHS-Downloader
stars: 11013
owner_login: JoeanAmier
owner_html_url: https://github.com/JoeanAmier
- name: polar
html_url: https://github.com/polarsource/polar
stars: 9775
owner_login: polarsource
owner_html_url: https://github.com/polarsource
- name: pycaret
html_url: https://github.com/pycaret/pycaret
stars: 9753
owner_login: pycaret
owner_html_url: https://github.com/pycaret
- name: FastUI
html_url: https://github.com/pydantic/FastUI
stars: 8961
owner_login: pydantic
owner_html_url: https://github.com/pydantic
- name: FileCodeBox
html_url: https://github.com/vastsa/FileCodeBox
stars: 8241
owner_login: vastsa
owner_html_url: https://github.com/vastsa
- name: nonebot2
html_url: https://github.com/nonebot/nonebot2
stars: 7488
owner_login: nonebot
owner_html_url: https://github.com/nonebot
- name: hatchet
html_url: https://github.com/hatchet-dev/hatchet
stars: 7044
owner_login: hatchet-dev
owner_html_url: https://github.com/hatchet-dev
- name: fastapi-users
html_url: https://github.com/fastapi-users/fastapi-users
stars: 6107
owner_login: fastapi-users
owner_html_url: https://github.com/fastapi-users
- name: serge
html_url: https://github.com/serge-chat/serge
stars: 5731
owner_login: serge-chat
owner_html_url: https://github.com/serge-chat
- name: Yuxi
html_url: https://github.com/xerrors/Yuxi
stars: 5063
owner_login: xerrors
owner_html_url: https://github.com/xerrors
- name: Kokoro-FastAPI
html_url: https://github.com/remsky/Kokoro-FastAPI
stars: 4785
owner_login: remsky
owner_html_url: https://github.com/remsky
- name: strawberry
html_url: https://github.com/strawberry-graphql/strawberry
stars: 4649
owner_login: strawberry-graphql
owner_html_url: https://github.com/strawberry-graphql
- name: devpush
html_url: https://github.com/hunvreus/devpush
stars: 4641
owner_login: hunvreus
owner_html_url: https://github.com/hunvreus
- name: poem
html_url: https://github.com/poem-web/poem
stars: 4387
owner_login: poem-web
owner_html_url: https://github.com/poem-web
- name: dynaconf
html_url: https://github.com/dynaconf/dynaconf
stars: 4291
owner_login: dynaconf
owner_html_url: https://github.com/dynaconf
- name: chatgpt-web-share
html_url: https://github.com/chatpire/chatgpt-web-share
stars: 4269
owner_login: chatpire
owner_html_url: https://github.com/chatpire
- name: logfire
html_url: https://github.com/pydantic/logfire
stars: 4206
owner_login: pydantic
owner_html_url: https://github.com/pydantic
- name: atrilabs-engine
html_url: https://github.com/Atri-Labs/atrilabs-engine
stars: 4080
owner_login: Atri-Labs
owner_html_url: https://github.com/Atri-Labs
- name: huma
html_url: https://github.com/danielgtaylor/huma
stars: 4043
owner_login: danielgtaylor
owner_html_url: https://github.com/danielgtaylor
- name: datamodel-code-generator
html_url: https://github.com/koxudaxi/datamodel-code-generator
stars: 3882
owner_login: koxudaxi
owner_html_url: https://github.com/koxudaxi
- name: LitServe
html_url: https://github.com/Lightning-AI/LitServe
stars: 3879
owner_login: Lightning-AI
owner_html_url: https://github.com/Lightning-AI
- name: fastapi-admin
html_url: https://github.com/fastapi-admin/fastapi-admin
stars: 3759
owner_login: fastapi-admin
owner_html_url: https://github.com/fastapi-admin
- name: mcp-context-forge
html_url: https://github.com/IBM/mcp-context-forge
stars: 3644
owner_login: IBM
owner_html_url: https://github.com/IBM
- name: tracecat
html_url: https://github.com/TracecatHQ/tracecat
stars: 3564
owner_login: TracecatHQ
owner_html_url: https://github.com/TracecatHQ
- name: farfalle
html_url: https://github.com/rashadphz/farfalle
stars: 3530
owner_login: rashadphz
owner_html_url: https://github.com/rashadphz
- name: opyrator
html_url: https://github.com/ml-tooling/opyrator
stars: 3137
owner_login: ml-tooling
owner_html_url: https://github.com/ml-tooling
- name: honcho
html_url: https://github.com/plastic-labs/honcho
stars: 3135
owner_login: plastic-labs
owner_html_url: https://github.com/plastic-labs
- name: docarray
html_url: https://github.com/docarray/docarray
stars: 3118
owner_login: docarray
owner_html_url: https://github.com/docarray
- name: fastapi-realworld-example-app
html_url: https://github.com/nsidnev/fastapi-realworld-example-app
stars: 3111
owner_login: nsidnev
owner_html_url: https://github.com/nsidnev
- name: uvicorn-gunicorn-fastapi-docker
html_url: https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker
stars: 2912
owner_login: tiangolo
owner_html_url: https://github.com/tiangolo
- name: FastAPI-template
html_url: https://github.com/s3rius/FastAPI-template
stars: 2780
owner_login: s3rius
owner_html_url: https://github.com/s3rius
- name: sqladmin
html_url: https://github.com/smithyhq/sqladmin
stars: 2716
owner_login: smithyhq
owner_html_url: https://github.com/smithyhq
- name: best-of-web-python
html_url: https://github.com/ml-tooling/best-of-web-python
stars: 2711
owner_login: ml-tooling
owner_html_url: https://github.com/ml-tooling
- name: YC-Killer
html_url: https://github.com/sahibzada-allahyar/YC-Killer
stars: 2626
owner_login: sahibzada-allahyar
owner_html_url: https://github.com/sahibzada-allahyar
- name: fastapi-react
html_url: https://github.com/Buuntu/fastapi-react
stars: 2581
owner_login: Buuntu
owner_html_url: https://github.com/Buuntu
- name: supabase-py
html_url: https://github.com/supabase/supabase-py
stars: 2499
owner_login: supabase
owner_html_url: https://github.com/supabase
- name: RasaGPT
html_url: https://github.com/paulpierre/RasaGPT
stars: 2466
owner_login: paulpierre
owner_html_url: https://github.com/paulpierre
- name: NoteDiscovery
html_url: https://github.com/gamosoft/NoteDiscovery
stars: 2465
owner_login: gamosoft
owner_html_url: https://github.com/gamosoft
- name: 30-Days-of-Python
html_url: https://github.com/codingforentrepreneurs/30-Days-of-Python
stars: 2459
owner_login: codingforentrepreneurs
owner_html_url: https://github.com/codingforentrepreneurs
- name: AIstudioProxyAPI
html_url: https://github.com/CJackHwang/AIstudioProxyAPI
stars: 2346
owner_login: CJackHwang
owner_html_url: https://github.com/CJackHwang
- name: nextpy
html_url: https://github.com/dot-agent/nextpy
stars: 2336
owner_login: dot-agent
owner_html_url: https://github.com/dot-agent
- name: langserve
html_url: https://github.com/langchain-ai/langserve
stars: 2319
owner_login: langchain-ai
owner_html_url: https://github.com/langchain-ai
- name: fastapi-utils
html_url: https://github.com/fastapiutils/fastapi-utils
stars: 2306
owner_login: fastapiutils
owner_html_url: https://github.com/fastapiutils
- name: fastapi-langgraph-agent-production-ready-template
html_url: https://github.com/wassim249/fastapi-langgraph-agent-production-ready-template
stars: 2218
owner_login: wassim249
owner_html_url: https://github.com/wassim249
- name: fastapi-best-architecture
html_url: https://github.com/fastapi-practices/fastapi-best-architecture
stars: 2206
owner_login: fastapi-practices
owner_html_url: https://github.com/fastapi-practices
- name: solara
html_url: https://github.com/widgetti/solara
stars: 2160
owner_login: widgetti
owner_html_url: https://github.com/widgetti
- name: vue-fastapi-admin
html_url: https://github.com/mizhexiaoxiao/vue-fastapi-admin
stars: 2108
owner_login: mizhexiaoxiao
owner_html_url: https://github.com/mizhexiaoxiao
- name: mangum
html_url: https://github.com/Kludex/mangum
stars: 2106
owner_login: Kludex
owner_html_url: https://github.com/Kludex
- name: slowapi
html_url: https://github.com/laurentS/slowapi
stars: 1960
owner_login: laurentS
owner_html_url: https://github.com/laurentS
- name: xhs_ai_publisher
html_url: https://github.com/BetaStreetOmnis/xhs_ai_publisher
stars: 1948
owner_login: BetaStreetOmnis
owner_html_url: https://github.com/BetaStreetOmnis
- name: agentkit
html_url: https://github.com/BCG-X-Official/agentkit
stars: 1944
owner_login: BCG-X-Official
owner_html_url: https://github.com/BCG-X-Official
- name: openapi-python-client
html_url: https://github.com/openapi-generators/openapi-python-client
stars: 1941
owner_login: openapi-generators
owner_html_url: https://github.com/openapi-generators
- name: manage-fastapi
html_url: https://github.com/ycd/manage-fastapi
stars: 1901
owner_login: ycd
owner_html_url: https://github.com/ycd
- name: piccolo
html_url: https://github.com/piccolo-orm/piccolo
stars: 1896
owner_login: piccolo-orm
owner_html_url: https://github.com/piccolo-orm
- name: FastAPI-boilerplate
html_url: https://github.com/benavlabs/FastAPI-boilerplate
stars: 1892
owner_login: benavlabs
owner_html_url: https://github.com/benavlabs
- name: fastapi-cache
html_url: https://github.com/long2ice/fastapi-cache
stars: 1859
owner_login: long2ice
owner_html_url: https://github.com/long2ice
- name: any-auto-register
html_url: https://github.com/lxf746/any-auto-register
stars: 1857
owner_login: lxf746
owner_html_url: https://github.com/lxf746
- name: python-week-2022
html_url: https://github.com/rochacbruno/python-week-2022
stars: 1810
owner_login: rochacbruno
owner_html_url: https://github.com/rochacbruno
- name: ormar
html_url: https://github.com/ormar-orm/ormar
stars: 1806
owner_login: ormar-orm
owner_html_url: https://github.com/ormar-orm
- name: termpair
html_url: https://github.com/cs01/termpair
stars: 1731
owner_login: cs01
owner_html_url: https://github.com/cs01
- name: fastapi-crudrouter
html_url: https://github.com/awtkns/fastapi-crudrouter
stars: 1687
owner_login: awtkns
owner_html_url: https://github.com/awtkns
- name: bracket
html_url: https://github.com/evroon/bracket
stars: 1653
owner_login: evroon
owner_html_url: https://github.com/evroon
- name: WebRPA
html_url: https://github.com/pmh1314520/WebRPA
stars: 1653
owner_login: pmh1314520
owner_html_url: https://github.com/pmh1314520
- name: fastapi-pagination
html_url: https://github.com/uriyyo/fastapi-pagination
stars: 1646
owner_login: uriyyo
owner_html_url: https://github.com/uriyyo
- name: langchain-serve
html_url: https://github.com/jina-ai/langchain-serve
stars: 1640
owner_login: jina-ai
owner_html_url: https://github.com/jina-ai
- name: headroom
html_url: https://github.com/chopratejas/headroom
stars: 1624
owner_login: chopratejas
owner_html_url: https://github.com/chopratejas
- name: awesome-fastapi-projects
html_url: https://github.com/Kludex/awesome-fastapi-projects
stars: 1599
owner_login: Kludex
owner_html_url: https://github.com/Kludex
- name: coronavirus-tracker-api
html_url: https://github.com/ExpDev07/coronavirus-tracker-api
stars: 1567
owner_login: ExpDev07
owner_html_url: https://github.com/ExpDev07
- name: fastapi-amis-admin
html_url: https://github.com/amisadmin/fastapi-amis-admin
stars: 1541
owner_login: amisadmin
owner_html_url: https://github.com/amisadmin
- name: fastcrud
html_url: https://github.com/benavlabs/fastcrud
stars: 1512
owner_login: benavlabs
owner_html_url: https://github.com/benavlabs
- name: open-wearables
html_url: https://github.com/the-momentum/open-wearables
stars: 1496
owner_login: the-momentum
owner_html_url: https://github.com/the-momentum
- name: fastapi-boilerplate
html_url: https://github.com/teamhide/fastapi-boilerplate
stars: 1486
owner_login: teamhide
owner_html_url: https://github.com/teamhide
- name: tavily-key-generator
html_url: https://github.com/skernelx/tavily-key-generator
stars: 1478
owner_login: skernelx
owner_html_url: https://github.com/skernelx
- name: prometheus-fastapi-instrumentator
html_url: https://github.com/trallnag/prometheus-fastapi-instrumentator
stars: 1451
owner_login: trallnag
owner_html_url: https://github.com/trallnag
- name: awesome-python-resources
html_url: https://github.com/DjangoEx/awesome-python-resources
stars: 1449
owner_login: DjangoEx
owner_html_url: https://github.com/DjangoEx
- name: fastapi-tutorial
html_url: https://github.com/liaogx/fastapi-tutorial
stars: 1399
owner_login: liaogx
owner_html_url: https://github.com/liaogx
- name: fastapi-code-generator
html_url: https://github.com/koxudaxi/fastapi-code-generator
stars: 1383
owner_login: koxudaxi
owner_html_url: https://github.com/koxudaxi
- name: budgetml
html_url: https://github.com/ebhy/budgetml
stars: 1345
owner_login: ebhy
owner_html_url: https://github.com/ebhy
- name: aktools
html_url: https://github.com/akfamily/aktools
stars: 1334
owner_login: akfamily
owner_html_url: https://github.com/akfamily
- name: RuoYi-Vue3-FastAPI
html_url: https://github.com/insistence/RuoYi-Vue3-FastAPI
stars: 1302
owner_login: insistence
owner_html_url: https://github.com/insistence
- name: bolt-python
html_url: https://github.com/slackapi/bolt-python
stars: 1296
owner_login: slackapi
owner_html_url: https://github.com/slackapi
- name: bedrock-chat
html_url: https://github.com/aws-samples/bedrock-chat
stars: 1288
owner_login: aws-samples
owner_html_url: https://github.com/aws-samples
- name: restish
html_url: https://github.com/rest-sh/restish
stars: 1279
owner_login: rest-sh
owner_html_url: https://github.com/rest-sh
- name: fastapi-alembic-sqlmodel-async
html_url: https://github.com/vargasjona/fastapi-alembic-sqlmodel-async
stars: 1270
owner_login: vargasjona
owner_html_url: https://github.com/vargasjona
- name: fastapi_production_template
html_url: https://github.com/zhanymkanov/fastapi_production_template
stars: 1243
owner_login: zhanymkanov
owner_html_url: https://github.com/zhanymkanov
- name: yubal
html_url: https://github.com/guillevc/yubal
stars: 1203
owner_login: guillevc
owner_html_url: https://github.com/guillevc
- name: langchain-extract
html_url: https://github.com/langchain-ai/langchain-extract
stars: 1196
owner_login: langchain-ai
owner_html_url: https://github.com/langchain-ai
- name: Chatterbox-TTS-Server
html_url: https://github.com/devnen/Chatterbox-TTS-Server
stars: 1194
owner_login: devnen
owner_html_url: https://github.com/devnen
================================================
FILE: docs/en/data/translation_reviewers.yml
================================================
s111d:
login: s111d
count: 147
avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
url: https://github.com/s111d
Xewus:
login: Xewus
count: 140
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
sodaMelon:
login: sodaMelon
count: 128
avatarUrl: https://avatars.githubusercontent.com/u/66295123?u=be939db90f1119efee9e6110cc05066ff1f40f00&v=4
url: https://github.com/sodaMelon
ceb10n:
login: ceb10n
count: 119
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
tokusumi:
login: tokusumi
count: 104
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
hard-coders:
login: hard-coders
count: 102
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4
url: https://github.com/hard-coders
hasansezertasan:
login: hasansezertasan
count: 95
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=d36995e41a00590da64e6204cfd112e0484ac1ca&v=4
url: https://github.com/hasansezertasan
alv2017:
login: alv2017
count: 88
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
url: https://github.com/alv2017
nazarepiedady:
login: nazarepiedady
count: 87
avatarUrl: https://avatars.githubusercontent.com/u/31008635?u=f69ddc4ea8bda3bdfac7aa0e2ea38de282e6ee2d&v=4
url: https://github.com/nazarepiedady
tiangolo:
login: tiangolo
count: 82
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
AlertRED:
login: AlertRED
count: 81
avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
url: https://github.com/AlertRED
Alexandrhub:
login: Alexandrhub
count: 68
avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
url: https://github.com/Alexandrhub
nilslindemann:
login: nilslindemann
count: 67
avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
url: https://github.com/nilslindemann
YuriiMotov:
login: YuriiMotov
count: 67
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
cassiobotaro:
login: cassiobotaro
count: 64
avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=a08022b191ddbd0a6159b2981d9d878b6d5bb71f&v=4
url: https://github.com/cassiobotaro
waynerv:
login: waynerv
count: 63
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
mattwang44:
login: mattwang44
count: 61
avatarUrl: https://avatars.githubusercontent.com/u/24987826?u=58e37fb3927b9124b458945ac4c97aa0f1062d85&v=4
url: https://github.com/mattwang44
Laineyzhang55:
login: Laineyzhang55
count: 48
avatarUrl: https://avatars.githubusercontent.com/u/59285379?v=4
url: https://github.com/Laineyzhang55
Kludex:
login: Kludex
count: 47
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=df8a3f06ba8f55ae1967a3e2d5ed882903a4e330&v=4
url: https://github.com/Kludex
komtaki:
login: komtaki
count: 45
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
url: https://github.com/komtaki
svlandeg:
login: svlandeg
count: 43
avatarUrl: https://avatars.githubusercontent.com/u/8796347?u=556c97650c27021911b0b9447ec55e75987b0e8a&v=4
url: https://github.com/svlandeg
rostik1410:
login: rostik1410
count: 42
avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
url: https://github.com/rostik1410
alperiox:
login: alperiox
count: 42
avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
url: https://github.com/alperiox
Rishat-F:
login: Rishat-F
count: 42
avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
url: https://github.com/Rishat-F
Winand:
login: Winand
count: 40
avatarUrl: https://avatars.githubusercontent.com/u/53390?u=bb0e71a2fc3910a8e0ee66da67c33de40ea695f8&v=4
url: https://github.com/Winand
solomein-sv:
login: solomein-sv
count: 38
avatarUrl: https://avatars.githubusercontent.com/u/46193920?u=789927ee09cfabd752d3bd554fa6baf4850d2777&v=4
url: https://github.com/solomein-sv
mezgoodle:
login: mezgoodle
count: 38
avatarUrl: https://avatars.githubusercontent.com/u/41520940?u=4a9c765af688389d54296845d18b8f6cd6ddf09a&v=4
url: https://github.com/mezgoodle
JavierSanchezCastro:
login: JavierSanchezCastro
count: 38
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
url: https://github.com/JavierSanchezCastro
alejsdev:
login: alejsdev
count: 37
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4
url: https://github.com/alejsdev
stlucasgarcia:
login: stlucasgarcia
count: 36
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=c22d8850e9dc396a8820766a59837f967e14f9a0&v=4
url: https://github.com/stlucasgarcia
SwftAlpc:
login: SwftAlpc
count: 36
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
url: https://github.com/SwftAlpc
timothy-jeong:
login: timothy-jeong
count: 36
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
url: https://github.com/timothy-jeong
rjNemo:
login: rjNemo
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
yychanlee:
login: yychanlee
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/103817302?u=3a042740dc0ff58615da0d8679230966fd7693e8&v=4
url: https://github.com/yychanlee
Vincy1230:
login: Vincy1230
count: 34
avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4
url: https://github.com/Vincy1230
akarev0:
login: akarev0
count: 33
avatarUrl: https://avatars.githubusercontent.com/u/53393089?u=6e528bb4789d56af887ce6fe237bea4010885406&v=4
url: https://github.com/akarev0
romashevchenko:
login: romashevchenko
count: 32
avatarUrl: https://avatars.githubusercontent.com/u/132477732?v=4
url: https://github.com/romashevchenko
LorhanSohaky:
login: LorhanSohaky
count: 30
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
url: https://github.com/LorhanSohaky
black-redoc:
login: black-redoc
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/18581590?u=7b6336166d0797fbbd44ea70c1c3ecadfc89af9e&v=4
url: https://github.com/black-redoc
pedabraham:
login: pedabraham
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/16860088?u=abf922a7b920bf8fdb7867d8b43e091f1e796178&v=4
url: https://github.com/pedabraham
Smlep:
login: Smlep
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/16785985?u=ffe99fa954c8e774ef1117e58d34aece92051e27&v=4
url: https://github.com/Smlep
dedkot01:
login: dedkot01
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
url: https://github.com/dedkot01
hsuanchi:
login: hsuanchi
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=7d25a398e478b6e63503bf6f26c54efa9e0da07b&v=4
url: https://github.com/hsuanchi
dpinezich:
login: dpinezich
count: 28
avatarUrl: https://avatars.githubusercontent.com/u/3204540?u=a2e1465e3ee10d537614d513589607eddefde09f&v=4
url: https://github.com/dpinezich
maoyibo:
login: maoyibo
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
url: https://github.com/maoyibo
0417taehyun:
login: 0417taehyun
count: 27
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
url: https://github.com/0417taehyun
BilalAlpaslan:
login: BilalAlpaslan
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
junah201:
login: junah201
count: 26
avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
url: https://github.com/junah201
zy7y:
login: zy7y
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/67154681?u=5d634834cc514028ea3f9115f7030b99a1f4d5a4&v=4
url: https://github.com/zy7y
mycaule:
login: mycaule
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/6161385?u=e3cec75bd6d938a0d73fae0dc5534d1ab2ed1b0e&v=4
url: https://github.com/mycaule
Aruelius:
login: Aruelius
count: 25
avatarUrl: https://avatars.githubusercontent.com/u/25380989?u=574f8cfcda3ea77a3f81884f6b26a97068e36a9d&v=4
url: https://github.com/Aruelius
wisderfin:
login: wisderfin
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/77553770?u=9a23740d520d65dc0051cdc1ecd87f31cb900313&v=4
url: https://github.com/wisderfin
OzgunCaglarArslan:
login: OzgunCaglarArslan
count: 24
avatarUrl: https://avatars.githubusercontent.com/u/86166426?v=4
url: https://github.com/OzgunCaglarArslan
ycd:
login: ycd
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
url: https://github.com/ycd
sh0nk:
login: sh0nk
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
axel584:
login: axel584
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
url: https://github.com/axel584
DianaTrufanova:
login: DianaTrufanova
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/119067607?u=1cd55f841b68b4a187fa6d06a7dafa5f070195aa&v=4
url: https://github.com/DianaTrufanova
AGolicyn:
login: AGolicyn
count: 21
avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
url: https://github.com/AGolicyn
Attsun1031:
login: Attsun1031
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
url: https://github.com/Attsun1031
delhi09:
login: delhi09
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4
url: https://github.com/delhi09
rogerbrinkmann:
login: rogerbrinkmann
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/5690226?u=a1fe0aee927d33ce9db8c455eabc40c1cdf2bb65&v=4
url: https://github.com/rogerbrinkmann
DevDae:
login: DevDae
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/87962045?u=08e10fa516e844934f4b3fc7c38b33c61697e4a1&v=4
url: https://github.com/DevDae
sattosan:
login: sattosan
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/20574756?u=b0d8474d2938189c6954423ae8d81d91013f80a8&v=4
url: https://github.com/sattosan
maru0123-2004:
login: maru0123-2004
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/43961566?u=16ed8603a4d6a4665cb6c53a7aece6f31379b769&v=4
url: https://github.com/maru0123-2004
yes0ng:
login: yes0ng
count: 19
avatarUrl: https://avatars.githubusercontent.com/u/25501794?u=3aed18b0d491e0220a167a1e9e58bea3638c6707&v=4
url: https://github.com/yes0ng
ComicShrimp:
login: ComicShrimp
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
url: https://github.com/ComicShrimp
simatheone:
login: simatheone
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/78508673?u=1b9658d9ee0bde33f56130dd52275493ddd38690&v=4
url: https://github.com/simatheone
ivan-abc:
login: ivan-abc
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
url: https://github.com/ivan-abc
Limsunoh:
login: Limsunoh
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/90311848?u=f456e0c5709fd50c8cd2898b551558eda14e5f21&v=4
url: https://github.com/Limsunoh
SofiiaTrufanova:
login: SofiiaTrufanova
count: 18
avatarUrl: https://avatars.githubusercontent.com/u/63260929?u=483e0b64fabc76343b3be39b7e1dcb930a95e1bb&v=4
url: https://github.com/SofiiaTrufanova
bezaca:
login: bezaca
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4
url: https://github.com/bezaca
lbmendes:
login: lbmendes
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/80999926?u=646619e2f07ac5a7c3f65fe7834197461a4fff9f&v=4
url: https://github.com/lbmendes
spacesphere:
login: spacesphere
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/34628304?u=cde91f6002dd33156e1bf8005f11a7a3ed76b790&v=4
url: https://github.com/spacesphere
panko:
login: panko
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/1569515?u=a84a5d255621ed82f8e1ca052f5f2eeb75997da2&v=4
url: https://github.com/panko
jeison-araya:
login: jeison-araya
count: 17
avatarUrl: https://avatars.githubusercontent.com/u/57369279?u=17001e68af7d8e5b8c343e5e9df4050f419998d5&v=4
url: https://github.com/jeison-araya
yanever:
login: yanever
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/21978760?v=4
url: https://github.com/yanever
mastizada:
login: mastizada
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/1975818?u=0751a06d7271c8bf17cb73b1b845644ab4d2c6dc&v=4
url: https://github.com/mastizada
Joao-Pedro-P-Holanda:
login: Joao-Pedro-P-Holanda
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
url: https://github.com/Joao-Pedro-P-Holanda
JaeHyuckSa:
login: JaeHyuckSa
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/104830931?u=f3b4a2baea550f428a4c602a30ebee6721c1e3df&v=4
url: https://github.com/JaeHyuckSa
Jedore:
login: Jedore
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/17944025?u=81d503e1c800eb666b3861ca47a3a773bbc3f539&v=4
url: https://github.com/Jedore
kim-sangah:
login: kim-sangah
count: 15
avatarUrl: https://avatars.githubusercontent.com/u/173775778?v=4
url: https://github.com/kim-sangah
PandaHun:
login: PandaHun
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/13096845?u=646eba44db720e37d0dbe8e98e77ab534ea78a20&v=4
url: https://github.com/PandaHun
dukkee:
login: dukkee
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/36825394?u=ccfd86e6a4f2d093dad6f7544cc875af67fa2df8&v=4
url: https://github.com/dukkee
BORA040126:
login: BORA040126
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/88664069?u=98e382727a485971e04aaa7c873d9a75a17ee3be&v=4
url: https://github.com/BORA040126
mattkoehne:
login: mattkoehne
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/80362153?v=4
url: https://github.com/mattkoehne
jovicon:
login: jovicon
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4
url: https://github.com/jovicon
izaguerreiro:
login: izaguerreiro
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
url: https://github.com/izaguerreiro
jburckel:
login: jburckel
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/11768758?u=044462e4130e086a0621f4abb45f0d7a289ab7fa&v=4
url: https://github.com/jburckel
peidrao:
login: peidrao
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=979c62398e16ff000cc0faa028e028efd679887c&v=4
url: https://github.com/peidrao
impocode:
login: impocode
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/109408819?u=9cdfc5ccb31a2094c520f41b6087012fa9048982&v=4
url: https://github.com/impocode
waketzheng:
login: waketzheng
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/35413830?u=df19e4fd5bb928e7d086e053ef26a46aad23bf84&v=4
url: https://github.com/waketzheng
wesinalves:
login: wesinalves
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/13563128?u=9eb17ed50645dd684bfec47e75dba4e9772ec9c1&v=4
url: https://github.com/wesinalves
andersonrocha0:
login: andersonrocha0
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4
url: https://github.com/andersonrocha0
NastasiaSaby:
login: NastasiaSaby
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4
url: https://github.com/NastasiaSaby
oandersonmagalhaes:
login: oandersonmagalhaes
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/83456692?u=daf5f302a59b950efc6d21129314af207e35441f&v=4
url: https://github.com/oandersonmagalhaes
mkdir700:
login: mkdir700
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/56359329?u=818e5f4b4dcc1a6ffb3e5aaa08fd827e5a726dfd&v=4
url: https://github.com/mkdir700
batlopes:
login: batlopes
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
url: https://github.com/batlopes
joonas-yoon:
login: joonas-yoon
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/9527681?u=0166d22ef4749e617c6516e79f833cd8d73f1949&v=4
url: https://github.com/joonas-yoon
baseplate-admin:
login: baseplate-admin
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/61817579?u=ce4c268fa949ae9a0290996e7949195302055812&v=4
url: https://github.com/baseplate-admin
KaniKim:
login: KaniKim
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=296dbdd490e0eb96e3d45a2608c065603b17dc31&v=4
url: https://github.com/KaniKim
gitgernit:
login: gitgernit
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/129539613?u=d04f10143ab32c93f563ea14bf242d1d2bc991b0&v=4
url: https://github.com/gitgernit
kwang1215:
login: kwang1215
count: 12
avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
url: https://github.com/kwang1215
AdrianDeAnda:
login: AdrianDeAnda
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4
url: https://github.com/AdrianDeAnda
blt232018:
login: blt232018
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
url: https://github.com/blt232018
NinaHwang:
login: NinaHwang
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=241f2cb6d38a2d379536608a8ea5a22ed4b1a3ea&v=4
url: https://github.com/NinaHwang
glsglsgls:
login: glsglsgls
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/76133879?v=4
url: https://github.com/glsglsgls
k94-ishi:
login: k94-ishi
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4
url: https://github.com/k94-ishi
codespearhead:
login: codespearhead
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/72931357?u=0fce6b82219b604d58adb614a761556425579cb5&v=4
url: https://github.com/codespearhead
emrhnsyts:
login: emrhnsyts
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/42899027?u=ad26798e3f8feed2041c5dd5f87e58933d6c3283&v=4
url: https://github.com/emrhnsyts
Lufa1u:
login: Lufa1u
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/112495876?u=087658920ed9e74311597bdd921d8d2de939d276&v=4
url: https://github.com/Lufa1u
KNChiu:
login: KNChiu
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/36751646?v=4
url: https://github.com/KNChiu
Zhongheng-Cheng:
login: Zhongheng-Cheng
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
url: https://github.com/Zhongheng-Cheng
Pyth3rEx:
login: Pyth3rEx
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/26427764?u=087724f74d813c95925d51e354554bd4b6d6bb60&v=4
url: https://github.com/Pyth3rEx
mariacamilagl:
login: mariacamilagl
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
url: https://github.com/mariacamilagl
ryuckel:
login: ryuckel
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/36391432?u=094eec0cfddd5013f76f31e55e56147d78b19553&v=4
url: https://github.com/ryuckel
umitkaanusta:
login: umitkaanusta
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/53405015?v=4
url: https://github.com/umitkaanusta
kty4119:
login: kty4119
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
url: https://github.com/kty4119
RobotToI:
login: RobotToI
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/44951382?u=e41dbc19191ce7abed86694b1a44ea0523e1c60e&v=4
url: https://github.com/RobotToI
vitumenezes:
login: vitumenezes
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/9680878?u=05fd25cfafdc09382bf8907c37293a696c205754&v=4
url: https://github.com/vitumenezes
fcrozetta:
login: fcrozetta
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/8006246?u=fa2a743e803de2c3a84d3ed8042faefed16c5e43&v=4
url: https://github.com/fcrozetta
sUeharaE4:
login: sUeharaE4
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/44468359?v=4
url: https://github.com/sUeharaE4
Ernilia:
login: Ernilia
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/125735800?u=13bfaac417a53fd5b5cf992efea363ca72598813&v=4
url: https://github.com/Ernilia
socket-socket:
login: socket-socket
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/121552599?u=104df6503242e8d762fe293e7036f7260f245d49&v=4
url: https://github.com/socket-socket
nick-cjyx9:
login: nick-cjyx9
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/119087246?u=3d51dcbd79222ecb6538642f31dc7c8bb708d191&v=4
url: https://github.com/nick-cjyx9
marcelomarkus:
login: marcelomarkus
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/20115018?u=dda090ce9160ef0cd2ff69b1e5ea741283425cba&v=4
url: https://github.com/marcelomarkus
lucasbalieiro:
login: lucasbalieiro
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/37416577?u=d144221c34c08adac8b20e1833d776ffa1c4b1d0&v=4
url: https://github.com/lucasbalieiro
RunningIkkyu:
login: RunningIkkyu
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
url: https://github.com/RunningIkkyu
JulianMaurin:
login: JulianMaurin
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
url: https://github.com/JulianMaurin
JeongHyeongKim:
login: JeongHyeongKim
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/26577800?u=77f060f4686f32c248907b81b16ee2b3177ca44c&v=4
url: https://github.com/JeongHyeongKim
arthurio:
login: arthurio
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/950449?u=76b997138273ce5e1990b971c4f27c9aff979fd5&v=4
url: https://github.com/arthurio
Lenclove:
login: Lenclove
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/32355298?u=d0065e01650c63c2b2413f42d983634b2ea85481&v=4
url: https://github.com/Lenclove
eVery1337:
login: eVery1337
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/84917945?u=7af243f05ecfba59191199a70d8ba365c1327768&v=4
url: https://github.com/eVery1337
aykhans:
login: aykhans
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/88669260?u=798da457cc3276d3c6dd7fd628d0005ad8b298cc&v=4
url: https://github.com/aykhans
riroan:
login: riroan
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/33053284?u=2d18e3771506ee874b66d6aa2b3b1107fd95c38f&v=4
url: https://github.com/riroan
MinLee0210:
login: MinLee0210
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/57653278?u=e7c4d8d7eeb7bceed1680ef0e5dafec0695f57e0&v=4
url: https://github.com/MinLee0210
yodai-yodai:
login: yodai-yodai
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/7031039?u=4f3593f5931892b931a745cfab846eff6e9332e7&v=4
url: https://github.com/yodai-yodai
JoaoGustavoRogel:
login: JoaoGustavoRogel
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/29525510?u=a0a91251f5e43e132608d55d28ccb8645c5ea405&v=4
url: https://github.com/JoaoGustavoRogel
valentinDruzhinin:
login: valentinDruzhinin
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
Yarous:
login: Yarous
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/61277193?u=5b462347458a373b2d599c6f416d2b75eddbffad&v=4
url: https://github.com/Yarous
dimaqq:
login: dimaqq
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/662249?u=15313dec91bae789685e4abb3c2152251de41948&v=4
url: https://github.com/dimaqq
julianofischer:
login: julianofischer
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/158303?u=d91662eb949d4cc7368831cf37a5cdfd90b7010c&v=4
url: https://github.com/julianofischer
bnzone:
login: bnzone
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/39371503?u=c16f00c41d88479fa2d57b0d7d233b758eacce2d&v=4
url: https://github.com/bnzone
shamosishen:
login: shamosishen
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/9498321?u=c83c20c79e019a0b555a125adf20fc4fb7a882c8&v=4
url: https://github.com/shamosishen
mertssmnoglu:
login: mertssmnoglu
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/61623638?u=59dd885b68ff1832f9ab3b4a4446896358c23442&v=4
url: https://github.com/mertssmnoglu
mahone3297:
login: mahone3297
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/1701379?u=20588ff0e456d13e8017333eb237595d11410234&v=4
url: https://github.com/mahone3297
KimJoonSeo:
login: KimJoonSeo
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/17760162?u=a58cdc77ae1c069a64166f7ecc4d42eecfd9a468&v=4
url: https://github.com/KimJoonSeo
camigomezdev:
login: camigomezdev
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/16061815?u=25b5ebc042fff53fa03dc107ded10e36b1b7a5b9&v=4
url: https://github.com/camigomezdev
minaton-ru:
login: minaton-ru
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/53541518?u=67336ca11a85493f75031508aade588dad3b9910&v=4
url: https://github.com/minaton-ru
sungchan1:
login: sungchan1
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/28076127?u=fadbf24840186aca639d344bb3e0ecf7ff3441cf&v=4
url: https://github.com/sungchan1
roli2py:
login: roli2py
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/61126128?u=bcb7a286e435a6b9d6a84b07db1232580ee796d4&v=4
url: https://github.com/roli2py
Serrones:
login: Serrones
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
url: https://github.com/Serrones
israteneda:
login: israteneda
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/20668624?u=67574648f89019d1c73b16a6a009da659557f9e5&v=4
url: https://github.com/israteneda
krocdort:
login: krocdort
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/34248814?v=4
url: https://github.com/krocdort
anthonycepeda:
login: anthonycepeda
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
url: https://github.com/anthonycepeda
fabioueno:
login: fabioueno
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/14273852?u=a3d546449cdc96621c32bcc26cf74be6e4390209&v=4
url: https://github.com/fabioueno
cfraboulet:
login: cfraboulet
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/62244267?u=ed0e286ba48fa1dafd64a08e50f3364b8e12df34&v=4
url: https://github.com/cfraboulet
HiemalBeryl:
login: HiemalBeryl
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/63165207?u=276f4af2829baf28b912c718675852bfccb0e7b4&v=4
url: https://github.com/HiemalBeryl
pablocm83:
login: pablocm83
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
url: https://github.com/pablocm83
d2a-raudenaerde:
login: d2a-raudenaerde
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/5213150?u=e6d0ef65c571c7e544fc1c7ec151c7c0a72fb6bb&v=4
url: https://github.com/d2a-raudenaerde
Zerohertz:
login: Zerohertz
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/42334717?u=5ebf4d33e73b1ad373154f6cdee44f7cab4d05ba&v=4
url: https://github.com/Zerohertz
EdmilsonRodrigues:
login: EdmilsonRodrigues
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4
url: https://github.com/EdmilsonRodrigues
deniscapeto:
login: deniscapeto
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/12864353?u=20c5b2300b264a585a8381acf3cef44bcfcc1ead&v=4
url: https://github.com/deniscapeto
bsab:
login: bsab
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/9799799?u=c4a09b1abb794cd8280c4793d43d0e2eb963ecda&v=4
url: https://github.com/bsab
ArcLightSlavik:
login: ArcLightSlavik
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4
url: https://github.com/ArcLightSlavik
Cajuteq:
login: Cajuteq
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/26676532?u=8ee0422981810e51480855de1c0d67b6b79cd3f2&v=4
url: https://github.com/Cajuteq
emmrichard:
login: emmrichard
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/1328018?u=8114d8fc0e8e42a092e4283013a1c54b792c466b&v=4
url: https://github.com/emmrichard
wakabame:
login: wakabame
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/35513518?u=41ef6b0a55076e5c540620d68fb006e386c2ddb0&v=4
url: https://github.com/wakabame
mawassk:
login: mawassk
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/84179197?v=4
url: https://github.com/mawassk
diogoduartec:
login: diogoduartec
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/31852339?u=7514a5f05fcbeccc62f8c5dc25879efeb1ef9335&v=4
url: https://github.com/diogoduartec
aqcool:
login: aqcool
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/52229895?v=4
url: https://github.com/aqcool
'1320555911':
login: '1320555911'
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/58590086?u=6d8f4fbf08d5ac72c1c895892c461c5e0b013dc3&v=4
url: https://github.com/1320555911
mcthesw:
login: mcthesw
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/61224072?u=82a1b106298348f060c3f4f39817e0cae5ce2b7c&v=4
url: https://github.com/mcthesw
xzmeng:
login: xzmeng
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
url: https://github.com/xzmeng
negadive:
login: negadive
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/47322392?u=c1be2e9b9b346b4a77d9157da2a5739ab25ce0f8&v=4
url: https://github.com/negadive
mbroton:
login: mbroton
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4
url: https://github.com/mbroton
Kirilex:
login: Kirilex
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/100281552?v=4
url: https://github.com/Kirilex
arunppsg:
login: arunppsg
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/26398753?v=4
url: https://github.com/arunppsg
dimastbk:
login: dimastbk
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/3132181?u=66587398d43466a1dc75c238df5f048e0afc77ed&v=4
url: https://github.com/dimastbk
dudyaosuplayer:
login: dudyaosuplayer
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/62661898?u=7864cc5f01b1c845ae8ad49acf45dec6faca0c57&v=4
url: https://github.com/dudyaosuplayer
talhaumer:
login: talhaumer
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/46643702?u=5d1fd7057ea9534fb3221931b809a3d750157212&v=4
url: https://github.com/talhaumer
bankofsardine:
login: bankofsardine
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/44944207?u=0368e1b698ffab6bf29e202f9fd2dddd352429f1&v=4
url: https://github.com/bankofsardine
Rekl0w:
login: Rekl0w
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/91488737?u=7d2b7791665e04a12695150776a1d516a6ea7d21&v=4
url: https://github.com/Rekl0w
rsip22:
login: rsip22
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/16676222?v=4
url: https://github.com/rsip22
jessicapaz:
login: jessicapaz
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/20428941?u=6ffdaab5a85bf77a2d8870dade5e53555f34577b&v=4
url: https://github.com/jessicapaz
mohsen-mahmoodi:
login: mohsen-mahmoodi
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/2872586?u=3a9fc1aa16a3a0ab93a1f8550de82a940592857d&v=4
url: https://github.com/mohsen-mahmoodi
jeesang7:
login: jeesang7
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/30719956?u=35fc8bca04d32d3c4ce085956f0636b959ba30f6&v=4
url: https://github.com/jeesang7
TemaSpb:
login: TemaSpb
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/20205738?u=d7dce0718720a7107803a573d628d8dd3d5c2fb4&v=4
url: https://github.com/TemaSpb
BugLight:
login: BugLight
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/13618366?u=7d733749f80e5f7e66a434cf42aedcfc60340f43&v=4
url: https://github.com/BugLight
0x4Dark:
login: 0x4Dark
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/7569289?v=4
url: https://github.com/0x4Dark
Wuerike:
login: Wuerike
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/35462243?u=80c753dedf4a78db12ef66316dbdebbe6d84a2b9&v=4
url: https://github.com/Wuerike
jvmazagao:
login: jvmazagao
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/22477816?u=2b57addf5830906bf6ae5f25cd4c8c2fa5c2d68e&v=4
url: https://github.com/jvmazagao
cun3yt:
login: cun3yt
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/24409240?u=06abfd77786db859b0602d5369d2ae18c932c17c&v=4
url: https://github.com/cun3yt
Mordson:
login: Mordson
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/39025897?u=b94ea96ef35bbe43bc85359cfb31d28ac16d470c&v=4
url: https://github.com/Mordson
aminkhani:
login: aminkhani
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/51851950?u=051896c4933816bc61d11091d887f6e8dfd1d27b&v=4
url: https://github.com/aminkhani
nifadyev:
login: nifadyev
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/36514612?u=e101da8641d5a09901d2155255a93f8ab3d9c468&v=4
url: https://github.com/nifadyev
LaurEars:
login: LaurEars
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/4914725?v=4
url: https://github.com/LaurEars
Chushine:
login: Chushine
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/135534400?v=4
url: https://github.com/Chushine
ChuyuChoyeon:
login: ChuyuChoyeon
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/129537877?u=f0c76f3327817a8b86b422d62e04a34bf2827f2b&v=4
url: https://github.com/ChuyuChoyeon
frwl404:
login: frwl404
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/42642656?u=8395a3d991d9fac86901277d76f0f70857b56ec5&v=4
url: https://github.com/frwl404
esrefzeki:
login: esrefzeki
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/54935247?u=193cf5a169ca05fc54995a4dceabc82c7dc6e5ea&v=4
url: https://github.com/esrefzeki
dtleal:
login: dtleal
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31096951?u=704664ec74ab655485e5c909b25de3fa09a922ba&v=4
url: https://github.com/dtleal
art3xa:
login: art3xa
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/92092049?v=4
url: https://github.com/art3xa
SamuelBFavarin:
login: SamuelBFavarin
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/17628602?u=5aac13ae492fa9a86e397a70803ac723dba2efe7&v=4
url: https://github.com/SamuelBFavarin
takacs:
login: takacs
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/44911031?u=f6c6b70b3ba86ceb93b0f9bcab609bf9328b2305&v=4
url: https://github.com/takacs
anton2yakovlev:
login: anton2yakovlev
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/44229180?u=ac245e57bc834ff80f08ca8128000bb650a77a3d&v=4
url: https://github.com/anton2yakovlev
ILoveSorasakiHina:
login: ILoveSorasakiHina
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/114038930?u=3d3ed8dc3bf57e641d1b26badee5bc79ef34f25b&v=4
url: https://github.com/ILoveSorasakiHina
devluisrodrigues:
login: devluisrodrigues
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/103431660?u=d9674a3249edc4601d2c712cdebf899918503c3a&v=4
url: https://github.com/devluisrodrigues
11kkw:
login: 11kkw
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/21125286?v=4
url: https://github.com/11kkw
lpdswing:
login: lpdswing
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/20874036?u=7a4fc3e4d0719e37b305deb7af234a7b63200787&v=4
url: https://github.com/lpdswing
SepehrRasouli:
login: SepehrRasouli
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/81516241?u=3987e880c77d653dd85963302150e07bb7c0ef99&v=4
url: https://github.com/SepehrRasouli
Zxilly:
login: Zxilly
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/31370133?u=c5359b8d9d80a7cdc23d5295d179ed90174996c8&v=4
url: https://github.com/Zxilly
eavv:
login: eavv
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/18273429?u=c05e8b4ea62810ee7889ca049e510cdd0a66fd26&v=4
url: https://github.com/eavv
AlexandreBiguet:
login: AlexandreBiguet
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1483079?u=ff926455cd4cab03c6c49441aa5dc2b21df3e266&v=4
url: https://github.com/AlexandreBiguet
FelipeSilva93:
login: FelipeSilva93
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/66804965?u=e7cb4b580e46f2e04ecb4cd4d7a12acdddd3c6c1&v=4
url: https://github.com/FelipeSilva93
peacekimjapan:
login: peacekimjapan
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/33534175?u=e4219bcebc3773a7068cc34c3eb268ef77cec31b&v=4
url: https://github.com/peacekimjapan
bas-baskara:
login: bas-baskara
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/41407847?u=cdabfaff7481c3323f24a76d9350393b964f2b89&v=4
url: https://github.com/bas-baskara
odiseo0:
login: odiseo0
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=75e754b9bd12ce6dfe90ad68e82e6f5255c7ac09&v=4
url: https://github.com/odiseo0
eryknn:
login: eryknn
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/87120651?v=4
url: https://github.com/eryknn
personage-hub:
login: personage-hub
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/76659786?v=4
url: https://github.com/personage-hub
aminalaee:
login: aminalaee
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/19784933?v=4
url: https://github.com/aminalaee
erfan-rfmhr:
login: erfan-rfmhr
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/98986056?u=0acda1ff1df0989f3f3eb79977baa35da4cb6c8c&v=4
url: https://github.com/erfan-rfmhr
Scorpionchiques:
login: Scorpionchiques
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/15703294?v=4
url: https://github.com/Scorpionchiques
lordqyxz:
login: lordqyxz
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/31722468?u=974553c0ba53526d9be7e9876544283291be3b0d&v=4
url: https://github.com/lordqyxz
heysaeid:
login: heysaeid
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/63112273?u=5397ead391319a147a18b70cc04d1a334f235ef3&v=4
url: https://github.com/heysaeid
Yois4101:
login: Yois4101
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/119609381?v=4
url: https://github.com/Yois4101
tamtam-fitness:
login: tamtam-fitness
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
url: https://github.com/tamtam-fitness
mpmeleshko:
login: mpmeleshko
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/34425664?v=4
url: https://github.com/mpmeleshko
SonnyYou:
login: SonnyYou
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/18657569?v=4
url: https://github.com/SonnyYou
matiasbertani:
login: matiasbertani
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/65260383?u=d5edd86a6e2ab4fb1aab7751931fe045a963afd7&v=4
url: https://github.com/matiasbertani
thiennc254:
login: thiennc254
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/97406628?u=1b2860679694b9a552764d0fa81dbd7a016322ec&v=4
url: https://github.com/thiennc254
javillegasna:
login: javillegasna
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/38879192?u=df9ab0d628f8c1f1c849db7b3c0939337f42c3f1&v=4
url: https://github.com/javillegasna
9zimin9:
login: 9zimin9
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4
url: https://github.com/9zimin9
ilhamfadillah:
login: ilhamfadillah
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/20577838?u=c56192cf99b55affcaad408b240259c62e633450&v=4
url: https://github.com/ilhamfadillah
gerry-sabar:
login: gerry-sabar
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
url: https://github.com/gerry-sabar
cookie-byte217:
login: cookie-byte217
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/57880178?v=4
url: https://github.com/cookie-byte217
AbolfazlKameli:
login: AbolfazlKameli
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/120686133?u=af8f025278cce0d489007071254e4055df60b78c&v=4
url: https://github.com/AbolfazlKameli
SBillion:
login: SBillion
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1070649?u=3ab493dfc88b39da0eb1600e3b8e7df1c90a5dee&v=4
url: https://github.com/SBillion
seuthootDev:
login: seuthootDev
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/175179350?u=7c2cbc48ab43b52e0c86592111d92e013d72ea4d&v=4
url: https://github.com/seuthootDev
tyronedamasceno:
login: tyronedamasceno
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/12273721?u=913bca6bab96d9416ad8c9874c80de0833782050&v=4
url: https://github.com/tyronedamasceno
LikoIlya:
login: LikoIlya
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/15039930?v=4
url: https://github.com/LikoIlya
ss-o-furda:
login: ss-o-furda
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/56111536?u=d2326baa464a3778c280ed85fd14c00f87eb1080&v=4
url: https://github.com/ss-o-furda
Frans06:
login: Frans06
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/5842109?u=77529d5517ae80438249b1a45f2d59372a31a212&v=4
url: https://github.com/Frans06
Jefidev:
login: Jefidev
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/9964497?u=1da6eee587a8b425ca4afbfdfc6c3a639fe85d02&v=4
url: https://github.com/Jefidev
Xaraxx:
login: Xaraxx
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/29824698?u=dde2e233e22bb5ca1f8bb0c6e353ccd0d06e6066&v=4
url: https://github.com/Xaraxx
Suyoung789:
login: Suyoung789
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/31277231?u=1591aaf651eb860017231a36590050e154c026b6&v=4
url: https://github.com/Suyoung789
akagaeng:
login: akagaeng
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/17076841?u=9ada2eb6a33dc705ba96d58f802c787dea3859b8&v=4
url: https://github.com/akagaeng
phamquanganh31101998:
login: phamquanganh31101998
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/43257497?u=6b3419ea9e318c356c42a973fb947682590bd8d3&v=4
url: https://github.com/phamquanganh31101998
peebbv6364:
login: peebbv6364
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/26784747?u=3bf07017eb4f4fa3639ba8d4ed19980a34bf8f90&v=4
url: https://github.com/peebbv6364
mrparalon:
login: mrparalon
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/19637629?u=6339508ceb665717cae862a4d33816ac874cbb8f&v=4
url: https://github.com/mrparalon
creyD:
login: creyD
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/15138480?u=51cd2873cd93807beb578af8e23975856fdbc945&v=4
url: https://github.com/creyD
zhoonit:
login: zhoonit
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/17230883?u=698cb26dcce4770374b592aad3b7489e91c07fc6&v=4
url: https://github.com/zhoonit
Sefank:
login: Sefank
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/12670778?u=ca16995c68a82cabc7435c54ac0564930f62dd59&v=4
url: https://github.com/Sefank
RuslanTer:
login: RuslanTer
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/48125303?v=4
url: https://github.com/RuslanTer
FedorGN:
login: FedorGN
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/66411909?u=22382380e7d66ee57ffbfc2ae6bd5efd0cdb672e&v=4
url: https://github.com/FedorGN
rafsaf:
login: rafsaf
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
url: https://github.com/rafsaf
frnsimoes:
login: frnsimoes
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/66239468?u=bd788dabd4d9321455db8b8751c1a2676783f50f&v=4
url: https://github.com/frnsimoes
lieryan:
login: lieryan
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1006989?v=4
url: https://github.com/lieryan
ValeryVal:
login: ValeryVal
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/85856176?v=4
url: https://github.com/ValeryVal
chesstrian:
login: chesstrian
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/3923412?u=8ea9bea6cfb5e6c64dc81be65ac2a9aaf23c5d47&v=4
url: https://github.com/chesstrian
PabloEmidio:
login: PabloEmidio
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/69937719?u=f4d04cb78da68bb93a641f0b793ff665162e712a&v=4
url: https://github.com/PabloEmidio
PraveenNanda124:
login: PraveenNanda124
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/116082827?u=b40c4f23c191692e88f676dc3bf33fc7f315edd4&v=4
url: https://github.com/PraveenNanda124
guites:
login: guites
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/71985299?u=5dab5eb82b0a67fe709fc893f47a423df4de5d46&v=4
url: https://github.com/guites
Junhyung21:
login: Junhyung21
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/138214497?u=66377988eaad4f57004decb183f396560407a73f&v=4
url: https://github.com/Junhyung21
rinaatt:
login: rinaatt
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/6111202?u=9f62ebd2a72879db54d0b51c07c1d1e7203a4813&v=4
url: https://github.com/rinaatt
Slijeff:
login: Slijeff
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/31459252?u=083776331690bbcf427766071e33ac28bb8d271d&v=4
url: https://github.com/Slijeff
GeorchW:
login: GeorchW
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/8687777?u=ae4160f1d88f32692760003f3be9b5fc40a6e00d&v=4
url: https://github.com/GeorchW
Vlad0395:
login: Vlad0395
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/37487589?u=57dc6660b9904cc0bc59b73569bbfb1ac871a4a1&v=4
url: https://github.com/Vlad0395
bisibuka:
login: bisibuka
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/221887?v=4
url: https://github.com/bisibuka
aimasheraz1:
login: aimasheraz1
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/132935019?v=4
url: https://github.com/aimasheraz1
whysage:
login: whysage
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/67018871?u=a05d63a1b315dcf56a4c0dda3c0ca84ce3d6c87f&v=4
url: https://github.com/whysage
Chake9928:
login: Chake9928
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/62596047?u=7aa2c0aad46911934ce3d22f83a895d05fa54e09&v=4
url: https://github.com/Chake9928
qaerial:
login: qaerial
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/41595550?v=4
url: https://github.com/qaerial
bluefish6:
login: bluefish6
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/3324881?u=d107f6d0017927191644829fb845a8ceb8ac20ee&v=4
url: https://github.com/bluefish6
Sion99:
login: Sion99
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/82511301?v=4
url: https://github.com/Sion99
nymous:
login: nymous
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
url: https://github.com/nymous
EpsilonRationes:
login: EpsilonRationes
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/148639079?u=5dd6c4a3f570dea44d208465fd10b709bcdfa69a&v=4
url: https://github.com/EpsilonRationes
SametEmin:
login: SametEmin
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/115692383?u=bda9052f698e50b0df6657fb9436d07e8496fe2f&v=4
url: https://github.com/SametEmin
fhabers21:
login: fhabers21
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/58401847?v=4
url: https://github.com/fhabers21
kohiry:
login: kohiry
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/57669492?u=f6ab0a062740261e882879269a41a47788c84043&v=4
url: https://github.com/kohiry
ptt3199:
login: ptt3199
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/51350651?u=2c3d947a80283e32bf616d4c3af139a6be69680f&v=4
url: https://github.com/ptt3199
arynoot:
login: arynoot
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/73756088?v=4
url: https://github.com/arynoot
GDemay:
login: GDemay
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/7033942?u=bbdcb4e2a67df4ec9caa2440362d8cebc44d65e8&v=4
url: https://github.com/GDemay
maxscheijen:
login: maxscheijen
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/47034840?v=4
url: https://github.com/maxscheijen
celestywang:
login: celestywang
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/184830753?v=4
url: https://github.com/celestywang
RyaWcksn:
login: RyaWcksn
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/42831964?u=0cb4265faf3e3425a89e59b6fddd3eb2de180af0&v=4
url: https://github.com/RyaWcksn
tienduong-21:
login: tienduong-21
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/80129618?v=4
url: https://github.com/tienduong-21
zbellos:
login: zbellos
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/204500646?v=4
url: https://github.com/zbellos
Mohammad222PR:
login: Mohammad222PR
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/116789737?u=25810a5fe049d2f1618e2e7417cea011cc353ce4&v=4
url: https://github.com/Mohammad222PR
blaisep:
login: blaisep
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/254456?u=97d584b7c0a6faf583aa59975df4f993f671d121&v=4
url: https://github.com/blaisep
SirTelemak:
login: SirTelemak
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4
url: https://github.com/SirTelemak
ovezovs:
login: ovezovs
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/44060682?u=9cb4d738b15e64157cb65afbe2e31bd0c8f3f6e6&v=4
url: https://github.com/ovezovs
neatek:
login: neatek
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/3075678?u=3001e778e4aa0bf6d3142d09f0b9d13b2c55066f&v=4
url: https://github.com/neatek
sprytnyk:
login: sprytnyk
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/16718258?u=4893ea96bfebfbdbde8abd9e06851eca12b01bc9&v=4
url: https://github.com/sprytnyk
wfpinedar:
login: wfpinedar
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/5309214?u=4af7b6b3907b015699a9994d0808137dd68f7658&v=4
url: https://github.com/wfpinedar
italopenaforte:
login: italopenaforte
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7786881?u=e64a8f24b1ba95eb82f283be8ab90892e40c5465&v=4
url: https://github.com/italopenaforte
hackerneocom:
login: hackerneocom
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/67042948?u=ca365045bd261cec5a64059aa23cf80065148c3c&v=4
url: https://github.com/hackerneocom
dmas-at-wiris:
login: dmas-at-wiris
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/24917162?u=0df147936a375b4b64232c650de31a227a6b59a0&v=4
url: https://github.com/dmas-at-wiris
TorhamDev:
login: TorhamDev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/87639984?u=07e5429fbd9c5d63c5ca55a0f31ef541216f0ce6&v=4
url: https://github.com/TorhamDev
jaystone776:
login: jaystone776
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
url: https://github.com/jaystone776
AaronDewes:
login: AaronDewes
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/67546953?v=4
url: https://github.com/AaronDewes
kunansy:
login: kunansy
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20476946?u=d8321cd00787d5ee29bfdd8ff6fde23ad783a581&v=4
url: https://github.com/kunansy
TimorChow:
login: TimorChow
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/18365403?u=bcbb357be0a447bc682a161932eab5032cede4af&v=4
url: https://github.com/TimorChow
ataberkciftlikli:
login: ataberkciftlikli
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/64265169?u=ca7c1348242559f70bc1dc027a4be277c464676f&v=4
url: https://github.com/ataberkciftlikli
leandrodesouzadev:
login: leandrodesouzadev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/85115541?u=4eb25f43f1fe23727d61e986cf83b73b86e2a95a&v=4
url: https://github.com/leandrodesouzadev
dutkiewicz:
login: dutkiewicz
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6649846?u=e941be6e1ab2ffdf41cea227a73f0ffbef20628f&v=4
url: https://github.com/dutkiewicz
mirusu400:
login: mirusu400
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/25397908?u=deda776115e4ee6f76fa526bb5127bd1a6c4b231&v=4
url: https://github.com/mirusu400
its0x08:
login: its0x08
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/15280042?u=d7c2058f29d4e8fbdae09b194e04c5e410350211&v=4
url: https://github.com/its0x08
linsein:
login: linsein
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/23748021?u=4db169ce262b69aa7292f82b785436544f69fb88&v=4
url: https://github.com/linsein
0xflotus:
login: 0xflotus
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/26602940?u=3c52ce6393bb547c97e6380ccdee03e0c64152c6&v=4
url: https://github.com/0xflotus
jonatasoli:
login: jonatasoli
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=f601c3f111f2148bd9244c2cb3ebbd57b592e674&v=4
url: https://github.com/jonatasoli
tyzh-dev:
login: tyzh-dev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51972581?u=ba3882da7c009918a8e2d6b9ead31c89f09c922d&v=4
url: https://github.com/tyzh-dev
yurkevich-dev:
login: yurkevich-dev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/45145188?u=db2de8c186073d95693279dcf085fcebffab57d0&v=4
url: https://github.com/yurkevich-dev
emp7yhead:
login: emp7yhead
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20521260?u=9494c74cb9e1601d734b1f2726e292e257777d98&v=4
url: https://github.com/emp7yhead
BartoszCki:
login: BartoszCki
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/17833351?u=40025e1182c32a9664834baec268dadad127703d&v=4
url: https://github.com/BartoszCki
hakancelikdev:
login: hakancelikdev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/19157033?u=095ea8e0af1de642edd92e5f806c70359e00c977&v=4
url: https://github.com/hakancelikdev
KaterinaSolovyeva:
login: KaterinaSolovyeva
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/85114725?u=1fe81463cb6b1fd01ac047172fa4895e2a3cecaa&v=4
url: https://github.com/KaterinaSolovyeva
zhanymkanov:
login: zhanymkanov
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/22341602?u=aa1c47285a4f5692d165ccb2a441c5553f23ef83&v=4
url: https://github.com/zhanymkanov
felipebpl:
login: felipebpl
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62957465?u=3c05f0f358b9575503c03122daefb115b6ac1414&v=4
url: https://github.com/felipebpl
iudeen:
login: iudeen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=f09cdd745e5bf16138f29b42732dd57c7f02bee1&v=4
url: https://github.com/iudeen
dwisulfahnur:
login: dwisulfahnur
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/12431528?v=4
url: https://github.com/dwisulfahnur
ayr-ton:
login: ayr-ton
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4
url: https://github.com/ayr-ton
Kadermiyanyedi:
login: Kadermiyanyedi
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/48386782?u=08c0f1594c5baf28b6fab7520a848cb9c3806c8e&v=4
url: https://github.com/Kadermiyanyedi
raphaelauv:
login: raphaelauv
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
url: https://github.com/raphaelauv
Fahad-Md-Kamal:
login: Fahad-Md-Kamal
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34704464?u=0b1da22a9b88b14d99e7e4368eadde7ecd695366&v=4
url: https://github.com/Fahad-Md-Kamal
zxcq544:
login: zxcq544
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/5781268?u=25959ea03803742c3b28220b27fc07923a491dcb&v=4
url: https://github.com/zxcq544
AlexandrMaltsevYDX:
login: AlexandrMaltsevYDX
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/109986802?u=ed275d72bfcdb4d15abdd54e7be026adbb9ca098&v=4
url: https://github.com/AlexandrMaltsevYDX
realFranco:
login: realFranco
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/45880759?u=22fea3007d3e2d4c8c82d6ccfbde71454c4c6dd8&v=4
url: https://github.com/realFranco
piaria:
login: piaria
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/110835535?u=5af3d56254faa05bbca4258a46c5723489480f90&v=4
url: https://github.com/piaria
mojtabapaso:
login: mojtabapaso
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/121169359?u=ced1d5ad673bcd9e949ebf967a4ab50185637443&v=4
url: https://github.com/mojtabapaso
eghbalpoorMH:
login: eghbalpoorMH
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/36267498?v=4
url: https://github.com/eghbalpoorMH
Tiazen:
login: Tiazen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/16170159?u=0ce5e32f76e3f10733c8f25d97db9e31b753838c&v=4
url: https://github.com/Tiazen
jfunez:
login: jfunez
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
url: https://github.com/jfunez
s-rigaud:
login: s-rigaud
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/46346622?u=eee0adaa9fdff9e312d52526fbd4020dd6860c27&v=4
url: https://github.com/s-rigaud
Artem4es:
login: Artem4es
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/110793967?u=0f9d4e80e055adc1aa8b548e951f6b4989fa2e78&v=4
url: https://github.com/Artem4es
sulemanhelp:
login: sulemanhelp
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/22400366?u=3e8e68750655c7f5b2e0ba1d54f5779ee526707d&v=4
url: https://github.com/sulemanhelp
theRealNonso:
login: theRealNonso
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/29557286?u=6f062680edccfeb4c802daf3b1d8b2a9e21ae013&v=4
url: https://github.com/theRealNonso
AhsanSheraz:
login: AhsanSheraz
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51913596?u=b5aa3c3a004cc0112e56c54f6901941836d8c26b&v=4
url: https://github.com/AhsanSheraz
HealerNguyen:
login: HealerNguyen
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/29653304?u=6ab095689054c63b1f4ceb26dd66847450225c87&v=4
url: https://github.com/HealerNguyen
isulim:
login: isulim
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/30448496?u=44c47838defa48a16606b895dce08890fca8482f&v=4
url: https://github.com/isulim
siavashyj:
login: siavashyj
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/43583410?u=562005ddc7901cd27a1219a118a2363817b14977&v=4
url: https://github.com/siavashyj
Ramin-RX7:
login: Ramin-RX7
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/52785580?u=b3678f779ad0ee9cd9dca9e50ccb804b5eb990a5&v=4
url: https://github.com/Ramin-RX7
DevSpace88:
login: DevSpace88
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/102557040?u=103e454f0d4d2d357bd66e9931e57f0bf267526a&v=4
url: https://github.com/DevSpace88
Yum-git:
login: Yum-git
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/56100888?u=7c6ae21af081488b5fb703ab096fb1926025fd50&v=4
url: https://github.com/Yum-git
oubush:
login: oubush
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/7489099?u=c86448bc61f5e7f03a1f14a768beeb09c33899d4&v=4
url: https://github.com/oubush
KAZAMA-DREAM:
login: KAZAMA-DREAM
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/73453137?u=5108c757a3842733a448d9a16cdc65d82899eee1&v=4
url: https://github.com/KAZAMA-DREAM
aprilcoskun:
login: aprilcoskun
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/17393603?u=18177d5bdba3a4567b8664587c882fb734e5fa09&v=4
url: https://github.com/aprilcoskun
zhiquanchi:
login: zhiquanchi
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/29973289?u=744c74bc2635f839235ec32a0a934c5cef9a156d&v=4
url: https://github.com/zhiquanchi
Jamim:
login: Jamim
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/5607572?u=9ce0b6a6d1a5124e28b3c04d8d26827ca328713a&v=4
url: https://github.com/Jamim
alvinkhalil:
login: alvinkhalil
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/84583022?u=ab0eeb9ce6ffe93fd9bb23daf782b9867b864149&v=4
url: https://github.com/alvinkhalil
leylaeminova:
login: leylaeminova
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/100516839?u=0b0dab9e31742076b22812b14a39b4e6d8f6de4a&v=4
url: https://github.com/leylaeminova
UN-9BOT:
login: UN-9BOT
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/111110804?u=39e158937ed795972c2d0400fc521c50e9bfb9e7&v=4
url: https://github.com/UN-9BOT
flasonme:
login: flasonme
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/30571019?v=4
url: https://github.com/flasonme
gustavoprezoto:
login: gustavoprezoto
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62812585?u=2e936a0c6a2f11ecf3a735ebd33386100bcfebf8&v=4
url: https://github.com/gustavoprezoto
johnny630:
login: johnny630
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/2870590?v=4
url: https://github.com/johnny630
JCTrapero:
login: JCTrapero
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/109148166?u=bea607a04058176c4c2ae0d7c2e9ec647ccef002&v=4
url: https://github.com/JCTrapero
ZhibangYue:
login: ZhibangYue
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/93324586?u=20fb23e3718e0364bb217966470d35e0637dd4fe&v=4
url: https://github.com/ZhibangYue
saeye:
login: saeye
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62229734?u=312d619db2588b60d5d5bde65260a2f44fdc6c76&v=4
url: https://github.com/saeye
Heumhub:
login: Heumhub
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/173761521?v=4
url: https://github.com/Heumhub
manumolina:
login: manumolina
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/2404208?u=fdc5502910f8dec814b2477f89587b9e45fac846&v=4
url: https://github.com/manumolina
logan2d5:
login: logan2d5
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4
url: https://github.com/logan2d5
guspan-tanadi:
login: guspan-tanadi
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/36249910?v=4
url: https://github.com/guspan-tanadi
tiaggo16:
login: tiaggo16
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62227573?u=359f4e2c51a4b13c8553ac5af405d635b07bb61f&v=4
url: https://github.com/tiaggo16
kiharito:
login: kiharito
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/38311245?v=4
url: https://github.com/kiharito
t4f1d:
login: t4f1d
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/4054172?u=463d5ce0ec8ad8582f6e9351bb8c9a5105b39bb7&v=4
url: https://github.com/t4f1d
J-Fuji:
login: J-Fuji
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/101452903?v=4
url: https://github.com/J-Fuji
MrL8199:
login: MrL8199
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/39489075?u=3fc4f89c86973e40b5970d838c801bdbc13ac828&v=4
url: https://github.com/MrL8199
ivintoiu:
login: ivintoiu
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1853336?u=e3de5fd0ab17efc12256b4295285b504ca281440&v=4
url: https://github.com/ivintoiu
TechnoService2:
login: TechnoService2
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/142113388?v=4
url: https://github.com/TechnoService2
EgorOnishchuk:
login: EgorOnishchuk
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/120256301?v=4
url: https://github.com/EgorOnishchuk
iamantonreznik:
login: iamantonreznik
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/112612414?u=b9ba8d9b4d3940198bc3a4353dfce70c044a39b1&v=4
url: https://github.com/iamantonreznik
Azazul123:
login: Azazul123
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/102759111?u=b48ce6e30a81a23467cc30e0c011bcc57f0326ab&v=4
url: https://github.com/Azazul123
ykertytsky:
login: ykertytsky
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/83857001?u=1f303eb73189f78cff376c13bd46db65f90ee314&v=4
url: https://github.com/ykertytsky
NavesSapnis:
login: NavesSapnis
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/79222417?u=b5b10291b8e9130ca84fd20f0a641e04ed94b6b1&v=4
url: https://github.com/NavesSapnis
isgin01:
login: isgin01
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/157279130?u=16d6466476cf7dbc55a4cd575b6ea920ebdd81e1&v=4
url: https://github.com/isgin01
syedasamina56:
login: syedasamina56
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/183273097?v=4
url: https://github.com/syedasamina56
================================================
FILE: docs/en/data/translators.yml
================================================
nilslindemann:
login: nilslindemann
count: 130
avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
url: https://github.com/nilslindemann
tiangolo:
login: tiangolo
count: 67
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=cb5d06e73a9e1998141b1641aa88e443c6717651&v=4
url: https://github.com/tiangolo
jaystone776:
login: jaystone776
count: 46
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
url: https://github.com/jaystone776
ceb10n:
login: ceb10n
count: 30
avatarUrl: https://avatars.githubusercontent.com/u/235213?u=edcce471814a1eba9f0cdaa4cd0de18921a940a6&v=4
url: https://github.com/ceb10n
valentinDruzhinin:
login: valentinDruzhinin
count: 29
avatarUrl: https://avatars.githubusercontent.com/u/12831905?u=aae1ebc675c91e8fa582df4fcc4fc4128106344d&v=4
url: https://github.com/valentinDruzhinin
tokusumi:
login: tokusumi
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
url: https://github.com/tokusumi
SwftAlpc:
login: SwftAlpc
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4
url: https://github.com/SwftAlpc
YuriiMotov:
login: YuriiMotov
count: 23
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=bc48be95c429989224786106b027f3c5e40cc354&v=4
url: https://github.com/YuriiMotov
hasansezertasan:
login: hasansezertasan
count: 22
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=d36995e41a00590da64e6204cfd112e0484ac1ca&v=4
url: https://github.com/hasansezertasan
waynerv:
login: waynerv
count: 20
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
url: https://github.com/waynerv
hard-coders:
login: hard-coders
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=78d12d1acdf853c817700145e73de7fd9e5d068b&v=4
url: https://github.com/hard-coders
AlertRED:
login: AlertRED
count: 16
avatarUrl: https://avatars.githubusercontent.com/u/15695000?u=f5a4944c6df443030409c88da7d7fa0b7ead985c&v=4
url: https://github.com/AlertRED
Joao-Pedro-P-Holanda:
login: Joao-Pedro-P-Holanda
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/110267046?u=331bd016326dac4cf3df4848f6db2dbbf8b5f978&v=4
url: https://github.com/Joao-Pedro-P-Holanda
yychanlee:
login: yychanlee
count: 14
avatarUrl: https://avatars.githubusercontent.com/u/103817302?u=3a042740dc0ff58615da0d8679230966fd7693e8&v=4
url: https://github.com/yychanlee
Xewus:
login: Xewus
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
url: https://github.com/Xewus
Zhongheng-Cheng:
login: Zhongheng-Cheng
count: 13
avatarUrl: https://avatars.githubusercontent.com/u/95612344?u=a0f7730a3cc7486827965e01a119ad610bda4b0a&v=4
url: https://github.com/Zhongheng-Cheng
Smlep:
login: Smlep
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/16785985?u=ffe99fa954c8e774ef1117e58d34aece92051e27&v=4
url: https://github.com/Smlep
marcelomarkus:
login: marcelomarkus
count: 11
avatarUrl: https://avatars.githubusercontent.com/u/20115018?u=dda090ce9160ef0cd2ff69b1e5ea741283425cba&v=4
url: https://github.com/marcelomarkus
KaniKim:
login: KaniKim
count: 10
avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=296dbdd490e0eb96e3d45a2608c065603b17dc31&v=4
url: https://github.com/KaniKim
Vincy1230:
login: Vincy1230
count: 9
avatarUrl: https://avatars.githubusercontent.com/u/81342412?u=ab5e256a4077a4a91f3f9cd2115ba80780454cbe&v=4
url: https://github.com/Vincy1230
rjNemo:
login: rjNemo
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
url: https://github.com/rjNemo
xzmeng:
login: xzmeng
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/40202897?v=4
url: https://github.com/xzmeng
pablocm83:
login: pablocm83
count: 8
avatarUrl: https://avatars.githubusercontent.com/u/28315068?u=3310fbb05bb8bfc50d2c48b6cb64ac9ee4a14549&v=4
url: https://github.com/pablocm83
ptt3199:
login: ptt3199
count: 7
avatarUrl: https://avatars.githubusercontent.com/u/51350651?u=2c3d947a80283e32bf616d4c3af139a6be69680f&v=4
url: https://github.com/ptt3199
NinaHwang:
login: NinaHwang
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=241f2cb6d38a2d379536608a8ea5a22ed4b1a3ea&v=4
url: https://github.com/NinaHwang
batlopes:
login: batlopes
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
url: https://github.com/batlopes
lucasbalieiro:
login: lucasbalieiro
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/37416577?u=d144221c34c08adac8b20e1833d776ffa1c4b1d0&v=4
url: https://github.com/lucasbalieiro
Alexandrhub:
login: Alexandrhub
count: 6
avatarUrl: https://avatars.githubusercontent.com/u/119126536?u=9fc0d48f3307817bafecc5861eb2168401a6cb04&v=4
url: https://github.com/Alexandrhub
Serrones:
login: Serrones
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4
url: https://github.com/Serrones
RunningIkkyu:
login: RunningIkkyu
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31848542?u=494ecc298e3f26197495bb357ad0f57cfd5f7a32&v=4
url: https://github.com/RunningIkkyu
Attsun1031:
login: Attsun1031
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/1175560?v=4
url: https://github.com/Attsun1031
rostik1410:
login: rostik1410
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/11443899?u=e26a635c2ba220467b308a326a579b8ccf4a8701&v=4
url: https://github.com/rostik1410
alv2017:
login: alv2017
count: 5
avatarUrl: https://avatars.githubusercontent.com/u/31544722?v=4
url: https://github.com/alv2017
komtaki:
login: komtaki
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4
url: https://github.com/komtaki
JulianMaurin:
login: JulianMaurin
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/63545168?u=b7d15ac865268cbefc2d739e2f23d9aeeac1a622&v=4
url: https://github.com/JulianMaurin
stlucasgarcia:
login: stlucasgarcia
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=c22d8850e9dc396a8820766a59837f967e14f9a0&v=4
url: https://github.com/stlucasgarcia
ComicShrimp:
login: ComicShrimp
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=d2fbf412e7730183ce91686ca48d4147e1b7dc74&v=4
url: https://github.com/ComicShrimp
BilalAlpaslan:
login: BilalAlpaslan
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4
url: https://github.com/BilalAlpaslan
axel584:
login: axel584
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/1334088?u=9667041f5b15dc002b6f9665fda8c0412933ac04&v=4
url: https://github.com/axel584
tamtam-fitness:
login: tamtam-fitness
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/62091034?u=8da19a6bd3d02f5d6ba30c7247d5b46c98dd1403&v=4
url: https://github.com/tamtam-fitness
Limsunoh:
login: Limsunoh
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/90311848?u=f456e0c5709fd50c8cd2898b551558eda14e5f21&v=4
url: https://github.com/Limsunoh
kwang1215:
login: kwang1215
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/74170199?u=2a63ff6692119dde3f5e5693365b9fcd6f977b08&v=4
url: https://github.com/kwang1215
k94-ishi:
login: k94-ishi
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/32672580?u=bc7c5c07af0656be9fe4f1784a444af8d81ded89&v=4
url: https://github.com/k94-ishi
Mohammad222PR:
login: Mohammad222PR
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/116789737?u=25810a5fe049d2f1618e2e7417cea011cc353ce4&v=4
url: https://github.com/Mohammad222PR
NavesSapnis:
login: NavesSapnis
count: 4
avatarUrl: https://avatars.githubusercontent.com/u/79222417?u=b5b10291b8e9130ca84fd20f0a641e04ed94b6b1&v=4
url: https://github.com/NavesSapnis
jfunez:
login: jfunez
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/805749?v=4
url: https://github.com/jfunez
ycd:
login: ycd
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=f1e7bae394a315da950912c92dc861a8eaf95d4c&v=4
url: https://github.com/ycd
mariacamilagl:
login: mariacamilagl
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
url: https://github.com/mariacamilagl
maoyibo:
login: maoyibo
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/7887703?v=4
url: https://github.com/maoyibo
blt232018:
login: blt232018
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/43393471?u=172b0e0391db1aa6c1706498d6dfcb003c8a4857&v=4
url: https://github.com/blt232018
magiskboy:
login: magiskboy
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/13352088?u=18b6d672523f9e9d98401f31dd50e28bb27d826f&v=4
url: https://github.com/magiskboy
luccasmmg:
login: luccasmmg
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/11317382?u=65099a5a0d492b89119471f8a7014637cc2e04da&v=4
url: https://github.com/luccasmmg
lbmendes:
login: lbmendes
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/80999926?u=646619e2f07ac5a7c3f65fe7834197461a4fff9f&v=4
url: https://github.com/lbmendes
Zssaer:
login: Zssaer
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/45691504?u=4c0c195f25cb5ac6af32acfb0ab35427682938d2&v=4
url: https://github.com/Zssaer
ChuyuChoyeon:
login: ChuyuChoyeon
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/129537877?u=f0c76f3327817a8b86b422d62e04a34bf2827f2b&v=4
url: https://github.com/ChuyuChoyeon
ivan-abc:
login: ivan-abc
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/36765187?u=c6e0ba571c1ccb6db9d94e62e4b8b5eda811a870&v=4
url: https://github.com/ivan-abc
mojtabapaso:
login: mojtabapaso
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/121169359?u=ced1d5ad673bcd9e949ebf967a4ab50185637443&v=4
url: https://github.com/mojtabapaso
hsuanchi:
login: hsuanchi
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/24913710?u=7d25a398e478b6e63503bf6f26c54efa9e0da07b&v=4
url: https://github.com/hsuanchi
alejsdev:
login: alejsdev
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=0facffe3abf87f57a1f05fa773d1119cc5c2f6a5&v=4
url: https://github.com/alejsdev
riroan:
login: riroan
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/33053284?u=2d18e3771506ee874b66d6aa2b3b1107fd95c38f&v=4
url: https://github.com/riroan
nayeonkinn:
login: nayeonkinn
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/98254573?u=64a75ac99b320d4935eff8d1fceea9680fa07473&v=4
url: https://github.com/nayeonkinn
pe-brian:
login: pe-brian
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1783138?u=7e6242eb9e85bcf673fa88bbac9dd6dc3f03b1b5&v=4
url: https://github.com/pe-brian
maxscheijen:
login: maxscheijen
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/47034840?v=4
url: https://github.com/maxscheijen
ilacftemp:
login: ilacftemp
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/159066669?v=4
url: https://github.com/ilacftemp
devluisrodrigues:
login: devluisrodrigues
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/103431660?u=d9674a3249edc4601d2c712cdebf899918503c3a&v=4
url: https://github.com/devluisrodrigues
devfernandoa:
login: devfernandoa
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/28360583?u=c4308abd62e8847c9e572e1bb9fe6b9dc9ef8e50&v=4
url: https://github.com/devfernandoa
kim-sangah:
login: kim-sangah
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/173775778?v=4
url: https://github.com/kim-sangah
9zimin9:
login: 9zimin9
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/174453744?v=4
url: https://github.com/9zimin9
nahyunkeem:
login: nahyunkeem
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/174440096?u=e12401d492eee58570f8914d0872b52e421a776e&v=4
url: https://github.com/nahyunkeem
timothy-jeong:
login: timothy-jeong
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/53824764?u=db3d0cea2f5fab64d810113c5039a369699a2774&v=4
url: https://github.com/timothy-jeong
gerry-sabar:
login: gerry-sabar
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/1120123?v=4
url: https://github.com/gerry-sabar
Rishat-F:
login: Rishat-F
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/66554797?v=4
url: https://github.com/Rishat-F
ruzia:
login: ruzia
count: 3
avatarUrl: https://avatars.githubusercontent.com/u/24503?u=abce66d26c9611818720f11e6ae6773a6e0928f8&v=4
url: https://github.com/ruzia
izaguerreiro:
login: izaguerreiro
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4
url: https://github.com/izaguerreiro
Xaraxx:
login: Xaraxx
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/29824698?u=dde2e233e22bb5ca1f8bb0c6e353ccd0d06e6066&v=4
url: https://github.com/Xaraxx
sh0nk:
login: sh0nk
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
url: https://github.com/sh0nk
dukkee:
login: dukkee
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/36825394?u=ccfd86e6a4f2d093dad6f7544cc875af67fa2df8&v=4
url: https://github.com/dukkee
oandersonmagalhaes:
login: oandersonmagalhaes
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/83456692?u=daf5f302a59b950efc6d21129314af207e35441f&v=4
url: https://github.com/oandersonmagalhaes
leandrodesouzadev:
login: leandrodesouzadev
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/85115541?u=4eb25f43f1fe23727d61e986cf83b73b86e2a95a&v=4
url: https://github.com/leandrodesouzadev
kty4119:
login: kty4119
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/49435654?v=4
url: https://github.com/kty4119
ASpathfinder:
login: ASpathfinder
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/31813636?u=2090bd1b7abb65cfeff0c618f99f11afa82c0548&v=4
url: https://github.com/ASpathfinder
jujumilk3:
login: jujumilk3
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/41659814?u=538f7dfef03b59f25e43f10d59a31c19ef538a0c&v=4
url: https://github.com/jujumilk3
ayr-ton:
login: ayr-ton
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1090517?u=5cf70a0e0f0dbf084e074e494aa94d7c91a46ba6&v=4
url: https://github.com/ayr-ton
Kadermiyanyedi:
login: Kadermiyanyedi
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/48386782?u=08c0f1594c5baf28b6fab7520a848cb9c3806c8e&v=4
url: https://github.com/Kadermiyanyedi
KdHyeon0661:
login: KdHyeon0661
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20253352?u=5ae1aae34b091a39f22cbe60a02b79dcbdbea031&v=4
url: https://github.com/KdHyeon0661
LorhanSohaky:
login: LorhanSohaky
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
url: https://github.com/LorhanSohaky
cfraboulet:
login: cfraboulet
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62244267?u=ed0e286ba48fa1dafd64a08e50f3364b8e12df34&v=4
url: https://github.com/cfraboulet
dedkot01:
login: dedkot01
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/26196675?u=e2966887124e67932853df4f10f86cb526edc7b0&v=4
url: https://github.com/dedkot01
AGolicyn:
login: AGolicyn
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/86262613?u=3c21606ab8d210a061a1673decff1e7d5592b380&v=4
url: https://github.com/AGolicyn
fhabers21:
login: fhabers21
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/58401847?v=4
url: https://github.com/fhabers21
TabarakoAkula:
login: TabarakoAkula
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/113298631?u=add801e370dbc502cd94ce6d3484760d7fef5406&v=4
url: https://github.com/TabarakoAkula
AhsanSheraz:
login: AhsanSheraz
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/51913596?u=b5aa3c3a004cc0112e56c54f6901941836d8c26b&v=4
url: https://github.com/AhsanSheraz
ArtemKhymenko:
login: ArtemKhymenko
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/14346625?u=f2fa553d9e5ec5e0f05d66bd649f7be347169631&v=4
url: https://github.com/ArtemKhymenko
hasnatsajid:
login: hasnatsajid
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/86589885?u=3712c0362d7a4000d76022339c545cf46aa5903f&v=4
url: https://github.com/hasnatsajid
alperiox:
login: alperiox
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
url: https://github.com/alperiox
emrhnsyts:
login: emrhnsyts
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/42899027?u=ad26798e3f8feed2041c5dd5f87e58933d6c3283&v=4
url: https://github.com/emrhnsyts
vusallyv:
login: vusallyv
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/85983771?u=620ce103dcdc47953c952bb8d402a9cf8199014d&v=4
url: https://github.com/vusallyv
jackleeio:
login: jackleeio
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/20477587?u=c5184dab6d021733d10c8f975b20e391856303d6&v=4
url: https://github.com/jackleeio
choi-haram:
login: choi-haram
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62204475?v=4
url: https://github.com/choi-haram
imtiaz101325:
login: imtiaz101325
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/54007087?u=61e79c4c39798cd4d339788045dc44d4c6252bde&v=4
url: https://github.com/imtiaz101325
fabianfalon:
login: fabianfalon
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/3700760?u=95f69e31280b17ac22299cdcd345323b142fe0af&v=4
url: https://github.com/fabianfalon
waketzheng:
login: waketzheng
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/35413830?u=df19e4fd5bb928e7d086e053ef26a46aad23bf84&v=4
url: https://github.com/waketzheng
billzhong:
login: billzhong
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/1644011?v=4
url: https://github.com/billzhong
chaoless:
login: chaoless
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/64477804?v=4
url: https://github.com/chaoless
logan2d5:
login: logan2d5
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/146642263?u=dbd6621f8b0330d6919f6a7131277b92e26fbe87&v=4
url: https://github.com/logan2d5
andersonrocha0:
login: andersonrocha0
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/22346169?u=93a1359c8c5461d894802c0cc65bcd09217e7a02&v=4
url: https://github.com/andersonrocha0
saeye:
login: saeye
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62229734?u=312d619db2588b60d5d5bde65260a2f44fdc6c76&v=4
url: https://github.com/saeye
11kkw:
login: 11kkw
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/21125286?v=4
url: https://github.com/11kkw
yes0ng:
login: yes0ng
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/25501794?u=3aed18b0d491e0220a167a1e9e58bea3638c6707&v=4
url: https://github.com/yes0ng
EgorOnishchuk:
login: EgorOnishchuk
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/120256301?v=4
url: https://github.com/EgorOnishchuk
EdmilsonRodrigues:
login: EdmilsonRodrigues
count: 2
avatarUrl: https://avatars.githubusercontent.com/u/62777025?u=217d6f3cd6cc750bb8818a3af7726c8d74eb7c2d&v=4
url: https://github.com/EdmilsonRodrigues
================================================
FILE: docs/en/docs/_llm-test.md
================================================
# LLM test file { #llm-test-file }
This document tests if the LLM, which translates the documentation, understands the `general_prompt` in `scripts/translate.py` and the language specific prompt in `docs/{language code}/llm-prompt.md`. The language specific prompt is appended to `general_prompt`.
Tests added here will be seen by all designers of language specific prompts.
Use as follows:
* Have a language specific prompt - `docs/{language code}/llm-prompt.md`.
* Do a fresh translation of this document into your desired target language (see e.g. the `translate-page` command of the `translate.py`). This will create the translation under `docs/{language code}/docs/_llm-test.md`.
* Check if things are okay in the translation.
* If necessary, improve your language specific prompt, the general prompt, or the English document.
* Then manually fix the remaining issues in the translation, so that it is a good translation.
* Retranslate, having the good translation in place. The ideal result would be that the LLM makes no changes anymore to the translation. That means that the general prompt and your language specific prompt are as good as they can be (It will sometimes make a few seemingly random changes, the reason is that [LLMs are not deterministic algorithms](https://doublespeak.chat/#/handbook#deterministic-output)).
The tests:
## Code snippets { #code-snippets }
//// tab | Test
This is a code snippet: `foo`. And this is another code snippet: `bar`. And another one: `baz quux`.
////
//// tab | Info
Content of code snippets should be left as is.
See section `### Content of code snippets` in the general prompt in `scripts/translate.py`.
////
## Quotes { #quotes }
//// tab | Test
Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'".
/// note
The LLM will probably translate this wrong. Interesting is only if it keeps the fixed translation when retranslating.
///
////
//// tab | Info
The prompt designer may choose if they want to convert neutral quotes to typographic quotes. It is okay to leave them as is.
See for example section `### Quotes` in `docs/de/llm-prompt.md`.
////
## Quotes in code snippets { #quotes-in-code-snippets }
//// tab | Test
`pip install "foo[bar]"`
Examples for string literals in code snippets: `"this"`, `'that'`.
A difficult example for string literals in code snippets: `f"I like {'oranges' if orange else "apples"}"`
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Info
... However, quotes inside code snippets must stay as is.
////
## code blocks { #code-blocks }
//// tab | Test
A Bash code example...
```bash
# Print a greeting to the universe
echo "Hello universe"
```
...and a console code example...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...and another console code example...
```console
// Create a directory "Code"
$ mkdir code
// Switch into that directory
$ cd code
```
...and a Python code example...
```Python
wont_work() # This won't work 😱
works(foo="bar") # This works 🎉
```
...and that's it.
////
//// tab | Info
Code in code blocks should not be modified, with the exception of comments.
See section `### Content of code blocks` in the general prompt in `scripts/translate.py`.
////
## Tabs and colored boxes { #tabs-and-colored-boxes }
//// tab | Test
/// info
Some text
///
/// note
Some text
///
/// note | Technical details
Some text
///
/// check
Some text
///
/// tip
Some text
///
/// warning
Some text
///
/// danger
Some text
///
////
//// tab | Info
Tabs and `Info`/`Note`/`Warning`/etc. blocks should have the translation of their title added after a vertical bar (`|`).
See sections `### Special blocks` and `### Tab blocks` in the general prompt in `scripts/translate.py`.
////
## Web- and internal links { #web-and-internal-links }
//// tab | Test
The link text should get translated, the link address should remain unchanged:
* [Link to heading above](#code-snippets)
* [Internal link](index.md#installation)
* [External link](https://sqlmodel.tiangolo.com/)
* [Link to a style](https://fastapi.tiangolo.com/css/styles.css)
* [Link to a script](https://fastapi.tiangolo.com/js/logic.js)
* [Link to an image](https://fastapi.tiangolo.com/img/foo.jpg)
The link text should get translated, the link address should point to the translation:
* [FastAPI link](https://fastapi.tiangolo.com/)
////
//// tab | Info
Links should be translated, but their address shall remain unchanged. An exception are absolute links to pages of the FastAPI documentation. In that case it should link to the translation.
See section `### Links` in the general prompt in `scripts/translate.py`.
////
## HTML "abbr" elements { #html-abbr-elements }
//// tab | Test
Here some things wrapped in HTML "abbr" elements (Some are invented):
### The abbr gives a full phrase { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### The abbr gives a full phrase and an explanation { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Info
"title" attributes of "abbr" elements are translated following some specific instructions.
Translations can add their own "abbr" elements which the LLM should not remove. E.g. to explain English words.
See section `### HTML abbr elements` in the general prompt in `scripts/translate.py`.
////
## HTML "dfn" elements { #html-dfn-elements }
* cluster
* Deep Learning
## Headings { #headings }
//// tab | Test
### Develop a webapp - a tutorial { #develop-a-webapp-a-tutorial }
Hello.
### Type hints and -annotations { #type-hints-and-annotations }
Hello again.
### Super- and subclasses { #super-and-subclasses }
Hello again.
////
//// tab | Info
The only hard rule for headings is that the LLM leaves the hash part inside curly brackets unchanged, which ensures that links do not break.
See section `### Headings` in the general prompt in `scripts/translate.py`.
For some language specific instructions, see e.g. section `### Headings` in `docs/de/llm-prompt.md`.
////
## Terms used in the docs { #terms-used-in-the-docs }
//// tab | Test
* you
* your
* e.g.
* etc.
* `foo` as an `int`
* `bar` as a `str`
* `baz` as a `list`
* the Tutorial - User guide
* the Advanced User Guide
* the SQLModel docs
* the API docs
* the automatic docs
* Data Science
* Deep Learning
* Machine Learning
* Dependency Injection
* HTTP Basic authentication
* HTTP Digest
* ISO format
* the JSON Schema standard
* the JSON schema
* the schema definition
* Password Flow
* Mobile
* deprecated
* designed
* invalid
* on the fly
* standard
* default
* case-sensitive
* case-insensitive
* to serve the application
* to serve the page
* the app
* the application
* the request
* the response
* the error response
* the path operation
* the path operation decorator
* the path operation function
* the body
* the request body
* the response body
* the JSON body
* the form body
* the file body
* the function body
* the parameter
* the body parameter
* the path parameter
* the query parameter
* the cookie parameter
* the header parameter
* the form parameter
* the function parameter
* the event
* the startup event
* the startup of the server
* the shutdown event
* the lifespan event
* the handler
* the event handler
* the exception handler
* to handle
* the model
* the Pydantic model
* the data model
* the database model
* the form model
* the model object
* the class
* the base class
* the parent class
* the subclass
* the child class
* the sibling class
* the class method
* the header
* the headers
* the authorization header
* the `Authorization` header
* the forwarded header
* the dependency injection system
* the dependency
* the dependable
* the dependant
* I/O bound
* CPU bound
* concurrency
* parallelism
* multiprocessing
* the env var
* the environment variable
* the `PATH`
* the `PATH` variable
* the authentication
* the authentication provider
* the authorization
* the authorization form
* the authorization provider
* the user authenticates
* the system authenticates the user
* the CLI
* the command line interface
* the server
* the client
* the cloud provider
* the cloud service
* the development
* the development stages
* the dict
* the dictionary
* the enumeration
* the enum
* the enum member
* the encoder
* the decoder
* to encode
* to decode
* the exception
* to raise
* the expression
* the statement
* the frontend
* the backend
* the GitHub discussion
* the GitHub issue
* the performance
* the performance optimization
* the return type
* the return value
* the security
* the security scheme
* the task
* the background task
* the task function
* the template
* the template engine
* the type annotation
* the type hint
* the server worker
* the Uvicorn worker
* the Gunicorn Worker
* the worker process
* the worker class
* the workload
* the deployment
* to deploy
* the SDK
* the software development kit
* the `APIRouter`
* the `requirements.txt`
* the Bearer Token
* the breaking change
* the bug
* the button
* the callable
* the code
* the commit
* the context manager
* the coroutine
* the database session
* the disk
* the domain
* the engine
* the fake X
* the HTTP GET method
* the item
* the library
* the lifespan
* the lock
* the middleware
* the mobile application
* the module
* the mounting
* the network
* the origin
* the override
* the payload
* the processor
* the property
* the proxy
* the pull request
* the query
* the RAM
* the remote machine
* the status code
* the string
* the tag
* the web framework
* the wildcard
* to return
* to validate
////
//// tab | Info
This is a not complete and not normative list of (mostly) technical terms seen in the docs. It may be helpful for the prompt designer to figure out for which terms the LLM needs a helping hand. For example when it keeps reverting a good translation to a suboptimal translation. Or when it has problems conjugating/declinating a term in your language.
See e.g. section `### List of English terms and their preferred German translations` in `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/en/docs/about/index.md
================================================
# About { #about }
About FastAPI, its design, inspiration and more. 🤓
================================================
FILE: docs/en/docs/advanced/additional-responses.md
================================================
# Additional Responses in OpenAPI { #additional-responses-in-openapi }
/// warning
This is a rather advanced topic.
If you are starting with **FastAPI**, you might not need this.
///
You can declare additional responses, with additional status codes, media types, descriptions, etc.
Those additional responses will be included in the OpenAPI schema, so they will also appear in the API docs.
But for those additional responses you have to make sure you return a `Response` like `JSONResponse` directly, with your status code and content.
## Additional Response with `model` { #additional-response-with-model }
You can pass to your *path operation decorators* a parameter `responses`.
It receives a `dict`: the keys are status codes for each response (like `200`), and the values are other `dict`s with the information for each of them.
Each of those response `dict`s can have a key `model`, containing a Pydantic model, just like `response_model`.
**FastAPI** will take that model, generate its JSON Schema and include it in the correct place in OpenAPI.
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note
Keep in mind that you have to return the `JSONResponse` directly.
///
/// info
The `model` key is not part of OpenAPI.
**FastAPI** will take the Pydantic model from there, generate the JSON Schema, and put it in the correct place.
The correct place is:
* In the key `content`, that has as value another JSON object (`dict`) that contains:
* A key with the media type, e.g. `application/json`, that contains as value another JSON object, that contains:
* A key `schema`, that has as the value the JSON Schema from the model, here's the correct place.
* **FastAPI** adds a reference here to the global JSON Schemas in another place in your OpenAPI instead of including it directly. This way, other applications and clients can use those JSON Schemas directly, provide better code generation tools, etc.
///
The generated responses in the OpenAPI for this *path operation* will be:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
The schemas are referenced to another place inside the OpenAPI schema:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Additional media types for the main response { #additional-media-types-for-the-main-response }
You can use this same `responses` parameter to add different media types for the same main response.
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note
Notice that you have to return the image using a `FileResponse` directly.
///
/// info
Unless you specify a different media type explicitly in your `responses` parameter, FastAPI will assume the response has the same media type as the main response class (default `application/json`).
But if you have specified a custom response class with `None` as its media type, FastAPI will use `application/json` for any additional response that has an associated model.
///
## Combining information { #combining-information }
You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters.
You can declare a `response_model`, using the default status code `200` (or a custom one if you need), and then declare additional information for that same response in `responses`, directly in the OpenAPI schema.
**FastAPI** will keep the additional information from `responses`, and combine it with the JSON Schema from your model.
For example, you can declare a response with a status code `404` that uses a Pydantic model and has a custom `description`.
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
It will all be combined and included in your OpenAPI, and shown in the API docs:
## Combine predefined responses and custom ones { #combine-predefined-responses-and-custom-ones }
You might want to have some predefined responses that apply to many *path operations*, but you want to combine them with custom responses needed by each *path operation*.
For those cases, you can use the Python technique of "unpacking" a `dict` with `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Here, `new_dict` will contain all the key-value pairs from `old_dict` plus the new key-value pair:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
You can use that technique to reuse some predefined responses in your *path operations* and combine them with additional custom ones.
For example:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## More information about OpenAPI responses { #more-information-about-openapi-responses }
To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification:
* [OpenAPI Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), it includes the `Response Object`.
* [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`.
================================================
FILE: docs/en/docs/advanced/additional-status-codes.md
================================================
# Additional Status Codes { #additional-status-codes }
By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
It will use the default status code or the one you set in your *path operation*.
## Additional status codes { #additional-status-codes_1 }
If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly.
For example, let's say that you want to have a *path operation* that allows to update items, and returns HTTP status codes of 200 "OK" when successful.
But you also want it to accept new items. And when the items didn't exist before, it creates them, and returns an HTTP status code of 201 "Created".
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning
When you return a `Response` directly, like in the example above, it will be returned directly.
It won't be serialized with a model, etc.
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
///
/// note | Technical Details
You could also use `from starlette.responses import JSONResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `status`.
///
## OpenAPI and API docs { #openapi-and-api-docs }
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return.
But you can document that in your code, using: [Additional Responses](additional-responses.md).
================================================
FILE: docs/en/docs/advanced/advanced-dependencies.md
================================================
# Advanced Dependencies { #advanced-dependencies }
## Parameterized dependencies { #parameterized-dependencies }
All the dependencies we have seen are a fixed function or class.
But there could be cases where you want to be able to set parameters on the dependency, without having to declare many different functions or classes.
Let's imagine that we want to have a dependency that checks if the query parameter `q` contains some fixed content.
But we want to be able to parameterize that fixed content.
## A "callable" instance { #a-callable-instance }
In Python there's a way to make an instance of a class a "callable".
Not the class itself (which is already a callable), but an instance of that class.
To do that, we declare a method `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
## Parameterize the instance { #parameterize-the-instance }
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
## Create an instance { #create-an-instance }
We could create an instance of this class with:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
## Use the instance as a dependency { #use-the-instance-as-a-dependency }
Then, we could use this `checker` in a `Depends(checker)`, instead of `Depends(FixedContentQueryChecker)`, because the dependency is the instance, `checker`, not the class itself.
And when solving the dependency, **FastAPI** will call this `checker` like:
```Python
checker(q="somequery")
```
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip
All this might seem contrived. And it might not be very clear how is it useful yet.
These examples are intentionally simple, but show how it all works.
In the chapters about security, there are utility functions that are implemented in this same way.
If you understood all this, you already know how those utility tools for security work underneath.
///
## Dependencies with `yield`, `HTTPException`, `except` and Background Tasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning
You most probably don't need these technical details.
These details are useful mainly if you had a FastAPI application older than 0.121.0 and you are facing issues with dependencies with `yield`.
///
Dependencies with `yield` have evolved over time to account for the different use cases and to fix some issues, here's a summary of what has changed.
### Dependencies with `yield` and `scope` { #dependencies-with-yield-and-scope }
In version 0.121.0, FastAPI added support for `Depends(scope="function")` for dependencies with `yield`.
Using `Depends(scope="function")`, the exit code after `yield` is executed right after the *path operation function* is finished, before the response is sent back to the client.
And when using `Depends(scope="request")` (the default), the exit code after `yield` is executed after the response is sent.
You can read more about it in the docs for [Dependencies with `yield` - Early exit and `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Dependencies with `yield` and `StreamingResponse`, Technical Details { #dependencies-with-yield-and-streamingresponse-technical-details }
Before FastAPI 0.118.0, if you used a dependency with `yield`, it would run the exit code after the *path operation function* returned but right before sending the response.
The intention was to avoid holding resources for longer than necessary, waiting for the response to travel through the network.
This change also meant that if you returned a `StreamingResponse`, the exit code of the dependency with `yield` would have been already run.
For example, if you had a database session in a dependency with `yield`, the `StreamingResponse` would not be able to use that session while streaming data because the session would have already been closed in the exit code after `yield`.
This behavior was reverted in 0.118.0, to make the exit code after `yield` be executed after the response is sent.
/// info
As you will see below, this is very similar to the behavior before version 0.106.0, but with several improvements and bug fixes for corner cases.
///
#### Use Cases with Early Exit Code { #use-cases-with-early-exit-code }
There are some use cases with specific conditions that could benefit from the old behavior of running the exit code of dependencies with `yield` before sending the response.
For example, imagine you have code that uses a database session in a dependency with `yield` only to verify a user, but the database session is never used again in the *path operation function*, only in the dependency, **and** the response takes a long time to be sent, like a `StreamingResponse` that sends data slowly, but for some reason doesn't use the database.
In this case, the database session would be held until the response is finished being sent, but if you don't use it, then it wouldn't be necessary to hold it.
Here's how it could look like:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
The exit code, the automatic closing of the `Session` in:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...would be run after the response finishes sending the slow data:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
But as `generate_stream()` doesn't use the database session, it is not really necessary to keep the session open while sending the response.
If you have this specific use case using SQLModel (or SQLAlchemy), you could explicitly close the session after you don't need it anymore:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
That way the session would release the database connection, so other requests could use it.
If you have a different use case that needs to exit early from a dependency with `yield`, please create a [GitHub Discussion Question](https://github.com/fastapi/fastapi/discussions/new?category=questions) with your specific use case and why you would benefit from having early closing for dependencies with `yield`.
If there are compelling use cases for early closing in dependencies with `yield`, I would consider adding a new way to opt in to early closing.
### Dependencies with `yield` and `except`, Technical Details { #dependencies-with-yield-and-except-technical-details }
Before FastAPI 0.110.0, if you used a dependency with `yield`, and then you captured an exception with `except` in that dependency, and you didn't raise the exception again, the exception would be automatically raised/forwarded to any exception handlers or the internal server error handler.
This was changed in version 0.110.0 to fix unhandled memory consumption from forwarded exceptions without a handler (internal server errors), and to make it consistent with the behavior of regular Python code.
### Background Tasks and Dependencies with `yield`, Technical Details { #background-tasks-and-dependencies-with-yield-technical-details }
Before FastAPI 0.106.0, raising exceptions after `yield` was not possible, the exit code in dependencies with `yield` was executed *after* the response was sent, so [Exception Handlers](../tutorial/handling-errors.md#install-custom-exception-handlers) would have already run.
This was designed this way mainly to allow using the same objects "yielded" by dependencies inside of background tasks, because the exit code would be executed after the background tasks were finished.
This was changed in FastAPI 0.106.0 with the intention to not hold resources while waiting for the response to travel through the network.
/// tip
Additionally, a background task is normally an independent set of logic that should be handled separately, with its own resources (e.g. its own database connection).
So, this way you will probably have cleaner code.
///
If you used to rely on this behavior, now you should create the resources for background tasks inside the background task itself, and use internally only data that doesn't depend on the resources of dependencies with `yield`.
For example, instead of using the same database session, you would create a new database session inside of the background task, and you would obtain the objects from the database using this new session. And then instead of passing the object from the database as a parameter to the background task function, you would pass the ID of that object and then obtain the object again inside the background task function.
================================================
FILE: docs/en/docs/advanced/advanced-python-types.md
================================================
# Advanced Python Types { #advanced-python-types }
Here are some additional ideas that might be useful when working with Python types.
## Using `Union` or `Optional` { #using-union-or-optional }
If your code for some reason can't use `|`, for example if it's not in a type annotation but in something like `response_model=`, instead of using the vertical bar (`|`) you can use `Union` from `typing`.
For example, you could declare that something could be a `str` or `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` also has a shortcut to declare that something could be `None`, with `Optional`.
Here's a tip from my very **subjective** point of view:
* 🚨 Avoid using `Optional[SomeType]`
* Instead ✨ **use `Union[SomeType, None]`** ✨.
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
I think `Union[SomeType, None]` is more explicit about what it means.
It's just about the words and names. But those words can affect how you and your teammates think about the code.
As an example, let's take this function:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
```Python
say_hi() # Oh, no, this throws an error! 😱
```
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
```Python
say_hi(name=None) # This works, None is valid 🎉
```
The good news is, in most cases, you will be able to simply use `|` to define unions of types:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
So, normally you don't have to worry about names like `Optional` and `Union`. 😎
================================================
FILE: docs/en/docs/advanced/async-tests.md
================================================
# Async Tests { #async-tests }
You have already seen how to test your **FastAPI** applications using the provided `TestClient`. Up to now, you have only seen how to write synchronous tests, without using `async` functions.
Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library.
Let's look at how we can make that work.
## pytest.mark.anyio { #pytest-mark-anyio }
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. AnyIO provides a neat plugin for this, that allows us to specify that some test functions are to be called asynchronously.
## HTTPX { #httpx }
Even if your **FastAPI** application uses normal `def` functions instead of `async def`, it is still an `async` application underneath.
The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions.
The `TestClient` is based on [HTTPX](https://www.python-httpx.org), and luckily, we can use it directly to test the API.
## Example { #example }
For a simple example, let's consider a file structure similar to the one described in [Bigger Applications](../tutorial/bigger-applications.md) and [Testing](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
The file `main.py` would have:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
The file `test_main.py` would have the tests for `main.py`, it could look like this now:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Run it { #run-it }
You can run your tests as usual via:
```console
$ pytest
---> 100%
```
## In Detail { #in-detail }
The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip
Note that the test function is now `async def` instead of just `def` as before when using the `TestClient`.
///
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
This is the equivalent to:
```Python
response = client.get('/')
```
...that we used to make our requests with the `TestClient`.
/// tip
Note that we're using async/await with the new `AsyncClient` - the request is asynchronous.
///
/// warning
If your application relies on lifespan events, the `AsyncClient` won't trigger these events. To ensure they are triggered, use `LifespanManager` from [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Other Asynchronous Function Calls { #other-asynchronous-function-calls }
As the testing function is now asynchronous, you can now also call (and `await`) other `async` functions apart from sending requests to your FastAPI application in your tests, exactly as you would call them anywhere else in your code.
/// tip
If you encounter a `RuntimeError: Task attached to a different loop` when integrating asynchronous function calls in your tests (e.g. when using [MongoDB's MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), remember to instantiate objects that need an event loop only within async functions, e.g. an `@app.on_event("startup")` callback.
///
================================================
FILE: docs/en/docs/advanced/behind-a-proxy.md
================================================
# Behind a Proxy { #behind-a-proxy }
In many situations, you would use a **proxy** like Traefik or Nginx in front of your FastAPI app.
These proxies could handle HTTPS certificates and other things.
## Proxy Forwarded Headers { #proxy-forwarded-headers }
A **proxy** in front of your application would normally set some headers on the fly before sending the requests to your **server** to let the server know that the request was **forwarded** by the proxy, letting it know the original (public) URL, including the domain, that it is using HTTPS, etc.
The **server** program (for example **Uvicorn** via **FastAPI CLI**) is capable of interpreting these headers, and then passing that information to your application.
But for security, as the server doesn't know it is behind a trusted proxy, it won't interpret those headers.
/// note | Technical Details
The proxy headers are:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Enable Proxy Forwarded Headers { #enable-proxy-forwarded-headers }
You can start FastAPI CLI with the *CLI Option* `--forwarded-allow-ips` and pass the IP addresses that should be trusted to read those forwarded headers.
If you set it to `--forwarded-allow-ips="*"` it would trust all the incoming IPs.
If your **server** is behind a trusted **proxy** and only the proxy talks to it, this would make it accept whatever is the IP of that **proxy**.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Redirects with HTTPS { #redirects-with-https }
For example, let's say you define a *path operation* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
If the client tries to go to `/items`, by default, it would be redirected to `/items/`.
But before setting the *CLI Option* `--forwarded-allow-ips` it could redirect to `http://localhost:8000/items/`.
But maybe your application is hosted at `https://mysuperapp.com`, and the redirection should be to `https://mysuperapp.com/items/`.
By setting `--proxy-headers` now FastAPI would be able to redirect to the right location. 😎
```
https://mysuperapp.com/items/
```
/// tip
If you want to learn more about HTTPS, check the guide [About HTTPS](../deployment/https.md).
///
### How Proxy Forwarded Headers Work { #how-proxy-forwarded-headers-work }
Here's a visual representation of how the **proxy** adds forwarded headers between the client and the **application server**:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
The **proxy** intercepts the original client request and adds the special *forwarded* headers (`X-Forwarded-*`) before passing the request to the **application server**.
These headers preserve information about the original request that would otherwise be lost:
* **X-Forwarded-For**: The original client's IP address
* **X-Forwarded-Proto**: The original protocol (`https`)
* **X-Forwarded-Host**: The original host (`mysuperapp.com`)
When **FastAPI CLI** is configured with `--forwarded-allow-ips`, it trusts these headers and uses them, for example to generate the correct URLs in redirects.
## Proxy with a stripped path prefix { #proxy-with-a-stripped-path-prefix }
You could have a proxy that adds a path prefix to your application.
In these cases you can use `root_path` to configure your application.
The `root_path` is a mechanism provided by the ASGI specification (that FastAPI is built on, through Starlette).
The `root_path` is used to handle these specific cases.
And it's also used internally when mounting sub-applications.
Having a proxy with a stripped path prefix, in this case, means that you could declare a path at `/app` in your code, but then, you add a layer on top (the proxy) that would put your **FastAPI** application under a path like `/api/v1`.
In this case, the original path `/app` would actually be served at `/api/v1/app`.
Even though all your code is written assuming there's just `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to the app server (probably Uvicorn via FastAPI CLI), keeping your application convinced that it is being served at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`.
Up to here, everything would work as normally.
But then, when you open the integrated docs UI (the frontend), it would expect to get the OpenAPI schema at `/openapi.json`, instead of `/api/v1/openapi.json`.
So, the frontend (that runs in the browser) would try to reach `/openapi.json` and wouldn't be able to get the OpenAPI schema.
Because we have a proxy with a path prefix of `/api/v1` for our app, the frontend needs to fetch the OpenAPI schema at `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip
The IP `0.0.0.0` is commonly used to mean that the program listens on all the IPs available in that machine/server.
///
The docs UI would also need the OpenAPI schema to declare that this API `server` is located at `/api/v1` (behind the proxy). For example:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// More stuff here
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// More stuff here
}
}
```
In this example, the "Proxy" could be something like **Traefik**. And the server would be something like FastAPI CLI with **Uvicorn**, running your FastAPI application.
### Providing the `root_path` { #providing-the-root-path }
To achieve this, you can use the command line option `--root-path` like:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
If you use Hypercorn, it also has the option `--root-path`.
/// note | Technical Details
The ASGI specification defines a `root_path` for this use case.
And the `--root-path` command line option provides that `root_path`.
///
### Checking the current `root_path` { #checking-the-current-root-path }
You can get the current `root_path` used by your application for each request, it is part of the `scope` dictionary (that's part of the ASGI spec).
Here we are including it in the message just for demonstration purposes.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Then, if you start Uvicorn with:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
The response would be something like:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Setting the `root_path` in the FastAPI app { #setting-the-root-path-in-the-fastapi-app }
Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn.
### About `root_path` { #about-root-path }
Keep in mind that the server (Uvicorn) won't use that `root_path` for anything else than passing it to the app.
But if you go with your browser to [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) you will see the normal response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
So, it won't expect to be accessed at `http://127.0.0.1:8000/api/v1/app`.
Uvicorn will expect the proxy to access Uvicorn at `http://127.0.0.1:8000/app`, and then it would be the proxy's responsibility to add the extra `/api/v1` prefix on top.
## About proxies with a stripped path prefix { #about-proxies-with-a-stripped-path-prefix }
Keep in mind that a proxy with stripped path prefix is only one of the ways to configure it.
Probably in many cases the default will be that the proxy doesn't have a stripped path prefix.
In a case like that (without a stripped path prefix), the proxy would listen on something like `https://myawesomeapp.com`, and then if the browser goes to `https://myawesomeapp.com/api/v1/app` and your server (e.g. Uvicorn) listens on `http://127.0.0.1:8000` the proxy (without a stripped path prefix) would access Uvicorn at the same path: `http://127.0.0.1:8000/api/v1/app`.
## Testing locally with Traefik { #testing-locally-with-traefik }
You can easily run the experiment locally with a stripped path prefix using [Traefik](https://docs.traefik.io/).
[Download Traefik](https://github.com/containous/traefik/releases), it's a single binary, you can extract the compressed file and run it directly from the terminal.
Then create a file `traefik.toml` with:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
This tells Traefik to listen on port 9999 and to use another file `routes.toml`.
/// tip
We are using port 9999 instead of the standard HTTP port 80 so that you don't have to run it with admin (`sudo`) privileges.
///
Now create that other file `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
This file configures Traefik to use the path prefix `/api/v1`.
And then Traefik will redirect its requests to your Uvicorn running on `http://127.0.0.1:8000`.
Now start Traefik:
And now start your app, using the `--root-path` option:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Check the responses { #check-the-responses }
Now, if you go to the URL with the port for Uvicorn: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), you will see the normal response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip
Notice that even though you are accessing it at `http://127.0.0.1:8000/app` it shows the `root_path` of `/api/v1`, taken from the option `--root-path`.
///
And now open the URL with the port for Traefik, including the path prefix: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
We get the same response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
but this time at the URL with the prefix path provided by the proxy: `/api/v1`.
Of course, the idea here is that everyone would access the app through the proxy, so the version with the path prefix `/api/v1` is the "correct" one.
And the version without the path prefix (`http://127.0.0.1:8000/app`), provided by Uvicorn directly, would be exclusively for the _proxy_ (Traefik) to access it.
That demonstrates how the Proxy (Traefik) uses the path prefix and how the server (Uvicorn) uses the `root_path` from the option `--root-path`.
### Check the docs UI { #check-the-docs-ui }
But here's the fun part. ✨
The "official" way to access the app would be through the proxy with the path prefix that we defined. So, as we would expect, if you try the docs UI served by Uvicorn directly, without the path prefix in the URL, it won't work, because it expects to be accessed through the proxy.
You can check it at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
But if we access the docs UI at the "official" URL using the proxy with port `9999`, at `/api/v1/docs`, it works correctly! 🎉
You can check it at [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Right as we wanted it. ✔️
This is because FastAPI uses this `root_path` to create the default `server` in OpenAPI with the URL provided by `root_path`.
## Additional servers { #additional-servers }
/// warning
This is a more advanced use case. Feel free to skip it.
///
By default, **FastAPI** will create a `server` in the OpenAPI schema with the URL for the `root_path`.
But you can also provide other alternative `servers`, for example if you want *the same* docs UI to interact with both a staging and a production environment.
If you pass a custom list of `servers` and there's a `root_path` (because your API lives behind a proxy), **FastAPI** will insert a "server" with this `root_path` at the beginning of the list.
For example:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Will generate an OpenAPI schema like:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// More stuff here
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// More stuff here
}
}
```
/// tip
Notice the auto-generated server with a `url` value of `/api/v1`, taken from the `root_path`.
///
In the docs UI at [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) it would look like:
/// tip
The docs UI will interact with the server that you select.
///
/// note | Technical Details
The `servers` property in the OpenAPI specification is optional.
If you don't specify the `servers` parameter and `root_path` is equal to `/`, the `servers` property in the generated OpenAPI schema will be omitted entirely by default, which is the equivalent of a single server with a `url` value of `/`.
///
### Disable automatic server from `root_path` { #disable-automatic-server-from-root-path }
If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
and then it won't include it in the OpenAPI schema.
## Mounting a sub-application { #mounting-a-sub-application }
If you need to mount a sub-application (as described in [Sub Applications - Mounts](sub-applications.md)) while also using a proxy with `root_path`, you can do it normally, as you would expect.
FastAPI will internally use the `root_path` smartly, so it will just work. ✨
================================================
FILE: docs/en/docs/advanced/custom-response.md
================================================
# Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others }
By default, **FastAPI** will return JSON responses.
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md).
But if you return a `Response` directly (or any subclass, like `JSONResponse`), the data won't be automatically converted (even if you declare a `response_model`), and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI).
But you can also declare the `Response` that you want to be used (e.g. any `Response` subclass), in the *path operation decorator* using the `response_class` parameter.
The contents that you return from your *path operation function* will be put inside of that `Response`.
/// note
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
///
## JSON Responses { #json-responses }
By default FastAPI returns JSON responses.
If you declare a [Response Model](../tutorial/response-model.md) FastAPI will use it to serialize the data to JSON, using Pydantic.
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md) and put it in a `JSONResponse`.
If you declare a `response_class` with a JSON media type (`application/json`), like is the case with the `JSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. But the data won't be serialized to JSON bytes with Pydantic, instead it will be converted with the `jsonable_encoder` and then passed to the `JSONResponse` class, which will serialize it to bytes using the standard JSON library in Python.
### JSON Performance { #json-performance }
In short, if you want the maximum performance, use a [Response Model](../tutorial/response-model.md) and don't declare a `response_class` in the *path operation decorator*.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML Response { #html-response }
To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
* Import `HTMLResponse`.
* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info
The parameter `response_class` will also be used to define the "media type" of the response.
In this case, the HTTP header `Content-Type` will be set to `text/html`.
And it will be documented as such in OpenAPI.
///
### Return a `Response` { #return-a-response }
As seen in [Return a Response directly](response-directly.md), you can also override the response directly in your *path operation*, by returning it.
The same example from above, returning an `HTMLResponse`, could look like:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning
A `Response` returned directly by your *path operation function* won't be documented in OpenAPI (for example, the `Content-Type` won't be documented) and won't be visible in the automatic interactive docs.
///
/// info
Of course, the actual `Content-Type` header, status code, etc, will come from the `Response` object you returned.
///
### Document in OpenAPI and override `Response` { #document-in-openapi-and-override-response }
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `response_class` parameter AND return a `Response` object.
The `response_class` will then be used only to document the OpenAPI *path operation*, but your `Response` will be used as is.
#### Return an `HTMLResponse` directly { #return-an-htmlresponse-directly }
For example, it could be something like:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
But as you passed the `HTMLResponse` in the `response_class` too, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
## Available responses { #available-responses }
Here are some of the available responses.
Keep in mind that you can use `Response` to return anything else, or even create a custom sub-class.
/// note | Technical Details
You could also use `from starlette.responses import HTMLResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
///
### `Response` { #response }
The main `Response` class, all the other responses inherit from it.
You can return it directly.
It accepts the following parameters:
* `content` - A `str` or `bytes`.
* `status_code` - An `int` HTTP status code.
* `headers` - A `dict` of strings.
* `media_type` - A `str` giving the media type. E.g. `"text/html"`.
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the `media_type` and appending a charset for text types.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Takes some text or bytes and returns an HTML response, as you read above.
### `PlainTextResponse` { #plaintextresponse }
Takes some text or bytes and returns a plain text response.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Takes some data and returns an `application/json` encoded response.
This is the default response used in **FastAPI**, as you read above.
/// note | Technical Details
But if you declare a response model or return type, that will be used directly to serialize the data to JSON, and a response with the right media type for JSON will be returned directly, without using the `JSONResponse` class.
This is the ideal way to get the best performance.
///
### `RedirectResponse` { #redirectresponse }
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
You can return a `RedirectResponse` directly:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Or you can use it in the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
If you do that, then you can return the URL directly from your *path operation* function.
In this case, the `status_code` used will be the default one for the `RedirectResponse`, which is `307`.
---
You can also use the `status_code` parameter combined with the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Takes an async generator or a normal generator/iterator (a function with `yield`) and streams the response body.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Technical Details
An `async` task can only be cancelled when it reaches an `await`. If there is no `await`, the generator (function with `yield`) can not be cancelled properly and may keep running even after cancellation is requested.
Since this small example does not need any `await` statements, we add an `await anyio.sleep(0)` to give the event loop a chance to handle cancellation.
This would be even more important with large or infinite streams.
///
/// tip
Instead of returning a `StreamingResponse` directly, you should probably follow the style in [Stream Data](./stream-data.md), it's much more convenient and handles cancellation behind the scenes for you.
If you are streaming JSON Lines, follow the [Stream JSON Lines](../tutorial/stream-json-lines.md) tutorial.
///
### `FileResponse` { #fileresponse }
Asynchronously streams a file as the response.
Takes a different set of arguments to instantiate than the other response types:
* `path` - The file path to the file to stream.
* `headers` - Any custom headers to include, as a dictionary.
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
* `filename` - If set, this will be included in the response `Content-Disposition`.
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
You can also use the `response_class` parameter:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
In this case, you can return the file path directly from your *path operation* function.
## Custom response class { #custom-response-class }
You can create your own custom response class, inheriting from `Response` and using it.
For example, let's say that you want to use [`orjson`](https://github.com/ijl/orjson) with some settings.
Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`.
You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Now instead of returning:
```json
{"message": "Hello World"}
```
...this response will return:
```json
{
"message": "Hello World"
}
```
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉
### `orjson` or Response Model { #orjson-or-response-model }
If what you are looking for is performance, you are probably better off using a [Response Model](../tutorial/response-model.md) than an `orjson` response.
With a response model, FastAPI will use Pydantic to serialize the data to JSON, without using intermediate steps, like converting it with `jsonable_encoder`, which would happen in any other case.
And under the hood, Pydantic uses the same underlying Rust mechanisms as `orjson` to serialize to JSON, so you will already get the best performance with a response model.
## Default response class { #default-response-class }
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
The parameter that defines this is `default_response_class`.
In the example below, **FastAPI** will use `HTMLResponse` by default, in all *path operations*, instead of JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip
You can still override `response_class` in *path operations* as before.
///
## Additional documentation { #additional-documentation }
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md).
================================================
FILE: docs/en/docs/advanced/dataclasses.md
================================================
# Using Dataclasses { #using-dataclasses }
FastAPI is built on top of **Pydantic**, and I have been showing you how to use Pydantic models to declare requests and responses.
But FastAPI also supports using [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) the same way:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
This is still supported thanks to **Pydantic**, as it has [internal support for `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
So, even with the code above that doesn't use Pydantic explicitly, FastAPI is using Pydantic to convert those standard dataclasses to Pydantic's own flavor of dataclasses.
And of course, it supports the same:
* data validation
* data serialization
* data documentation, etc.
This works the same way as with Pydantic models. And it is actually achieved in the same way underneath, using Pydantic.
/// info
Keep in mind that dataclasses can't do everything Pydantic models can do.
So, you might still need to use Pydantic models.
But if you have a bunch of dataclasses laying around, this is a nice trick to use them to power a web API using FastAPI. 🤓
///
## Dataclasses in `response_model` { #dataclasses-in-response-model }
You can also use `dataclasses` in the `response_model` parameter:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
The dataclass will be automatically converted to a Pydantic dataclass.
This way, its schema will show up in the API docs user interface:
## Dataclasses in Nested Data Structures { #dataclasses-in-nested-data-structures }
You can also combine `dataclasses` with other type annotations to make nested data structures.
In some cases, you might still have to use Pydantic's version of `dataclasses`. For example, if you have errors with the automatically generated API documentation.
In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. We still import `field` from standard `dataclasses`.
2. `pydantic.dataclasses` is a drop-in replacement for `dataclasses`.
3. The `Author` dataclass includes a list of `Item` dataclasses.
4. The `Author` dataclass is used as the `response_model` parameter.
5. You can use other standard type annotations with dataclasses as the request body.
In this case, it's a list of `Item` dataclasses.
6. Here we are returning a dictionary that contains `items` which is a list of dataclasses.
FastAPI is still capable of serializing the data to JSON.
7. Here the `response_model` is using a type annotation of a list of `Author` dataclasses.
Again, you can combine `dataclasses` with standard type annotations.
8. Notice that this *path operation function* uses regular `def` instead of `async def`.
As always, in FastAPI you can combine `def` and `async def` as needed.
If you need a refresher about when to use which, check out the section _"In a hurry?"_ in the docs about [`async` and `await`](../async.md#in-a-hurry).
9. This *path operation function* is not returning dataclasses (although it could), but a list of dictionaries with internal data.
FastAPI will use the `response_model` parameter (that includes dataclasses) to convert the response.
You can combine `dataclasses` with other type annotations in many different combinations to form complex data structures.
Check the in-code annotation tips above to see more specific details.
## Learn More { #learn-more }
You can also combine `dataclasses` with other Pydantic models, inherit from them, include them in your own models, etc.
To learn more, check the [Pydantic docs about dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Version { #version }
This is available since FastAPI version `0.67.0`. 🔖
================================================
FILE: docs/en/docs/advanced/events.md
================================================
# Lifespan Events { #lifespan-events }
You can define logic (code) that should be executed before the application **starts up**. This means that this code will be executed **once**, **before** the application **starts receiving requests**.
The same way, you can define logic (code) that should be executed when the application is **shutting down**. In this case, this code will be executed **once**, **after** having handled possibly **many requests**.
Because this code is executed before the application **starts** taking requests, and right after it **finishes** handling requests, it covers the whole application **lifespan** (the word "lifespan" will be important in a second 😉).
This can be very useful for setting up **resources** that you need to use for the whole app, and that are **shared** among requests, and/or that you need to **clean up** afterwards. For example, a database connection pool, or loading a shared machine learning model.
## Use Case { #use-case }
Let's start with an example **use case** and then see how to solve it with this.
Let's imagine that you have some **machine learning models** that you want to use to handle requests. 🤖
The same models are shared among requests, so, it's not one model per request, or one per user or something similar.
Let's imagine that loading the model can **take quite some time**, because it has to read a lot of **data from disk**. So you don't want to do it for every request.
You could load it at the top level of the module/file, but that would also mean that it would **load the model** even if you are just running a simple automated test, then that test would be **slow** because it would have to wait for the model to load before being able to run an independent part of the code.
That's what we'll solve, let's load the model before the requests are handled, but only right before the application starts receiving requests, not while the code is being loaded.
## Lifespan { #lifespan }
You can define this *startup* and *shutdown* logic using the `lifespan` parameter of the `FastAPI` app, and a "context manager" (I'll show you what that is in a second).
Let's start with an example and then see it in detail.
We create an async function `lifespan()` with `yield` like this:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*.
And then, right after the `yield`, we unload the model. This code will be executed **after** the application **finishes handling requests**, right before the *shutdown*. This could, for example, release resources like memory or a GPU.
/// tip
The `shutdown` would happen when you are **stopping** the application.
Maybe you need to start a new version, or you just got tired of running it. 🤷
///
### Lifespan function { #lifespan-function }
The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
The first part of the function, before the `yield`, will be executed **before** the application starts.
And the part after the `yield` will be executed **after** the application has finished.
### Async Context Manager { #async-context-manager }
If you check, the function is decorated with an `@asynccontextmanager`.
That converts the function into something called an "**async context manager**".
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager:
```Python
with open("file.txt") as file:
file.read()
```
In recent versions of Python, there's also an **async context manager**. You would use it with `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
When you create a context manager or an async context manager like above, what it does is that, before entering the `with` block, it will execute the code before the `yield`, and after exiting the `with` block, it will execute the code after the `yield`.
In our code example above, we don't use it directly, but we pass it to FastAPI for it to use it.
The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Alternative Events (deprecated) { #alternative-events-deprecated }
/// warning
The recommended way to handle the *startup* and *shutdown* is using the `lifespan` parameter of the `FastAPI` app as described above. If you provide a `lifespan` parameter, `startup` and `shutdown` event handlers will no longer be called. It's all `lifespan` or all events, not both.
You can probably skip this part.
///
There's an alternative way to define this logic to be executed during *startup* and during *shutdown*.
You can define event handlers (functions) that need to be executed before the application starts up, or when the application is shutting down.
These functions can be declared with `async def` or normal `def`.
### `startup` event { #startup-event }
To add a function that should be run before the application starts, declare it with the event `"startup"`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
You can add more than one event handler function.
And your application won't start receiving requests until all the `startup` event handlers have completed.
### `shutdown` event { #shutdown-event }
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
/// info
In the `open()` function, the `mode="a"` means "append", so, the line will be added after whatever is on that file, without overwriting the previous contents.
///
/// tip
Notice that in this case we are using a standard Python `open()` function that interacts with a file.
So, it involves I/O (input/output), that requires "waiting" for things to be written to disk.
But `open()` doesn't use `async` and `await`.
So, we declare the event handler function with standard `def` instead of `async def`.
///
### `startup` and `shutdown` together { #startup-and-shutdown-together }
There's a high chance that the logic for your *startup* and *shutdown* is connected, you might want to start something and then finish it, acquire a resource and then release it, etc.
Doing that in separated functions that don't share logic or variables together is more difficult as you would need to store values in global variables or similar tricks.
Because of that, it's now recommended to instead use the `lifespan` as explained above.
## Technical Details { #technical-details }
Just a technical detail for the curious nerds. 🤓
Underneath, in the ASGI technical specification, this is part of the [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), and it defines events called `startup` and `shutdown`.
/// info
You can read more about the Starlette `lifespan` handlers in [Starlette's Lifespan' docs](https://www.starlette.dev/lifespan/).
Including how to handle lifespan state that can be used in other areas of your code.
///
## Sub Applications { #sub-applications }
🚨 Keep in mind that these lifespan events (startup and shutdown) will only be executed for the main application, not for [Sub Applications - Mounts](sub-applications.md).
================================================
FILE: docs/en/docs/advanced/generate-clients.md
================================================
# Generating SDKs { #generating-sdks }
Because **FastAPI** is based on the **OpenAPI** specification, its APIs can be described in a standard format that many tools understand.
This makes it easy to generate up-to-date **documentation**, client libraries (**SDKs**) in multiple languages, and **testing** or **automation workflows** that stay in sync with your code.
In this guide, you'll learn how to generate a **TypeScript SDK** for your FastAPI backend.
## Open Source SDK Generators { #open-source-sdk-generators }
A versatile option is the [OpenAPI Generator](https://openapi-generator.tech/), which supports **many programming languages** and can generate SDKs from your OpenAPI specification.
For **TypeScript clients**, [Hey API](https://heyapi.dev/) is a purpose-built solution, providing an optimized experience for the TypeScript ecosystem.
You can discover more SDK generators on [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip
FastAPI automatically generates **OpenAPI 3.1** specifications, so any tool you use must support this version.
///
## SDK Generators from FastAPI Sponsors { #sdk-generators-from-fastapi-sponsors }
This section highlights **venture-backed** and **company-supported** solutions from companies that sponsor FastAPI. These products provide **additional features** and **integrations** on top of high-quality generated SDKs.
By ✨ [**sponsoring FastAPI**](../help-fastapi.md#sponsor-the-author) ✨, these companies help ensure the framework and its **ecosystem** remain healthy and **sustainable**.
Their sponsorship also demonstrates a strong commitment to the FastAPI **community** (you), showing that they care not only about offering a **great service** but also about supporting a **robust and thriving framework**, FastAPI. 🙇
For example, you might want to try:
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Some of these solutions may also be open source or offer free tiers, so you can try them without a financial commitment. Other commercial SDK generators are available and can be found online. 🤓
## Create a TypeScript SDK { #create-a-typescript-sdk }
Let's start with a simple FastAPI application:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`.
### API Docs { #api-docs }
If you go to `/docs`, you will see that it has the **schemas** for the data to be sent in requests and received in responses:
You can see those schemas because they were declared with the models in the app.
That information is available in the app's **OpenAPI schema**, and then shown in the API docs.
That same information from the models that is included in OpenAPI is what can be used to **generate the client code**.
### Hey API { #hey-api }
Once we have a FastAPI app with the models, we can use Hey API to generate a TypeScript client. The fastest way to do that is via npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
This will generate a TypeScript SDK in `./src/client`.
You can learn how to [install `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started) and read about the [generated output](https://heyapi.dev/openapi-ts/output) on their website.
### Using the SDK { #using-the-sdk }
Now you can import and use the client code. It could look like this, notice that you get autocompletion for the methods:
You will also get autocompletion for the payload to send:
/// tip
Notice the autocompletion for `name` and `price`, that was defined in the FastAPI application, in the `Item` model.
///
You will have inline errors for the data that you send:
The response object will also have autocompletion:
## FastAPI App with Tags { #fastapi-app-with-tags }
In many cases, your FastAPI app will be bigger, and you will probably use tags to separate different groups of *path operations*.
For example, you could have a section for **items** and another section for **users**, and they could be separated by tags:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Generate a TypeScript Client with Tags { #generate-a-typescript-client-with-tags }
If you generate a client for a FastAPI app using tags, it will normally also separate the client code based on the tags.
This way, you will be able to have things ordered and grouped correctly for the client code:
In this case, you have:
* `ItemsService`
* `UsersService`
### Client Method Names { #client-method-names }
Right now, the generated method names like `createItemItemsPost` don't look very clean:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...that's because the client generator uses the OpenAPI internal **operation ID** for each *path operation*.
OpenAPI requires that each operation ID is unique across all the *path operations*, so FastAPI uses the **function name**, the **path**, and the **HTTP method/operation** to generate that operation ID, because that way it can make sure that the operation IDs are unique.
But I'll show you how to improve that next. 🤓
## Custom Operation IDs and Better Method Names { #custom-operation-ids-and-better-method-names }
You can **modify** the way these operation IDs are **generated** to make them simpler and have **simpler method names** in the clients.
In this case, you will have to ensure that each operation ID is **unique** in some other way.
For example, you could make sure that each *path operation* has a tag, and then generate the operation ID based on the **tag** and the *path operation* **name** (the function name).
### Custom Generate Unique ID Function { #custom-generate-unique-id-function }
FastAPI uses a **unique ID** for each *path operation*, which is used for the **operation ID** and also for the names of any needed custom models, for requests or responses.
You can customize that function. It takes an `APIRoute` and outputs a string.
For example, here it is using the first tag (you will probably have only one tag) and the *path operation* name (the function name).
You can then pass that custom function to **FastAPI** as the `generate_unique_id_function` parameter:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Generate a TypeScript Client with Custom Operation IDs { #generate-a-typescript-client-with-custom-operation-ids }
Now, if you generate the client again, you will see that it has the improved method names:
As you see, the method names now have the tag and then the function name, now they don't include information from the URL path and the HTTP operation.
### Preprocess the OpenAPI Specification for the Client Generator { #preprocess-the-openapi-specification-for-the-client-generator }
The generated code still has some **duplicated information**.
We already know that this method is related to the **items** because that word is in the `ItemsService` (taken from the tag), but we still have the tag name prefixed in the method name too. 😕
We will probably still want to keep it for OpenAPI in general, as that will ensure that the operation IDs are **unique**.
But for the generated client, we could **modify** the OpenAPI operation IDs right before generating the clients, just to make those method names nicer and **cleaner**.
We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
With that, the operation IDs would be renamed from things like `items-get_items` to just `get_items`, that way the client generator can generate simpler method names.
### Generate a TypeScript Client with the Preprocessed OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
Since the end result is now in an `openapi.json` file, you need to update your input location:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
After generating the new client, you would now have **clean method names**, with all the **autocompletion**, **inline errors**, etc:
## Benefits { #benefits }
When using the automatically generated clients, you would get **autocompletion** for:
* Methods.
* Request payloads in the body, query parameters, etc.
* Response payloads.
You would also have **inline errors** for everything.
And whenever you update the backend code, and **regenerate** the frontend, it would have any new *path operations* available as methods, the old ones removed, and any other change would be reflected on the generated code. 🤓
This also means that if something changed, it will be **reflected** on the client code automatically. And if you **build** the client, it will error out if you have any **mismatch** in the data used.
So, you would **detect many errors** very early in the development cycle instead of having to wait for the errors to show up to your final users in production and then trying to debug where the problem is. ✨
================================================
FILE: docs/en/docs/advanced/index.md
================================================
# Advanced User Guide { #advanced-user-guide }
## Additional Features { #additional-features }
The main [Tutorial - User Guide](../tutorial/index.md) should be enough to give you a tour through all the main features of **FastAPI**.
In the next sections you will see other options, configurations, and additional features.
/// tip
The next sections are **not necessarily "advanced"**.
And it's possible that for your use case, the solution is in one of them.
///
## Read the Tutorial first { #read-the-tutorial-first }
You could still use most of the features in **FastAPI** with the knowledge from the main [Tutorial - User Guide](../tutorial/index.md).
And the next sections assume you already read it, and assume that you know those main ideas.
================================================
FILE: docs/en/docs/advanced/json-base64-bytes.md
================================================
# JSON with Bytes as Base64 { #json-with-bytes-as-base64 }
If your app needs to receive and send JSON data, but you need to include binary data in it, you can encode it as base64.
## Base64 vs Files { #base64-vs-files }
Consider first if you can use [Request Files](../tutorial/request-files.md) for uploading binary data and [Custom Response - FileResponse](./custom-response.md#fileresponse--fileresponse-) for sending binary data, instead of encoding it in JSON.
JSON can only contain UTF-8 encoded strings, so it can't contain raw bytes.
Base64 can encode binary data in strings, but to do it, it needs to use more characters than the original binary data, so it would normally be less efficient than regular files.
Use base64 only if you definitely need to include binary data in JSON, and you can't use files for that.
## Pydantic `bytes` { #pydantic-bytes }
You can declare a Pydantic model with `bytes` fields, and then use `val_json_bytes` in the model config to tell it to use base64 to *validate* input JSON data, as part of that validation it will decode the base64 string into bytes.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
If you check the `/docs`, they will show that the field `data` expects base64 encoded bytes:
You could send a request like:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip
`aGVsbG8=` is the base64 encoding of `hello`.
///
And then Pydantic will decode the base64 string and give you the original bytes in the `data` field of the model.
You will receive a response like:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` for Output Data { #pydantic-bytes-for-output-data }
You can also use `bytes` fields with `ser_json_bytes` in the model config for output data, and Pydantic will *serialize* the bytes as base64 when generating the JSON response.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` for Input and Output Data { #pydantic-bytes-for-input-and-output-data }
And of course, you can use the same model configured to use base64 to handle both input (*validate*) with `val_json_bytes` and output (*serialize*) with `ser_json_bytes` when receiving and sending JSON data.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/en/docs/advanced/middleware.md
================================================
# Advanced Middleware { #advanced-middleware }
In the main tutorial you read how to add [Custom Middleware](../tutorial/middleware.md) to your application.
And then you also read how to handle [CORS with the `CORSMiddleware`](../tutorial/cors.md).
In this section we'll see how to use other middlewares.
## Adding ASGI middlewares { #adding-asgi-middlewares }
As **FastAPI** is based on Starlette and implements the ASGI specification, you can use any ASGI middleware.
A middleware doesn't have to be made for FastAPI or Starlette to work, as long as it follows the ASGI spec.
In general, ASGI middlewares are classes that expect to receive an ASGI app as the first argument.
So, in the documentation for third-party ASGI middlewares they will probably tell you to do something like:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares handle server errors and custom exception handlers work properly.
For that, you use `app.add_middleware()` (as in the example for CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` receives a middleware class as the first argument and any additional arguments to be passed to the middleware.
## Integrated middlewares { #integrated-middlewares }
**FastAPI** includes several middlewares for common use cases, we'll see next how to use them.
/// note | Technical Details
For the next examples, you could also use `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Enforces that all incoming requests must either be `https` or `wss`.
Any incoming request to `http` or `ws` will be redirected to the secure scheme instead.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
The following arguments are supported:
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains. To allow any hostname either use `allowed_hosts=["*"]` or omit the middleware.
* `www_redirect` - If set to True, requests to non-www versions of the allowed hosts will be redirected to their www counterparts. Defaults to `True`.
If an incoming request does not validate correctly then a `400` response will be sent.
## `GZipMiddleware` { #gzipmiddleware }
Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header.
The middleware will handle both standard and streaming responses.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
The following arguments are supported:
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
* `compresslevel` - Used during GZip compression. It is an integer ranging from 1 to 9. Defaults to `9`. Lower value results in faster compression but larger file sizes, while higher value results in slower compression but smaller file sizes.
## Other middlewares { #other-middlewares }
There are many other ASGI middlewares.
For example:
* [Uvicorn's `ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
To see other available middlewares check [Starlette's Middleware docs](https://www.starlette.dev/middleware/) and the [ASGI Awesome List](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/en/docs/advanced/openapi-callbacks.md
================================================
# OpenAPI Callbacks { #openapi-callbacks }
You could create an API with a *path operation* that could trigger a request to an *external API* created by someone else (probably the same developer that would be *using* your API).
The process that happens when your API app calls the *external API* is named a "callback". Because the software that the external developer wrote sends a request to your API and then your API *calls back*, sending a request to an *external API* (that was probably created by the same developer).
In this case, you could want to document how that external API *should* look like. What *path operation* it should have, what body it should expect, what response it should return, etc.
## An app with callbacks { #an-app-with-callbacks }
Let's see all this with an example.
Imagine you develop an app that allows creating invoices.
These invoices will have an `id`, `title` (optional), `customer`, and `total`.
The user of your API (an external developer) will create an invoice in your API with a POST request.
Then your API will (let's imagine):
* Send the invoice to some customer of the external developer.
* Collect the money.
* Send a notification back to the API user (the external developer).
* This will be done by sending a POST request (from *your API*) to some *external API* provided by that external developer (this is the "callback").
## The normal **FastAPI** app { #the-normal-fastapi-app }
Let's first see how the normal API app would look like before adding the callback.
It will have a *path operation* that will receive an `Invoice` body, and a query parameter `callback_url` that will contain the URL for the callback.
This part is pretty normal, most of the code is probably already familiar to you:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip
The `callback_url` query parameter uses a Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/) type.
///
The only new thing is the `callbacks=invoices_callback_router.routes` as an argument to the *path operation decorator*. We'll see what that is next.
## Documenting the callback { #documenting-the-callback }
The actual callback code will depend heavily on your own API app.
And it will probably vary a lot from one app to the next.
It could be just one or two lines of code, like:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
But possibly the most important part of the callback is making sure that your API user (the external developer) implements the *external API* correctly, according to the data that *your API* is going to send in the request body of the callback, etc.
So, what we will do next is add the code to document how that *external API* should look like to receive the callback from *your API*.
That documentation will show up in the Swagger UI at `/docs` in your API, and it will let external developers know how to build the *external API*.
This example doesn't implement the callback itself (that could be just a line of code), only the documentation part.
/// tip
The actual callback is just an HTTP request.
When implementing the callback yourself, you could use something like [HTTPX](https://www.python-httpx.org) or [Requests](https://requests.readthedocs.io/).
///
## Write the callback documentation code { #write-the-callback-documentation-code }
This code won't be executed in your app, we only need it to *document* how that *external API* should look like.
But, you already know how to easily create automatic documentation for an API with **FastAPI**.
So we are going to use that same knowledge to document how the *external API* should look like... by creating the *path operation(s)* that the external API should implement (the ones your API will call).
/// tip
When writing the code to document a callback, it might be useful to imagine that you are that *external developer*. And that you are currently implementing the *external API*, not *your API*.
Temporarily adopting this point of view (of the *external developer*) can help you feel like it's more obvious where to put the parameters, the Pydantic model for the body, for the response, etc. for that *external API*.
///
### Create a callback `APIRouter` { #create-a-callback-apirouter }
First create a new `APIRouter` that will contain one or more callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Create the callback *path operation* { #create-the-callback-path-operation }
To create the callback *path operation* use the same `APIRouter` you created above.
It should look just like a normal FastAPI *path operation*:
* It should probably have a declaration of the body it should receive, e.g. `body: InvoiceEvent`.
* And it could also have a declaration of the response it should return, e.g. `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
There are 2 main differences from a normal *path operation*:
* It doesn't need to have any actual code, because your app will never call this code. It's only used to document the *external API*. So, the function could just have `pass`.
* The *path* can contain an [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (see more below) where it can use variables with parameters and parts of the original request sent to *your API*.
### The callback path expression { #the-callback-path-expression }
The callback *path* can have an [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) that can contain parts of the original request sent to *your API*.
In this case, it's the `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
So, if your API user (the external developer) sends a request to *your API* to:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
with a JSON body of:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
then *your API* will process the invoice, and at some point later, send a callback request to the `callback_url` (the *external API*):
```
https://www.external.org/events/invoices/2expen51ve
```
with a JSON body containing something like:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
and it would expect a response from that *external API* with a JSON body like:
```JSON
{
"ok": true
}
```
/// tip
Notice how the callback URL used contains the URL received as a query parameter in `callback_url` (`https://www.external.org/events`) and also the invoice `id` from inside of the JSON body (`2expen51ve`).
///
### Add the callback router { #add-the-callback-router }
At this point you have the *callback path operation(s)* needed (the one(s) that the *external developer* should implement in the *external API*) in the callback router you created above.
Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip
Notice that you are not passing the router itself (`invoices_callback_router`) to `callback=`, but the attribute `.routes`, as in `invoices_callback_router.routes`.
///
### Check the docs { #check-the-docs }
Now you can start your app and go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see your docs including a "Callbacks" section for your *path operation* that shows how the *external API* should look like:
================================================
FILE: docs/en/docs/advanced/openapi-webhooks.md
================================================
# OpenAPI Webhooks { #openapi-webhooks }
There are cases where you want to tell your API **users** that your app could call *their* app (sending a request) with some data, normally to **notify** of some type of **event**.
This means that instead of the normal process of your users sending requests to your API, it's **your API** (or your app) that could **send requests to their system** (to their API, their app).
This is normally called a **webhook**.
## Webhooks steps { #webhooks-steps }
The process normally is that **you define** in your code what is the message that you will send, the **body of the request**.
You also define in some way at which **moments** your app will send those requests or events.
And **your users** define in some way (for example in a web dashboard somewhere) the **URL** where your app should send those requests.
All the **logic** about how to register the URLs for webhooks and the code to actually send those requests is up to you. You write it however you want to in **your own code**.
## Documenting webhooks with **FastAPI** and OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
With **FastAPI**, using OpenAPI, you can define the names of these webhooks, the types of HTTP operations that your app can send (e.g. `POST`, `PUT`, etc.) and the request **bodies** that your app would send.
This can make it a lot easier for your users to **implement their APIs** to receive your **webhook** requests, they might even be able to autogenerate some of their own API code.
/// info
Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0` and above.
///
## An app with webhooks { #an-app-with-webhooks }
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.
/// info
The `app.webhooks` object is actually just an `APIRouter`, the same type you would use when structuring your app with multiple files.
///
Notice that with webhooks you are actually not declaring a *path* (like `/items/`), the text you pass there is just an **identifier** of the webhook (the name of the event), for example in `@app.webhooks.post("new-subscription")`, the webhook name is `new-subscription`.
This is because it is expected that **your users** would define the actual **URL path** where they want to receive the webhook request in some other way (e.g. a web dashboard).
### Check the docs { #check-the-docs }
Now you can start your app and go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see your docs have the normal *path operations* and now also some **webhooks**:
================================================
FILE: docs/en/docs/advanced/path-operation-advanced-configuration.md
================================================
# Path Operation Advanced Configuration { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning
If you are not an "expert" in OpenAPI, you probably don't need this.
///
You can set the OpenAPI `operationId` to be used in your *path operation* with the parameter `operation_id`.
You would have to make sure that it is unique for each operation.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Using the *path operation function* name as the operationId { #using-the-path-operation-function-name-as-the-operationid }
If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`.
You should do it after adding all your *path operations*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip
If you manually call `app.openapi()`, you should update the `operationId`s before that.
///
/// warning
If you do this, you have to make sure each one of your *path operation functions* has a unique name.
Even if they are in different modules (Python files).
///
## Exclude from OpenAPI { #exclude-from-openapi }
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Advanced description from docstring { #advanced-description-from-docstring }
You can limit the lines used from the docstring of a *path operation function* for OpenAPI.
Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate the output used for OpenAPI at this point.
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Additional Responses { #additional-responses }
You probably have seen how to declare the `response_model` and `status_code` for a *path operation*.
That defines the metadata about the main response of a *path operation*.
You can also declare additional responses with their models, status codes, etc.
There's a whole chapter here in the documentation about it, you can read it at [Additional Responses in OpenAPI](additional-responses.md).
## OpenAPI Extra { #openapi-extra }
When you declare a *path operation* in your application, **FastAPI** automatically generates the relevant metadata about that *path operation* to be included in the OpenAPI schema.
/// note | Technical details
In the OpenAPI specification it is called the [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
It has all the information about the *path operation* and is used to generate the automatic documentation.
It includes the `tags`, `parameters`, `requestBody`, `responses`, etc.
This *path operation*-specific OpenAPI schema is normally generated automatically by **FastAPI**, but you can also extend it.
/// tip
This is a low level extension point.
If you only need to declare additional responses, a more convenient way to do it is with [Additional Responses in OpenAPI](additional-responses.md).
///
You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`.
### OpenAPI Extensions { #openapi-extensions }
This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will see your extension as part of the specific *path operation* too:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Custom OpenAPI *path operation* schema { #custom-openapi-path-operation-schema }
The dictionary in `openapi_extra` will be deeply merged with the automatically generated OpenAPI schema for the *path operation*.
So, you could add additional data to the automatically generated schema.
For example, you could decide to read and validate the request with your own code, without using the automatic features of FastAPI with Pydantic, but you could still want to define the request in the OpenAPI schema.
You could do that with `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
In this example, we didn't declare any Pydantic model. In fact, the request body is not even parsed as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
Nevertheless, we can declare the expected schema for the request body.
### Custom OpenAPI content type { #custom-openapi-content-type }
Using this same trick, you could use a Pydantic model to define the JSON Schema that is then included in the custom OpenAPI schema section for the *path operation*.
And you could do this even if the data type in the request is not JSON.
For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML.
Then we use the request directly, and extract the body as `bytes`. This means that FastAPI won't even try to parse the request payload as JSON.
And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip
Here we reuse the same Pydantic model.
But the same way, we could have validated it in some other way.
///
================================================
FILE: docs/en/docs/advanced/response-change-status-code.md
================================================
# Response - Change Status Code { #response-change-status-code }
You probably read before that you can set a default [Response Status Code](../tutorial/response-status-code.md).
But in some cases you need to return a different status code than the default.
## Use case { #use-case }
For example, imagine that you want to return an HTTP status code of "OK" `200` by default.
But if the data didn't exist, you want to create it, and return an HTTP status code of "CREATED" `201`.
But you still want to be able to filter and convert the data you return with a `response_model`.
For those cases, you can use a `Response` parameter.
## Use a `Response` parameter { #use-a-response-parameter }
You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies and headers).
And then you can set the `status_code` in that *temporal* response object.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
And if you declared a `response_model`, it will still be used to filter and convert the object you returned.
**FastAPI** will use that *temporal* response to extract the status code (also cookies and headers), and will put them in the final response that contains the value you returned, filtered by any `response_model`.
You can also declare the `Response` parameter in dependencies, and set the status code in them. But keep in mind that the last one to be set will win.
================================================
FILE: docs/en/docs/advanced/response-cookies.md
================================================
# Response Cookies { #response-cookies }
## Use a `Response` parameter { #use-a-response-parameter }
You can declare a parameter of type `Response` in your *path operation function*.
And then you can set cookies in that *temporal* response object.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
And if you declared a `response_model`, it will still be used to filter and convert the object you returned.
**FastAPI** will use that *temporal* response to extract the cookies (also headers and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`.
You can also declare the `Response` parameter in dependencies, and set cookies (and headers) in them.
## Return a `Response` directly { #return-a-response-directly }
You can also create cookies when returning a `Response` directly in your code.
To do that, you can create a response as described in [Return a Response Directly](response-directly.md).
Then set Cookies in it, and then return it:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip
Keep in mind that if you return a response directly instead of using the `Response` parameter, FastAPI will return it directly.
So, you will have to make sure your data is of the correct type. E.g. it is compatible with JSON, if you are returning a `JSONResponse`.
And also that you are not sending any data that should have been filtered by a `response_model`.
///
### More info { #more-info }
/// note | Technical Details
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
///
To see all the available parameters and options, check the [documentation in Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/en/docs/advanced/response-directly.md
================================================
# Return a Response Directly { #return-a-response-directly }
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
If you declare a [Response Model](../tutorial/response-model.md) FastAPI will use it to serialize the data to JSON, using Pydantic.
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md) and put it in a `JSONResponse`.
You could also create a `JSONResponse` directly and return it.
/// tip
You will normally have much better performance using a [Response Model](../tutorial/response-model.md) than returning a `JSONResponse` directly, as that way it serializes the data using Pydantic, in Rust.
///
## Return a `Response` { #return-a-response }
You can return a `Response` or any sub-class of it.
/// info
`JSONResponse` itself is a sub-class of `Response`.
///
And when you return a `Response`, **FastAPI** will pass it directly.
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
This gives you a lot of **flexibility**. You can return any data type, override any data declaration or validation, etc.
It also gives you a lot of **responsibility**. You have to make sure that the data you return is correct, in the correct format, that it can be serialized, etc.
## Using the `jsonable_encoder` in a `Response` { #using-the-jsonable-encoder-in-a-response }
Because **FastAPI** doesn't make any changes to a `Response` you return, you have to make sure its contents are ready for it.
For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types.
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Technical Details
You could also use `from starlette.responses import JSONResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
///
## Returning a custom `Response` { #returning-a-custom-response }
The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default.
Now, let's see how you could use that to return a custom response.
Let's say that you want to return an [XML](https://en.wikipedia.org/wiki/XML) response.
You could put your XML content in a string, put that in a `Response`, and return it:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## How a Response Model Works { #how-a-response-model-works }
When you declare a [Response Model - Return Type](../tutorial/response-model.md) in a path operation, **FastAPI** will use it to serialize the data to JSON, using Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
As that will happen on the Rust side, the performance will be much better than if it was done with regular Python and the `JSONResponse` class.
When using a `response_model` or return type, FastAPI won't use the `jsonable_encoder` to convert the data (which would be slower) nor the `JSONResponse` class.
Instead it takes the JSON bytes generated with Pydantic using the response model (or return type) and returns a `Response` with the right media type for JSON directly (`application/json`).
## Notes { #notes }
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.
But you can still document it as described in [Additional Responses in OpenAPI](additional-responses.md).
You can see in later sections how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc.
================================================
FILE: docs/en/docs/advanced/response-headers.md
================================================
# Response Headers { #response-headers }
## Use a `Response` parameter { #use-a-response-parameter }
You can declare a parameter of type `Response` in your *path operation function* (as you can do for cookies).
And then you can set headers in that *temporal* response object.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
And if you declared a `response_model`, it will still be used to filter and convert the object you returned.
**FastAPI** will use that *temporal* response to extract the headers (also cookies and status code), and will put them in the final response that contains the value you returned, filtered by any `response_model`.
You can also declare the `Response` parameter in dependencies, and set headers (and cookies) in them.
## Return a `Response` directly { #return-a-response-directly }
You can also add headers when you return a `Response` directly.
Create a response as described in [Return a Response Directly](response-directly.md) and pass the headers as an additional parameter:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Technical Details
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
///
## Custom Headers { #custom-headers }
Keep in mind that custom proprietary headers can be added [using the `X-` prefix](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations (read more in [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), using the parameter `expose_headers` documented in [Starlette's CORS docs](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/en/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
For the simplest cases, you can use HTTP Basic Auth.
In HTTP Basic Auth, the application expects a header that contains a username and a password.
If it doesn't receive it, it returns an HTTP 401 "Unauthorized" error.
And returns a header `WWW-Authenticate` with a value of `Basic`, and an optional `realm` parameter.
That tells the browser to show the integrated prompt for a username and password.
Then, when you type that username and password, the browser sends them in the header automatically.
## Simple HTTP Basic Auth { #simple-http-basic-auth }
* Import `HTTPBasic` and `HTTPBasicCredentials`.
* Create a "`security` scheme" using `HTTPBasic`.
* Use that `security` with a dependency in your *path operation*.
* It returns an object of type `HTTPBasicCredentials`:
* It contains the `username` and `password` sent.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
## Check the username { #check-the-username }
Here's a more complete example.
Use a dependency to check if the username and password are correct.
For this, use the Python standard module [`secrets`](https://docs.python.org/3/library/secrets.html) to check the username and password.
`secrets.compare_digest()` needs to take `bytes` or a `str` that only contains ASCII characters (the ones in English), this means it wouldn't work with characters like `á`, as in `Sebastián`.
To handle that, we first convert the `username` and `password` to `bytes` encoding them with UTF-8.
Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
This would be similar to:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Return some error
...
```
But by using the `secrets.compare_digest()` it will be secure against a type of attacks called "timing attacks".
### Timing Attacks { #timing-attacks }
But what's a "timing attack"?
Let's imagine some attackers are trying to guess the username and password.
And they send a request with a username `johndoe` and a password `love123`.
Then the Python code in your application would be equivalent to something like:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
But right at the moment Python compares the first `j` in `johndoe` to the first `s` in `stanleyjobson`, it will return `False`, because it already knows that those two strings are not the same, thinking that "there's no need to waste more computation comparing the rest of the letters". And your application will say "Incorrect username or password".
But then the attackers try with username `stanleyjobsox` and password `love123`.
And your application code does something like:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python will have to compare the whole `stanleyjobso` in both `stanleyjobsox` and `stanleyjobson` before realizing that both strings are not the same. So it will take some extra microseconds to reply back "Incorrect username or password".
#### The time to answer helps the attackers { #the-time-to-answer-helps-the-attackers }
At that point, by noticing that the server took some microseconds longer to send the "Incorrect username or password" response, the attackers will know that they got _something_ right, some of the initial letters were right.
And then they can try again knowing that it's probably something more similar to `stanleyjobsox` than to `johndoe`.
#### A "professional" attack { #a-professional-attack }
Of course, the attackers would not try all this by hand, they would write a program to do it, possibly with thousands or millions of tests per second. And they would get just one extra correct letter at a time.
But doing that, in some minutes or hours the attackers would have guessed the correct username and password, with the "help" of our application, just using the time taken to answer.
#### Fix it with `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
But in our code we are actually using `secrets.compare_digest()`.
In short, it will take the same time to compare `stanleyjobsox` to `stanleyjobson` than it takes to compare `johndoe` to `stanleyjobson`. And the same for the password.
That way, using `secrets.compare_digest()` in your application code, it will be safe against this whole range of security attacks.
### Return the error { #return-the-error }
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/en/docs/advanced/security/index.md
================================================
# Advanced Security { #advanced-security }
## Additional Features { #additional-features }
There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/index.md).
/// tip
The next sections are **not necessarily "advanced"**.
And it's possible that for your use case, the solution is in one of them.
///
## Read the Tutorial first { #read-the-tutorial-first }
The next sections assume you already read the main [Tutorial - User Guide: Security](../../tutorial/security/index.md).
They are all based on the same concepts, but allow some extra functionalities.
================================================
FILE: docs/en/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2 scopes { #oauth2-scopes }
You can use OAuth2 scopes directly with **FastAPI**, they are integrated to work seamlessly.
This would allow you to have a more fine-grained permission system, following the OAuth2 standard, integrated into your OpenAPI application (and the API docs).
OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, X (Twitter), etc. They use it to provide specific permissions to users and applications.
Every time you "log in with" Facebook, Google, GitHub, Microsoft, X (Twitter), that application is using OAuth2 with scopes.
In this section you will see how to manage authentication and authorization with the same OAuth2 with scopes in your **FastAPI** application.
/// warning
This is a more or less advanced section. If you are just starting, you can skip it.
You don't necessarily need OAuth2 scopes, and you can handle authentication and authorization however you want.
But OAuth2 with scopes can be nicely integrated into your API (with OpenAPI) and your API docs.
Nevertheless, you still enforce those scopes, or any other security/authorization requirement, however you need, in your code.
In many cases, OAuth2 with scopes can be an overkill.
But if you know you need it, or you are curious, keep reading.
///
## OAuth2 scopes and OpenAPI { #oauth2-scopes-and-openapi }
The OAuth2 specification defines "scopes" as a list of strings separated by spaces.
The content of each of these strings can have any format, but should not contain spaces.
These scopes represent "permissions".
In OpenAPI (e.g. the API docs), you can define "security schemes".
When one of these security schemes uses OAuth2, you can also declare and use scopes.
Each "scope" is just a string (without spaces).
They are normally used to declare specific security permissions, for example:
* `users:read` or `users:write` are common examples.
* `instagram_basic` is used by Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` is used by Google.
/// info
In OAuth2 a "scope" is just a string that declares a specific permission required.
It doesn't matter if it has other characters like `:` or if it is a URL.
Those details are implementation specific.
For OAuth2 they are just strings.
///
## Global view { #global-view }
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md). Now using OAuth2 scopes:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Now let's review those changes step by step.
## OAuth2 Security scheme { #oauth2-security-scheme }
The first change is that now we are declaring the OAuth2 security scheme with two available scopes, `me` and `items`.
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
And you will be able to select which scopes you want to give access to: `me` and `items`.
This is the same mechanism used when you give permissions while logging in with Facebook, Google, GitHub, etc:
## JWT token with scopes { #jwt-token-with-scopes }
Now, modify the token *path operation* to return the scopes requested.
We are still using the same `OAuth2PasswordRequestForm`. It includes a property `scopes` with a `list` of `str`, with each scope it received in the request.
And we return the scopes as part of the JWT token.
/// danger
For simplicity, here we are just adding the scopes received directly to the token.
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Declare scopes in *path operations* and dependencies { #declare-scopes-in-path-operations-and-dependencies }
Now we declare that the *path operation* for `/users/me/items/` requires the scope `items`.
For this, we import and use `Security` from `fastapi`.
You can use `Security` to declare dependencies (just like `Depends`), but `Security` also receives a parameter `scopes` with a list of scopes (strings).
In this case, we pass a dependency function `get_current_active_user` to `Security` (the same way we would do with `Depends`).
But we also pass a `list` of scopes, in this case with just one scope: `items` (it could have more).
And the dependency function `get_current_active_user` can also declare sub-dependencies, not only with `Depends` but also with `Security`. Declaring its own sub-dependency function (`get_current_user`), and more scope requirements.
In this case, it requires the scope `me` (it could require more than one scope).
/// note
You don't necessarily need to add different scopes in different places.
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Technical Details
`Security` is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
But by using `Security` instead of `Depends`, **FastAPI** will know that it can declare security scopes, use them internally, and document the API with OpenAPI.
But when you import `Query`, `Path`, `Depends`, `Security` and others from `fastapi`, those are actually functions that return special classes.
///
## Use `SecurityScopes` { #use-securityscopes }
Now update the dependency `get_current_user`.
This is the one used by the dependencies above.
Here's where we are using the same OAuth2 scheme we created before, declaring it as a dependency: `oauth2_scheme`.
Because this dependency function doesn't have any scope requirements itself, we can use `Depends` with `oauth2_scheme`, we don't have to use `Security` when we don't need to specify security scopes.
We also declare a special parameter of type `SecurityScopes`, imported from `fastapi.security`.
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Use the `scopes` { #use-the-scopes }
The parameter `security_scopes` will be of type `SecurityScopes`.
It will have a property `scopes` with a list containing all the scopes required by itself and all the dependencies that use this as a sub-dependency. That means, all the "dependants"... this might sound confusing, it is explained again later below.
The `security_scopes` object (of class `SecurityScopes`) also provides a `scope_str` attribute with a single string, containing those scopes separated by spaces (we are going to use it).
We create an `HTTPException` that we can reuse (`raise`) later at several points.
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in the `WWW-Authenticate` header (this is part of the spec).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Verify the `username` and data shape { #verify-the-username-and-data-shape }
We verify that we get a `username`, and extract the scopes.
And then we validate that data with the Pydantic model (catching the `ValidationError` exception), and if we get an error reading the JWT token or validating the data with Pydantic, we raise the `HTTPException` we created before.
For that, we update the Pydantic model `TokenData` with a new property `scopes`.
By validating the data with Pydantic we can make sure that we have, for example, exactly a `list` of `str` with the scopes and a `str` with the `username`.
Instead of, for example, a `dict`, or something else, as it could break the application at some point later, making it a security risk.
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Verify the `scopes` { #verify-the-scopes }
We now verify that all the scopes required, by this dependency and all the dependants (including *path operations*), are included in the scopes provided in the token received, otherwise raise an `HTTPException`.
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Dependency tree and scopes { #dependency-tree-and-scopes }
Let's review again this dependency tree and the scopes.
As the `get_current_active_user` dependency has as a sub-dependency on `get_current_user`, the scope `"me"` declared at `get_current_active_user` will be included in the list of required scopes in the `security_scopes.scopes` passed to `get_current_user`.
The *path operation* itself also declares a scope, `"items"`, so this will also be in the list of `security_scopes.scopes` passed to `get_current_user`.
Here's how the hierarchy of dependencies and scopes looks like:
* The *path operation* `read_own_items` has:
* Required scopes `["items"]` with the dependency:
* `get_current_active_user`:
* The dependency function `get_current_active_user` has:
* Required scopes `["me"]` with the dependency:
* `get_current_user`:
* The dependency function `get_current_user` has:
* No scopes required by itself.
* A dependency using `oauth2_scheme`.
* A `security_scopes` parameter of type `SecurityScopes`:
* This `security_scopes` parameter has a property `scopes` with a `list` containing all these scopes declared above, so:
* `security_scopes.scopes` will contain `["me", "items"]` for the *path operation* `read_own_items`.
* `security_scopes.scopes` will contain `["me"]` for the *path operation* `read_users_me`, because it is declared in the dependency `get_current_active_user`.
* `security_scopes.scopes` will contain `[]` (nothing) for the *path operation* `read_system_status`, because it didn't declare any `Security` with `scopes`, and its dependency, `get_current_user`, doesn't declare any `scopes` either.
/// tip
The important and "magic" thing here is that `get_current_user` will have a different list of `scopes` to check for each *path operation*.
All depending on the `scopes` declared in each *path operation* and each dependency in the dependency tree for that specific *path operation*.
///
## More details about `SecurityScopes` { #more-details-about-securityscopes }
You can use `SecurityScopes` at any point, and in multiple places, it doesn't have to be at the "root" dependency.
It will always have the security scopes declared in the current `Security` dependencies and all the dependants for **that specific** *path operation* and **that specific** dependency tree.
Because the `SecurityScopes` will have all the scopes declared by dependants, you can use it to verify that a token has the required scopes in a central dependency function, and then declare different scope requirements in different *path operations*.
They will be checked independently for each *path operation*.
## Check it { #check-it }
If you open the API docs, you can authenticate and specify which scopes you want to authorize.
If you don't select any scope, you will be "authenticated", but when you try to access `/users/me/` or `/users/me/items/` you will get an error saying that you don't have enough permissions. You will still be able to access `/status/`.
And if you select the scope `me` but not the scope `items`, you will be able to access `/users/me/` but not `/users/me/items/`.
That's what would happen to a third party application that tried to access one of these *path operations* with a token provided by a user, depending on how many permissions the user gave the application.
## About third party integrations { #about-third-party-integrations }
In this example we are using the OAuth2 "password" flow.
This is appropriate when we are logging in to our own application, probably with our own frontend.
Because we can trust it to receive the `username` and `password`, as we control it.
But if you are building an OAuth2 application that others would connect to (i.e., if you are building an authentication provider equivalent to Facebook, Google, GitHub, etc.) you should use one of the other flows.
The most common is the implicit flow.
The most secure is the code flow, but it's more complex to implement as it requires more steps. As it is more complex, many providers end up suggesting the implicit flow.
/// note
It's common that each authentication provider names their flows in a different way, to make it part of their brand.
But in the end, they are implementing the same OAuth2 standard.
///
**FastAPI** includes utilities for all these OAuth2 authentication flows in `fastapi.security.oauth2`.
## `Security` in decorator `dependencies` { #security-in-decorator-dependencies }
The same way you can define a `list` of `Depends` in the decorator's `dependencies` parameter (as explained in [Dependencies in path operation decorators](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), you could also use `Security` with `scopes` there.
================================================
FILE: docs/en/docs/advanced/settings.md
================================================
# Settings and Environment Variables { #settings-and-environment-variables }
In many cases your application could need some external settings or configurations, for example secret keys, database credentials, credentials for email services, etc.
Most of these settings are variable (can change), like database URLs. And many could be sensitive, like secrets.
For this reason it's common to provide them in environment variables that are read by the application.
/// tip
To understand environment variables you can read [Environment Variables](../environment-variables.md).
///
## Types and validation { #types-and-validation }
These environment variables can only handle text strings, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS).
That means that any value read in Python from an environment variable will be a `str`, and any conversion to a different type or any validation has to be done in code.
## Pydantic `Settings` { #pydantic-settings }
Fortunately, Pydantic provides a great utility to handle these settings coming from environment variables with [Pydantic: Settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Install `pydantic-settings` { #install-pydantic-settings }
First, make sure you create your [virtual environment](../virtual-environments.md), activate it, and then install the `pydantic-settings` package:
### Create the `Settings` object { #create-the-settings-object }
Import `BaseSettings` from Pydantic and create a sub-class, very much like with a Pydantic model.
The same way as with Pydantic models, you declare class attributes with type annotations, and possibly default values.
You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip
If you want something quick to copy and paste, don't use this example, use the last one below.
///
Then, when you create an instance of that `Settings` class (in this case, in the `settings` object), Pydantic will read the environment variables in a case-insensitive way, so, an upper-case variable `APP_NAME` will still be read for the attribute `app_name`.
Next it will convert and validate the data. So, when you use that `settings` object, you will have data of the types you declared (e.g. `items_per_user` will be an `int`).
### Use the `settings` { #use-the-settings }
Then you can use the new `settings` object in your application:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Run the server { #run-the-server }
Next, you would run the server passing the configurations as environment variables, for example you could set an `ADMIN_EMAIL` and `APP_NAME` with:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip
To set multiple env vars for a single command just separate them with a space, and put them all before the command.
///
And then the `admin_email` setting would be set to `"deadpool@example.com"`.
The `app_name` would be `"ChimichangApp"`.
And the `items_per_user` would keep its default value of `50`.
## Settings in another module { #settings-in-another-module }
You could put those settings in another module file as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md).
For example, you could have a file `config.py` with:
{* ../../docs_src/settings/app01_py310/config.py *}
And then use it in a file `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip
You would also need a file `__init__.py` as you saw in [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md).
///
## Settings in a dependency { #settings-in-a-dependency }
In some occasions it might be useful to provide the settings from a dependency, instead of having a global object with `settings` that is used everywhere.
This could be especially useful during testing, as it's very easy to override a dependency with your own custom settings.
### The config file { #the-config-file }
Coming from the previous example, your `config.py` file could look like:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Notice that now we don't create a default instance `settings = Settings()`.
### The main app file { #the-main-app-file }
Now we create a dependency that returns a new `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip
We'll discuss the `@lru_cache` in a bit.
For now you can assume `get_settings()` is a normal function.
///
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Settings and testing { #settings-and-testing }
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object.
Then we can test that it is used.
## Reading a `.env` file { #reading-a-env-file }
If you have many settings that possibly change a lot, maybe in different environments, it might be useful to put them on a file and then read them from it as if they were environment variables.
This practice is common enough that it has a name, these environment variables are commonly placed in a file `.env`, and the file is called a "dotenv".
/// tip
A file starting with a dot (`.`) is a hidden file in Unix-like systems, like Linux and macOS.
But a dotenv file doesn't really have to have that exact filename.
///
Pydantic has support for reading from these types of files using an external library. You can read more at [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip
For this to work, you need to `pip install python-dotenv`.
///
### The `.env` file { #the-env-file }
You could have a `.env` file with:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Read settings from `.env` { #read-settings-from-env }
And then update your `config.py` with:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip
The `model_config` attribute is used just for Pydantic configuration. You can read more at [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Here we define the config `env_file` inside of your Pydantic `Settings` class, and set the value to the filename with the dotenv file we want to use.
### Creating the `Settings` only once with `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Reading a file from disk is normally a costly (slow) operation, so you probably want to do it only once and then reuse the same settings object, instead of reading it for each request.
But every time we do:
```Python
Settings()
```
a new `Settings` object would be created, and at creation it would read the `.env` file again.
If the dependency function was just like:
```Python
def get_settings():
return Settings()
```
we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️
But as we are using the `@lru_cache` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Then for any subsequent call of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.
#### `lru_cache` Technical Details { #lru-cache-technical-details }
`@lru_cache` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time.
So, the function below it will be executed once for each combination of arguments. And then the values returned by each of those combinations of arguments will be used again and again whenever the function is called with exactly the same combination of arguments.
For example, if you have a function:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
your program could execute like this:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
In the case of our dependency `get_settings()`, the function doesn't even take any arguments, so it always returns the same value.
That way, it behaves almost as if it was just a global variable. But as it uses a dependency function, then we can override it easily for testing.
`@lru_cache` is part of `functools` which is part of Python's standard library, you can read more about it in the [Python docs for `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Recap { #recap }
You can use Pydantic Settings to handle the settings or configurations for your application, with all the power of Pydantic models.
* By using a dependency you can simplify testing.
* You can use `.env` files with it.
* Using `@lru_cache` lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing.
================================================
FILE: docs/en/docs/advanced/stream-data.md
================================================
# Stream Data { #stream-data }
If you want to stream data that can be structured as JSON, you should [Stream JSON Lines](../tutorial/stream-json-lines.md).
But if you want to **stream pure binary data** or strings, here's how you can do it.
/// info
Added in FastAPI 0.134.0.
///
## Use Cases { #use-cases }
You could use this if you want to stream pure strings, for example directly from the output of an **AI LLM** service.
You could also use it to stream **large binary files**, where you stream each chunk of data as you read it, without having to read it all in memory at once.
You could also stream **video** or **audio** this way, it could even be generated as you process and send it.
## A `StreamingResponse` with `yield` { #a-streamingresponse-with-yield }
If you declare a `response_class=StreamingResponse` in your *path operation function*, you can use `yield` to send each chunk of data in turn.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI will give each chunk of data to the `StreamingResponse` as is, it won't try to convert it to JSON or anything similar.
### Non-async *path operation functions* { #non-async-path-operation-functions }
You can also use regular `def` functions (without `async`), and use `yield` the same way.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### No Annotation { #no-annotation }
You don't really need to declare the return type annotation for streaming binary data.
As FastAPI will not try to convert the data to JSON with Pydantic or serialize it in any way, in this case, the type annotation is only for your editor and tools to use, it won't be used by FastAPI.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
This also means that with `StreamingResponse` you have the **freedom** and **responsibility** to produce and encode the data bytes exactly as you need them to be sent, independent of the type annotations. 🤓
### Stream Bytes { #stream-bytes }
One of the main use cases would be to stream `bytes` instead of strings, you can of course do it.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## A Custom `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
In the examples above, the data bytes were streamed, but the response didn't have a `Content-Type` header, so the client didn't know what type of data it was receiving.
You can create a custom sub-class of `StreamingResponse` that sets the `Content-Type` header to the type of data you're streaming.
For example, you can create a `PNGStreamingResponse` that sets the `Content-Type` header to `image/png` using the `media_type` attribute:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Then you can use this new class in `response_class=PNGStreamingResponse` in your *path operation function*:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Simulate a File { #simulate-a-file }
In this example, we are simulating a file with `io.BytesIO`, which is a file-like object that lives only in memory, but lets us use the same interface.
For example, we can iterate over it to consume its contents, as we could with a file.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Technical Details
The other two variables, `image_base64` and `binary_image`, are an image encoded in Base64, and then converted to bytes, to then pass it to `io.BytesIO`.
Only so that it can live in the same file for this example and you can copy it and run it as is. 🥚
///
By using a `with` block, we make sure that the file-like object is closed after the generator function (the function with `yield`) is done. So, after it finishes sending the response.
It wouldn't be that important in this specific example because it's a fake in-memory file (with `io.BytesIO`), but with a real file, it would be important to make sure the file is closed after the work with it is done.
### Files and Async { #files-and-async }
In most cases, file-like objects are not compatible with async and await by default.
For example, they don't have an `await file.read()`, or `async for chunk in file`.
And in many cases, reading them would be a blocking operation (that could block the event loop), because they are read from disk or from the network.
/// info
The example above is actually an exception, because the `io.BytesIO` object is already in memory, so reading it won't block anything.
But in many cases reading a file or a file-like object would block.
///
To avoid blocking the event loop, you can simply declare the *path operation function* with regular `def` instead of `async def`, that way FastAPI will run it on a threadpool worker, to avoid blocking the main loop.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip
If you need to call blocking code from inside of an async function, or an async function from inside of a blocking function, you could use [Asyncer](https://asyncer.tiangolo.com), a sibling library to FastAPI.
///
### `yield from` { #yield-from }
When you are iterating over something, like a file-like object, and then you are doing `yield` for each item, you could also use `yield from` to yield each item directly and skip the `for` loop.
This is not particular to FastAPI, it's just Python, but it's a nice trick to know. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/en/docs/advanced/strict-content-type.md
================================================
# Strict Content-Type Checking { #strict-content-type-checking }
By default, **FastAPI** uses strict `Content-Type` header checking for JSON request bodies, this means that JSON requests **must** include a valid `Content-Type` header (e.g. `application/json`) in order for the body to be parsed as JSON.
## CSRF Risk { #csrf-risk }
This default behavior provides protection against a class of **Cross-Site Request Forgery (CSRF)** attacks in a very specific scenario.
These attacks exploit the fact that browsers allow scripts to send requests without doing any CORS preflight check when they:
* don't have a `Content-Type` header (e.g. using `fetch()` with a `Blob` body)
* and don't send any authentication credentials.
This type of attack is mainly relevant when:
* the application is running locally (e.g. on `localhost`) or in an internal network
* and the application doesn't have any authentication, it expects that any request from the same network can be trusted.
## Example Attack { #example-attack }
Imagine you build a way to run a local AI agent.
It provides an API at
```
http://localhost:8000/v1/agents/multivac
```
There's also a frontend at
```
http://localhost:8000
```
/// tip
Note that both have the same host.
///
Then using the frontend you can make the AI agent do things on your behalf.
As it's running **locally**, and not in the open internet, you decide to **not have any authentication** set up, just trusting the access to the local network.
Then one of your users could install it and run it locally.
Then they could open a malicious website, e.g. something like
```
https://evilhackers.example.com
```
And that malicious website sends requests using `fetch()` with a `Blob` body to the local API at
```
http://localhost:8000/v1/agents/multivac
```
Even though the host of the malicious website and the local app is different, the browser won't trigger a CORS preflight request because:
* It's running without any authentication, it doesn't have to send any credentials.
* The browser thinks it's not sending JSON (because of the missing `Content-Type` header).
Then the malicious website could make the local AI agent send angry messages to the user's ex-boss... or worse. 😅
## Open Internet { #open-internet }
If your app is in the open internet, you wouldn't "trust the network" and let anyone send privileged requests without authentication.
Attackers could simply run a script to send requests to your API, no need for browser interaction, so you are probably already securing any privileged endpoints.
In that case **this attack / risk doesn't apply to you**.
This risk and attack is mainly relevant when the app runs on the **local network** and that is the **only assumed protection**.
## Allowing Requests Without Content-Type { #allowing-requests-without-content-type }
If you need to support clients that don't send a `Content-Type` header, you can disable strict checking by setting `strict_content_type=False`:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
With this setting, requests without a `Content-Type` header will have their body parsed as JSON, which is the same behavior as older versions of FastAPI.
/// info
This behavior and configuration was added in FastAPI 0.132.0.
///
================================================
FILE: docs/en/docs/advanced/sub-applications.md
================================================
# Sub Applications - Mounts { #sub-applications-mounts }
If you need to have two independent FastAPI applications, with their own independent OpenAPI and their own docs UIs, you can have a main app and "mount" one (or more) sub-application(s).
## Mounting a **FastAPI** application { #mounting-a-fastapi-application }
"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling everything under that path, with the _path operations_ declared in that sub-application.
### Top-level application { #top-level-application }
First, create the main, top-level, **FastAPI** application, and its *path operations*:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sub-application { #sub-application }
Then, create your sub-application, and its *path operations*.
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Mount the sub-application { #mount-the-sub-application }
In your top-level application, `app`, mount the sub-application, `subapi`.
In this case, it will be mounted at the path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Check the automatic API docs { #check-the-automatic-api-docs }
Now, run the `fastapi` command:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
And open the docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see the automatic API docs for the main app, including only its own _path operations_:
And then, open the docs for the sub-application, at [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
You will see the automatic API docs for the sub-application, including only its own _path operations_, all under the correct sub-path prefix `/subapi`:
If you try interacting with any of the two user interfaces, they will work correctly, because the browser will be able to talk to each specific app or sub-app.
### Technical Details: `root_path` { #technical-details-root-path }
When you mount a sub-application as described above, FastAPI will take care of communicating the mount path for the sub-application using a mechanism from the ASGI specification called a `root_path`.
That way, the sub-application will know to use that path prefix for the docs UI.
And the sub-application could also have its own mounted sub-applications and everything would work correctly, because FastAPI handles all these `root_path`s automatically.
You will learn more about the `root_path` and how to use it explicitly in the section about [Behind a Proxy](behind-a-proxy.md).
================================================
FILE: docs/en/docs/advanced/templates.md
================================================
# Templates { #templates }
You can use any template engine you want with **FastAPI**.
A common choice is Jinja2, the same one used by Flask and other tools.
There are utilities to configure it easily that you can use directly in your **FastAPI** application (provided by Starlette).
## Install dependencies { #install-dependencies }
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and install `jinja2`:
It will install all the dependencies and your local FastAPI in your local environment.
### Using your local FastAPI
If you create a Python file that imports and uses FastAPI, and run it with the Python from your local environment, it will use your cloned local FastAPI source code.
And if you update that local FastAPI source code when you run that Python file again, it will use the fresh version of FastAPI you just edited.
That way, you don't have to "install" your local version to be able to test every change.
/// note | Technical Details
This only happens when you install using `uv sync --extra all` instead of running `pip install fastapi` directly.
That is because `uv sync --extra all` will install the local version of FastAPI in "editable" mode by default.
///
### Format the code
There is a script that you can run that will format and clean all your code:
```console
$ bash scripts/format.sh
```
It will also auto-sort all your imports.
## Tests
There is a script that you can run locally to test all the code and generate coverage reports in HTML:
```console
$ bash scripts/test-cov-html.sh
```
This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.
## Docs
First, make sure you set up your environment as described above, that will install all the requirements.
### Docs live
During local development, there is a script that builds the site and checks for any changes, live-reloading:
```console
$ python ./scripts/docs.py live
[INFO] Serving on http://127.0.0.1:8008
[INFO] Start watching changes
[INFO] Start detecting changes
```
It will serve the documentation on `http://127.0.0.1:8008`.
That way, you can edit the documentation/source files and see the changes live.
/// tip
Alternatively, you can perform the same steps that scripts does manually.
Go into the language directory, for the main docs in English it's at `docs/en/`:
```console
$ cd docs/en/
```
Then run `mkdocs` in that directory:
```console
$ mkdocs serve --dev-addr 127.0.0.1:8008
```
///
#### Typer CLI (optional)
The instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly.
But you can also use [Typer CLI](https://typer.tiangolo.com/typer-cli/), and you will get autocompletion in your terminal for the commands after installing completion.
If you install Typer CLI, you can install completion with:
```console
$ typer --install-completion
zsh completion installed in /home/user/.bashrc.
Completion will take effect once you restart the terminal.
```
### Docs Structure
The documentation uses [MkDocs](https://www.mkdocs.org/).
And there are extra tools/scripts in place to handle translations in `./scripts/docs.py`.
/// tip
You don't need to see the code in `./scripts/docs.py`, you just use it in the command line.
///
All the documentation is in Markdown format in the directory `./docs/en/`.
Many of the tutorials have blocks of code.
In most of the cases, these blocks of code are actual complete applications that can be run as is.
In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory.
And those Python files are included/injected in the documentation when generating the site.
### Docs for tests
Most of the tests actually run against the example source files in the documentation.
This helps to make sure that:
* The documentation is up-to-date.
* The documentation examples can be run as is.
* Most of the features are covered by the documentation, ensured by test coverage.
#### Apps and docs at the same time
If you run the examples with, e.g.:
```console
$ fastapi dev tutorial001.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash.
### Translations
Help with translations is VERY MUCH appreciated! And it can't be done without the help from the community. 🌎 🚀
Translation pull requests are made by LLMs guided with prompts designed by the FastAPI team together with the community of native speakers for each supported language.
#### LLM Prompt per Language
Each language has a directory: [https://github.com/fastapi/fastapi/tree/master/docs](https://github.com/fastapi/fastapi/tree/master/docs), in it you can see a file `llm-prompt.md` with the prompt specific for that language.
For example, for Spanish, the prompt is at: [`docs/es/llm-prompt.md`](https://github.com/fastapi/fastapi/blob/master/docs/es/llm-prompt.md).
If you see mistakes in your language, you can make suggestions to the prompt in that file for your language, and request the specific pages you would like to re-generate after the changes.
#### Reviewing Translation PRs
We don’t require approval from native speakers for translation PRs generated automatically by our translation workflow. However, you can still review them and suggest improvements to the LLM prompt for that language to make the future translations better.
You can check the currently [existing pull requests](https://github.com/fastapi/fastapi/pulls) for your language. You can filter the pull requests by the ones with the label for your language. For example, for Spanish, the label is [`lang-es`](https://github.com/fastapi/fastapi/pulls?q=is%3Aopen+sort%3Aupdated-desc+label%3Alang-es+label%3Aawaiting-review).
You can also review already merged translation PRs. To do this, go to the [closed pull requests](https://github.com/fastapi/fastapi/pulls?q=is%3Apr+is%3Aclosed) and filter by your language label. For example, for Spanish, you can use [`lang-es`](https://github.com/fastapi/fastapi/pulls?q=is%3Apr+is%3Aclosed+label%3Alang-es).
When reviewing a pull request, it's better not to suggest changes in the same pull request, because it is LLM generated, and it won't be possible to make sure that small individual changes are replicated in other similar sections, or that they are preserved when translating the same content again.
Instead of adding suggestions to the translation PR, make the suggestions to the LLM prompt file for that language, in a new PR. For example, for Spanish, the LLM prompt file is at: [`docs/es/llm-prompt.md`](https://github.com/fastapi/fastapi/blob/master/docs/es/llm-prompt.md).
/// tip
Check the docs about [adding a pull request review](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews) to approve it or request changes.
///
PRs with suggestions to the language-specific LLM prompt require approval from at least one native speaker. Your help here is very much appreciated!
#### Subscribe to Notifications for Your Language
Check if there's a [GitHub Discussion](https://github.com/fastapi/fastapi/discussions/categories/translations) to coordinate translations for your language. You can subscribe to it, and when there's a new pull request to review, an automatic comment will be added to the discussion.
To check the 2-letter code for the language you want to translate, you can use the table [List of ISO 639-1 codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
#### Request a New Language
Let's say that you want to request translations for a language that is not yet translated, not even some pages. For example, Latin.
* The first step would be for you to find other 2 people that would be willing to be reviewing translation PRs for that language with you.
* Once there are at least 3 people that would be willing to commit to help maintain that language, you can continue the next steps.
* Create a new discussion following the template.
* Tag the other 2 people that will help with the language, and ask them to confirm there they will help.
Once there are several people in the discussion, the FastAPI team can evaluate it and can make it an official translation.
Then the docs will be automatically translated using LLMs, and the team of native speakers can review the translation, and help tweak the LLM prompts.
Once there's a new translation, for example if docs are updated or there's a new section, there will be a comment in the same discussion with the link to the new translation to review.
## Automated Code and AI
You are encouraged to use all the tools you want to do your work and contribute as efficiently as possible, this includes AI (LLM) tools, etc. Nevertheless, contributions should have meaningful human intervention, judgement, context, etc.
If the **human effort** put in a PR, e.g. writing LLM prompts, is **less** than the **effort we would need to put** to **review it**, please **don't** submit the PR.
Think of it this way: we can already write LLM prompts or run automated tools ourselves, and that would be faster than reviewing external PRs.
### Closing Automated and AI PRs
If we see PRs that seem AI generated or automated in similar ways, we'll flag them and close them.
The same applies to comments and descriptions, please don't copy paste the content generated by an LLM.
### Human Effort Denial of Service
Using automated tools and AI to submit PRs or comments that we have to carefully review and handle would be the equivalent of a [Denial-of-service attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) on our human effort.
It would be very little effort from the person submitting the PR (an LLM prompt) that generates a large amount of effort on our side (carefully reviewing code).
Please don't do that.
We'll need to block accounts that spam us with repeated automated PRs or comments.
### Use Tools Wisely
As Uncle Ben said:
With great powertools comes great responsibility.
Avoid inadvertently doing harm.
You have amazing tools at hand, use them wisely to help effectively.
================================================
FILE: docs/en/docs/css/custom.css
================================================
/* Fira Code, including characters used by Rich output, like the "heavy right-pointing angle bracket ornament", not included in Google Fonts */
@import url(https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css);
/* Noto Color Emoji for emoji support with the same font everywhere */
@import url(https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap);
/* Override default code font in Material for MkDocs to Fira Code */
:root {
--md-code-font: "Fira Code", monospace, "Noto Color Emoji";
}
/* Override default regular font in Material for MkDocs to include Noto Color Emoji */
:root {
--md-text-font: "Roboto", "Noto Color Emoji";
}
.termynal-comment {
color: #4a968f;
font-style: italic;
display: block;
}
.termy {
/* For right to left languages */
direction: ltr;
}
.termy [data-termynal] {
white-space: pre-wrap;
}
.termy .linenos {
display: none;
}
/* External links: detected by JS comparing origin to site origin
JS sets data-external-link on links pointing outside the site
Skip image links, .no-link-icon, and .announce-link */
a[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link) {
/* For right to left languages */
direction: ltr;
display: inline-block;
}
a[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link)::after {
content: "";
display: inline-block;
width: 0.75em;
height: 0.75em;
margin-left: 0.25em;
vertical-align: middle;
opacity: 0.55;
background: currentColor;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
mask-size: contain;
mask-repeat: no-repeat;
}
a[data-external-link]:not(:has(img)):not(.no-link-icon):not(.announce-link):hover::after {
opacity: 0.85;
}
/* Internal links opening in new tab: same-origin links with target=_blank
JS sets data-internal-link on links pointing to the same site origin
Skip image links, .no-link-icon, and .announce-link */
a[data-internal-link][target="_blank"]:not(:has(img)):not(.no-link-icon):not(.announce-link) {
/* For right to left languages */
direction: ltr;
display: inline-block;
}
a[data-internal-link][target="_blank"]:not(:has(img)):not(.no-link-icon):not(.announce-link)::after {
content: "";
display: inline-block;
width: 0.75em;
height: 0.75em;
margin-left: 0.25em;
vertical-align: middle;
opacity: 0.55;
background: currentColor;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='14' height='14' rx='2'/%3E%3Cpath d='M7 3h14v14'/%3E%3C/svg%3E");
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='14' height='14' rx='2'/%3E%3Cpath d='M7 3h14v14'/%3E%3C/svg%3E");
mask-size: contain;
mask-repeat: no-repeat;
}
a[data-internal-link][target="_blank"]:not(:has(img)):not(.no-link-icon):not(.announce-link):hover::after {
opacity: 0.85;
}
/* Disable link icons in footer and header nav */
.md-footer a::after,
.md-header a::after {
content: none !important;
}
.shadow {
box-shadow: 5px 5px 10px #999;
}
/* Give space to lower icons so Gitter chat doesn't get on top of them */
.md-footer-meta {
padding-bottom: 2em;
}
.md-footer-meta .md-social {
padding-right: 4rem;
}
.user-list {
display: flex;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.user-list-center {
justify-content: space-evenly;
}
.user {
margin: 1em;
min-width: 7em;
}
.user .avatar-wrapper {
width: 80px;
height: 80px;
margin: 10px auto;
overflow: hidden;
border-radius: 50%;
position: relative;
}
.user .avatar-wrapper img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.user .title {
text-align: center;
}
.user .count {
font-size: 80%;
text-align: center;
}
a.announce-link:link,
a.announce-link:visited {
color: #fff;
}
a.announce-link:hover {
color: var(--md-accent-fg-color);
}
.announce-wrapper {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
align-items: center;
}
.announce-wrapper #announce-left div.item {
display: none;
}
.announce-wrapper #announce-right {
display: none;
}
.announce-wrapper #announce-right div.item {
display: none;
}
.announce-wrapper .sponsor-badge {
display: block;
position: absolute;
top: -10px;
right: 0;
font-size: 0.5rem;
color: #e6e6e6;
background-color: #666;
border-radius: 10px;
padding: 0 10px;
z-index: 10;
}
.announce-wrapper .sponsor-image {
display: block;
border-radius: 20px;
}
.announce-wrapper>div {
min-height: 40px;
display: flex;
align-items: center;
}
.twitter {
color: #00acee;
}
/* Right to left languages */
code {
direction: ltr;
display: inline-block;
}
.illustration {
margin-top: 2em;
margin-bottom: 2em;
}
/* Screenshots */
/*
Simulate a browser window frame.
Inspired by Termynal's CSS tricks with modifications
*/
.screenshot {
display: block;
background-color: #d3e0de;
border-radius: 4px;
padding: 45px 5px 5px;
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.screenshot img {
display: block;
border-radius: 2px;
}
.screenshot:before {
content: '';
position: absolute;
top: 15px;
left: 15px;
display: inline-block;
width: 15px;
height: 15px;
border-radius: 50%;
/* A little hack to display the window buttons in one pseudo element. */
background: #d9515d;
-webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
}
.doc-param-details .highlight {
overflow-x: auto;
width: 0;
min-width: 100%;
}
.md-typeset dfn {
border-bottom: .05rem dotted var(--md-default-fg-color--light);
cursor: help;
}
/* Opinions: interactive logo tabs */
.fastapi-opinions {
margin: 1.5rem 0 2rem;
}
.fastapi-opinions__tabs {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.25rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--md-default-fg-color--lightest);
}
.fastapi-opinions__tab {
position: relative;
appearance: none;
background: none;
border: 0;
padding: 0.625rem 0.5rem;
margin: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: inherit;
font: inherit;
min-height: 40px;
min-width: 0;
}
.fastapi-opinions__tab::after {
content: "";
position: absolute;
left: 50%;
right: 50%;
bottom: -1px;
height: 2px;
background-color: var(--md-primary-fg-color);
opacity: 0;
transition: left 0.2s ease, right 0.2s ease, opacity 0.2s ease;
}
.fastapi-opinions__tab[aria-selected="true"]::after {
left: 12%;
right: 12%;
opacity: 1;
}
.fastapi-opinions__tab:focus-visible {
outline: 2px solid var(--md-primary-fg-color);
outline-offset: 2px;
border-radius: 4px;
}
.fastapi-opinions__mark {
display: flex;
align-items: center;
justify-content: center;
height: 22px;
max-width: 100%;
filter: grayscale(1);
opacity: 0.5;
transition: filter 0.2s, opacity 0.2s;
}
.fastapi-opinions__mark img {
height: 100%;
width: auto;
max-width: 100%;
object-fit: contain;
display: block;
}
.fastapi-opinions__tab:hover .fastapi-opinions__mark {
filter: grayscale(0.3);
opacity: 0.85;
}
.fastapi-opinions__tab[aria-selected="true"] .fastapi-opinions__mark {
filter: grayscale(0);
opacity: 1;
}
/* Dark mode: brighten dark wordmarks so they read on slate */
[data-md-color-scheme="slate"] .fastapi-opinions__mark {
filter: grayscale(1) invert(0.85);
}
[data-md-color-scheme="slate"] .fastapi-opinions__tab:hover .fastapi-opinions__mark {
filter: grayscale(0.3) invert(0.4);
}
[data-md-color-scheme="slate"] .fastapi-opinions__tab[aria-selected="true"] .fastapi-opinions__mark {
filter: none;
}
.fastapi-opinions__panel {
position: relative;
padding: 0.5rem 1rem 0.5rem 3rem;
}
.fastapi-opinions__panel::before {
content: "\201C";
position: absolute;
top: -0.75rem;
left: 0.25rem;
font-family: Georgia, "Times New Roman", serif;
font-size: 4rem;
line-height: 1;
color: var(--md-primary-fg-color);
opacity: 0.18;
pointer-events: none;
}
.md-typeset blockquote.fastapi-opinions__quote {
margin: 0;
font-size: 1rem;
font-style: italic;
line-height: 1.65;
color: var(--md-default-fg-color);
border-left: 0;
padding-left: 0;
}
.fastapi-opinions__quote strong { font-style: normal; }
.fastapi-opinions__attr {
margin-top: 0.875rem;
font-size: 0.8rem;
color: var(--md-default-fg-color--light);
}
.fastapi-opinions__attr strong { color: var(--md-default-fg-color); }
.fastapi-opinions__attr a {
color: var(--md-primary-fg-color);
text-decoration: none;
font-size: 0.75rem;
margin-left: 0.25rem;
}
.fastapi-opinions__attr a:hover { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
.fastapi-opinions__tab::after { transition: none; }
}
@media (max-width: 600px) {
.fastapi-opinions__tabs { gap: 0.125rem; }
.fastapi-opinions__mark { height: 18px; }
.fastapi-opinions__panel { padding-left: 2.25rem; }
.fastapi-opinions__panel::before { font-size: 3rem; }
}
.fastapi-sponsors {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1rem 1.25rem;
margin: 1rem 0 2rem;
}
.fastapi-sponsors__card {
transition: transform 0.15s ease;
}
.fastapi-sponsors__card:hover {
transform: translateY(-1px);
}
.fastapi-sponsors__card--keystone {
width: 100%;
max-width: 560px;
}
.fastapi-sponsors__banner {
display: block;
border-radius: 12px;
}
.fastapi-sponsors__card--keystone .fastapi-sponsors__banner { width: 100%; }
.fastapi-sponsors__card--gold .fastapi-sponsors__banner { height: 80px; }
.fastapi-sponsors__card--silver .fastapi-sponsors__banner { height: 60px; }
@media (max-width: 600px) {
.fastapi-sponsors__card--gold .fastapi-sponsors__banner { height: 64px; }
.fastapi-sponsors__card--silver .fastapi-sponsors__banner { height: 50px; }
}
.fastapi-feature-banner {
display: block;
max-width: 680px;
margin: 1rem auto 1.5rem;
}
.fastapi-feature-banner img {
display: block;
width: 100%;
border-radius: 12px;
}
/* Hidden in MkDocs; rendered on GitHub (which doesn't load this stylesheet) */
.only-github { display: none; }
================================================
FILE: docs/en/docs/css/termynal.css
================================================
/**
* termynal.js
*
* @author Ines Montani
* @version 0.0.1
* @license MIT
*/
:root {
--color-bg: #252a33;
--color-text: #eee;
--color-text-subtle: #a2a2a2;
}
[data-termynal] {
width: 750px;
max-width: 100%;
background: var(--color-bg);
color: var(--color-text);
/* font-size: 18px; */
font-size: 15px;
/* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */
font-family: var(--md-code-font-family), 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;
border-radius: 4px;
padding: 75px 45px 35px;
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
/* Custom line-height */
line-height: 1.2;
}
[data-termynal]:before {
content: '';
position: absolute;
top: 15px;
left: 15px;
display: inline-block;
width: 15px;
height: 15px;
border-radius: 50%;
/* A little hack to display the window buttons in one pseudo element. */
background: #d9515d;
-webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
}
[data-termynal]:after {
content: 'bash';
position: absolute;
color: var(--color-text-subtle);
top: 5px;
left: 0;
width: 100%;
text-align: center;
}
a[data-terminal-control] {
text-align: right;
display: block;
color: #aebbff;
}
[data-ty] {
display: block;
line-height: 2;
}
[data-ty]:before {
/* Set up defaults and ensure empty lines are displayed. */
content: '';
display: inline-block;
vertical-align: middle;
}
[data-ty="input"]:before,
[data-ty-prompt]:before {
margin-right: 0.75em;
color: var(--color-text-subtle);
}
[data-ty="input"]:before {
content: '$';
}
[data-ty][data-ty-prompt]:before {
content: attr(data-ty-prompt);
}
[data-ty-cursor]:after {
content: attr(data-ty-cursor);
font-family: monospace;
margin-left: 0.5em;
-webkit-animation: blink 1s infinite;
animation: blink 1s infinite;
}
/* Cursor animation */
@-webkit-keyframes blink {
50% {
opacity: 0;
}
}
@keyframes blink {
50% {
opacity: 0;
}
}
================================================
FILE: docs/en/docs/deployment/cloud.md
================================================
# Deploy FastAPI on Cloud Providers { #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.
## FastAPI Cloud { #fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** is built by the same author and team behind **FastAPI**.
It streamlines the process of **building**, **deploying**, and **accessing** an API with minimal effort.
It brings the same **developer experience** of building apps with FastAPI to **deploying** them to the cloud. 🎉
FastAPI Cloud is the primary sponsor and funding provider for the *FastAPI and friends* open source projects. ✨
## Cloud Providers - Sponsors { #cloud-providers-sponsors }
Some other cloud providers ✨ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author) ✨ too. 🙇
You might also want to consider them to follow their guides and try their services:
* [Render](https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi)
* [Railway](https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi)
================================================
FILE: docs/en/docs/deployment/concepts.md
================================================
# Deployments Concepts { #deployments-concepts }
When deploying a **FastAPI** application, or actually, any type of web API, there are several concepts that you probably care about, and using them you can find the **most appropriate** way to **deploy your application**.
Some of the important concepts are:
* Security - HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
We'll see how they would affect **deployments**.
In the end, the ultimate objective is to be able to **serve your API clients** in a way that is **secure**, to **avoid disruptions**, and to use the **compute resources** (for example remote servers/virtual machines) as efficiently as possible. 🚀
I'll tell you a bit more about these **concepts** here, and that would hopefully give you the **intuition** you would need to decide how to deploy your API in very different environments, possibly even in **future** ones that don't exist yet.
By considering these concepts, you will be able to **evaluate and design** the best way to deploy **your own APIs**.
In the next chapters, I'll give you more **concrete recipes** to deploy FastAPI applications.
But for now, let's check these important **conceptual ideas**. These concepts also apply to any other type of web API. 💡
## Security - HTTPS { #security-https }
In the [previous chapter about HTTPS](https.md) we learned about how HTTPS provides encryption for your API.
We also saw that HTTPS is normally provided by a component **external** to your application server, a **TLS Termination Proxy**.
And there has to be something in charge of **renewing the HTTPS certificates**, it could be the same component or it could be something different.
### Example Tools for HTTPS { #example-tools-for-https }
Some of the tools you could use as a TLS Termination Proxy are:
* Traefik
* Automatically handles certificates renewals ✨
* Caddy
* Automatically handles certificates renewals ✨
* Nginx
* With an external component like Certbot for certificate renewals
* HAProxy
* With an external component like Certbot for certificate renewals
* Kubernetes with an Ingress Controller like Nginx
* With an external component like cert-manager for certificate renewals
* Handled internally by a cloud provider as part of their services (read below 👇)
Another option is that you could use a **cloud service** that does more of the work including setting up HTTPS. It could have some restrictions or charge you more, etc. But in that case, you wouldn't have to set up a TLS Termination Proxy yourself.
I'll show you some concrete examples in the next chapters.
---
Then the next concepts to consider are all about the program running your actual API (e.g. Uvicorn).
## Program and Process { #program-and-process }
We will talk a lot about the running "**process**", so it's useful to have clarity about what it means, and what's the difference with the word "**program**".
### What is a Program { #what-is-a-program }
The word **program** is commonly used to describe many things:
* The **code** that you write, the **Python files**.
* The **file** that can be **executed** by the operating system, for example: `python`, `python.exe` or `uvicorn`.
* A particular program while it is **running** on the operating system, using the CPU, and storing things in memory. This is also called a **process**.
### What is a Process { #what-is-a-process }
The word **process** is normally used in a more specific way, only referring to the thing that is running in the operating system (like in the last point above):
* A particular program while it is **running** on the operating system.
* This doesn't refer to the file, nor to the code, it refers **specifically** to the thing that is being **executed** and managed by the operating system.
* Any program, any code, **can only do things** when it is being **executed**. So, when there's a **process running**.
* The process can be **terminated** (or "killed") by you, or by the operating system. At that point, it stops running/being executed, and it can **no longer do things**.
* Each application that you have running on your computer has some process behind it, each running program, each window, etc. And there are normally many processes running **at the same time** while a computer is on.
* There can be **multiple processes** of the **same program** running at the same time.
If you check out the "task manager" or "system monitor" (or similar tools) in your operating system, you will be able to see many of those processes running.
And, for example, you will probably see that there are multiple processes running the same browser program (Firefox, Chrome, Edge, etc). They normally run one process per tab, plus some other extra processes.
---
Now that we know the difference between the terms **process** and **program**, let's continue talking about deployments.
## Running on Startup { #running-on-startup }
In most cases, when you create a web API, you want it to be **always running**, uninterrupted, so that your clients can always access it. This is of course, unless you have a specific reason why you want it to run only in certain situations, but most of the time you want it constantly running and **available**.
### In a Remote Server { #in-a-remote-server }
When you set up a remote server (a cloud server, a virtual machine, etc.) the simplest thing you can do is use `fastapi run` (which uses Uvicorn) or something similar, manually, the same way you do when developing locally.
And it will work and will be useful **during development**.
But if your connection to the server is lost, the **running process** will probably die.
And if the server is restarted (for example after updates, or migrations from the cloud provider) you probably **won't notice it**. And because of that, you won't even know that you have to restart the process manually. So, your API will just stay dead. 😱
### Run Automatically on Startup { #run-automatically-on-startup }
In general, you will probably want the server program (e.g. Uvicorn) to be started automatically on server startup, and without needing any **human intervention**, to have a process always running with your API (e.g. Uvicorn running your FastAPI app).
### Separate Program { #separate-program }
To achieve this, you will normally have a **separate program** that would make sure your application is run on startup. And in many cases, it would also make sure other components or applications are also run, for example, a database.
### Example Tools to Run at Startup { #example-tools-to-run-at-startup }
Some examples of the tools that can do this job are:
* Docker
* Kubernetes
* Docker Compose
* Docker in Swarm Mode
* Systemd
* Supervisor
* Handled internally by a cloud provider as part of their services
* Others...
I'll give you more concrete examples in the next chapters.
## Restarts { #restarts }
Similar to making sure your application is run on startup, you probably also want to make sure it is **restarted** after failures.
### We Make Mistakes { #we-make-mistakes }
We, as humans, make **mistakes**, all the time. Software almost *always* has **bugs** hidden in different places. 🐛
And we as developers keep improving the code as we find those bugs and as we implement new features (possibly adding new bugs too 😅).
### Small Errors Automatically Handled { #small-errors-automatically-handled }
When building web APIs with FastAPI, if there's an error in our code, FastAPI will normally contain it to the single request that triggered the error. 🛡
The client will get a **500 Internal Server Error** for that request, but the application will continue working for the next requests instead of just crashing completely.
### Bigger Errors - Crashes { #bigger-errors-crashes }
Nevertheless, there might be cases where we write some code that **crashes the entire application** making Uvicorn and Python crash. 💥
And still, you would probably not want the application to stay dead because there was an error in one place, you probably want it to **continue running** at least for the *path operations* that are not broken.
### Restart After Crash { #restart-after-crash }
But in those cases with really bad errors that crash the running **process**, you would want an external component that is in charge of **restarting** the process, at least a couple of times...
/// tip
...Although if the whole application is just **crashing immediately** it probably doesn't make sense to keep restarting it forever. But in those cases, you will probably notice it during development, or at least right after deployment.
So let's focus on the main cases, where it could crash entirely in some particular cases **in the future**, and it still makes sense to restart it.
///
You would probably want to have the thing in charge of restarting your application as an **external component**, because by that point, the same application with Uvicorn and Python already crashed, so there's nothing in the same code of the same app that could do anything about it.
### Example Tools to Restart Automatically { #example-tools-to-restart-automatically }
In most cases, the same tool that is used to **run the program on startup** is also used to handle automatic **restarts**.
For example, this could be handled by:
* Docker
* Kubernetes
* Docker Compose
* Docker in Swarm Mode
* Systemd
* Supervisor
* Handled internally by a cloud provider as part of their services
* Others...
## Replication - Processes and Memory { #replication-processes-and-memory }
With a FastAPI application, using a server program like the `fastapi` command that runs Uvicorn, running it once in **one process** can serve multiple clients concurrently.
But in many cases, you will want to run several worker processes at the same time.
### Multiple Processes - Workers { #multiple-processes-workers }
If you have more clients than what a single process can handle (for example if the virtual machine is not too big) and you have **multiple cores** in the server's CPU, then you could have **multiple processes** running with the same application at the same time, and distribute all the requests among them.
When you run **multiple processes** of the same API program, they are commonly called **workers**.
### Worker Processes and Ports { #worker-processes-and-ports }
Remember from the docs [About HTTPS](https.md) that only one process can be listening on one combination of port and IP address in a server?
This is still true.
So, to be able to have **multiple processes** at the same time, there has to be a **single process listening on a port** that then transmits the communication to each worker process in some way.
### Memory per Process { #memory-per-process }
Now, when the program loads things in memory, for example, a machine learning model in a variable, or the contents of a large file in a variable, all that **consumes a bit of the memory (RAM)** of the server.
And multiple processes normally **don't share any memory**. This means that each running process has its own things, variables, and memory. And if you are consuming a large amount of memory in your code, **each process** will consume an equivalent amount of memory.
### Server Memory { #server-memory }
For example, if your code loads a Machine Learning model with **1 GB in size**, when you run one process with your API, it will consume at least 1 GB of RAM. And if you start **4 processes** (4 workers), each will consume 1 GB of RAM. So in total, your API will consume **4 GB of RAM**.
And if your remote server or virtual machine only has 3 GB of RAM, trying to load more than 4 GB of RAM will cause problems. 🚨
### Multiple Processes - An Example { #multiple-processes-an-example }
In this example, there's a **Manager Process** that starts and controls two **Worker Processes**.
This Manager Process would probably be the one listening on the **port** in the IP. And it would transmit all the communication to the worker processes.
Those worker processes would be the ones running your application, they would perform the main computations to receive a **request** and return a **response**, and they would load anything you put in variables in RAM.
And of course, the same machine would probably have **other processes** running as well, apart from your application.
An interesting detail is that the percentage of the **CPU used** by each process can **vary** a lot over time, but the **memory (RAM)** normally stays more or less **stable**.
If you have an API that does a comparable amount of computations every time and you have a lot of clients, then the **CPU utilization** will probably *also be stable* (instead of constantly going up and down quickly).
### Examples of Replication Tools and Strategies { #examples-of-replication-tools-and-strategies }
There can be several approaches to achieve this, and I'll tell you more about specific strategies in the next chapters, for example when talking about Docker and containers.
The main constraint to consider is that there has to be a **single** component handling the **port** in the **public IP**. And then it has to have a way to **transmit** the communication to the replicated **processes/workers**.
Here are some possible combinations and strategies:
* **Uvicorn** with `--workers`
* One Uvicorn **process manager** would listen on the **IP** and **port**, and it would start **multiple Uvicorn worker processes**.
* **Kubernetes** and other distributed **container systems**
* Something in the **Kubernetes** layer would listen on the **IP** and **port**. The replication would be by having **multiple containers**, each with **one Uvicorn process** running.
* **Cloud services** that handle this for you
* The cloud service will probably **handle replication for you**. It would possibly let you define **a process to run**, or a **container image** to use, in any case, it would most probably be **a single Uvicorn process**, and the cloud service would be in charge of replicating it.
/// tip
Don't worry if some of these items about **containers**, Docker, or Kubernetes don't make a lot of sense yet.
I'll tell you more about container images, Docker, Kubernetes, etc. in a future chapter: [FastAPI in Containers - Docker](docker.md).
///
## Previous Steps Before Starting { #previous-steps-before-starting }
There are many cases where you want to perform some steps **before starting** your application.
For example, you might want to run **database migrations**.
But in most cases, you will want to perform these steps only **once**.
So, you will want to have a **single process** to perform those **previous steps**, before starting the application.
And you will have to make sure that it's a single process running those previous steps *even* if afterwards, you start **multiple processes** (multiple workers) for the application itself. If those steps were run by **multiple processes**, they would **duplicate** the work by running it in **parallel**, and if the steps were something delicate like a database migration, they could cause conflicts with each other.
Of course, there are some cases where there's no problem in running the previous steps multiple times, in that case, it's a lot easier to handle.
/// tip
Also, keep in mind that depending on your setup, in some cases you **might not even need any previous steps** before starting your application.
In that case, you wouldn't have to worry about any of this. 🤷
///
### Examples of Previous Steps Strategies { #examples-of-previous-steps-strategies }
This will **depend heavily** on the way you **deploy your system**, and it would probably be connected to the way you start programs, handling restarts, etc.
Here are some possible ideas:
* An "Init Container" in Kubernetes that runs before your app container
* A bash script that runs the previous steps and then starts your application
* You would still need a way to start/restart *that* bash script, detect errors, etc.
/// tip
I'll give you more concrete examples for doing this with containers in a future chapter: [FastAPI in Containers - Docker](docker.md).
///
## Resource Utilization { #resource-utilization }
Your server(s) is (are) a **resource**, you can consume or **utilize**, with your programs, the computation time on the CPUs, and the RAM memory available.
How much of the system resources do you want to be consuming/utilizing? It might be easy to think "not much", but in reality, you will probably want to consume **as much as possible without crashing**.
If you are paying for 3 servers but you are using only a little bit of their RAM and CPU, you are probably **wasting money** 💸, and probably **wasting server electric power** 🌎, etc.
In that case, it could be better to have only 2 servers and use a higher percentage of their resources (CPU, memory, disk, network bandwidth, etc).
On the other hand, if you have 2 servers and you are using **100% of their CPU and RAM**, at some point one process will ask for more memory, and the server will have to use the disk as "memory" (which can be thousands of times slower), or even **crash**. Or one process might need to do some computation and would have to wait until the CPU is free again.
In this case, it would be better to get **one extra server** and run some processes on it so that they all have **enough RAM and CPU time**.
There's also the chance that for some reason you have a **spike** of usage of your API. Maybe it went viral, or maybe some other services or bots start using it. And you might want to have extra resources to be safe in those cases.
You could put an **arbitrary number** to target, for example, something **between 50% to 90%** of resource utilization. The point is that those are probably the main things you will want to measure and use to tweak your deployments.
You can use simple tools like `htop` to see the CPU and RAM used in your server or the amount used by each process. Or you can use more complex monitoring tools, which may be distributed across servers, etc.
## Recap { #recap }
You have been reading here some of the main concepts that you would probably need to keep in mind when deciding how to deploy your application:
* Security - HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
Understanding these ideas and how to apply them should give you the intuition necessary to take any decisions when configuring and tweaking your deployments. 🤓
In the next sections, I'll give you more concrete examples of possible strategies you can follow. 🚀
================================================
FILE: docs/en/docs/deployment/docker.md
================================================
# FastAPI in Containers - Docker { #fastapi-in-containers-docker }
When deploying FastAPI applications a common approach is to build a **Linux container image**. It's normally done using [**Docker**](https://www.docker.com/). You can then deploy that container image in one of a few possible ways.
Using Linux containers has several advantages including **security**, **replicability**, **simplicity**, and others.
/// tip
In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#build-a-docker-image-for-fastapi).
///
Dockerfile Preview 👀
```Dockerfile
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# If running behind a proxy like Nginx or Traefik add --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
```
## What is a Container { #what-is-a-container }
Containers (mainly Linux containers) are a very **lightweight** way to package applications including all their dependencies and necessary files while keeping them isolated from other containers (other applications or components) in the same system.
Linux containers run using the same Linux kernel of the host (machine, virtual machine, cloud server, etc). This just means that they are very lightweight (compared to full virtual machines emulating an entire operating system).
This way, containers consume **little resources**, an amount comparable to running the processes directly (a virtual machine would consume much more).
Containers also have their own **isolated** running processes (commonly just one process), file system, and network, simplifying deployment, security, development, etc.
## What is a Container Image { #what-is-a-container-image }
A **container** is run from a **container image**.
A container image is a **static** version of all the files, environment variables, and the default command/program that should be present in a container. **Static** here means that the container **image** is not running, it's not being executed, it's only the packaged files and metadata.
In contrast to a "**container image**" that is the stored static contents, a "**container**" normally refers to the running instance, the thing that is being **executed**.
When the **container** is started and running (started from a **container image**) it could create or change files, environment variables, etc. Those changes will exist only in that container, but would not persist in the underlying container image (would not be saved to disk).
A container image is comparable to the **program** file and contents, e.g. `python` and some file `main.py`.
And the **container** itself (in contrast to the **container image**) is the actual running instance of the image, comparable to a **process**. In fact, a container is running only when it has a **process running** (and normally it's only a single process). The container stops when there's no process running in it.
## Container Images { #container-images }
Docker has been one of the main tools to create and manage **container images** and **containers**.
And there's a public [Docker Hub](https://hub.docker.com/) with pre-made **official container images** for many tools, environments, databases, and applications.
For example, there's an official [Python Image](https://hub.docker.com/_/python).
And there are many other images for different things like databases, for example for:
* [PostgreSQL](https://hub.docker.com/_/postgres)
* [MySQL](https://hub.docker.com/_/mysql)
* [MongoDB](https://hub.docker.com/_/mongo)
* [Redis](https://hub.docker.com/_/redis), etc.
By using a pre-made container image it's very easy to **combine** and use different tools. For example, to try out a new database. In most cases, you can use the **official images**, and just configure them with environment variables.
That way, in many cases you can learn about containers and Docker and reuse that knowledge with many different tools and components.
So, you would run **multiple containers** with different things, like a database, a Python application, a web server with a React frontend application, and connect them together via their internal network.
All the container management systems (like Docker or Kubernetes) have these networking features integrated into them.
## Containers and Processes { #containers-and-processes }
A **container image** normally includes in its metadata the default program or command that should be run when the **container** is started and the parameters to be passed to that program. Very similar to what would be if it was in the command line.
When a **container** is started, it will run that command/program (although you can override it and make it run a different command/program).
A container is running as long as the **main process** (command or program) is running.
A container normally has a **single process**, but it's also possible to start subprocesses from the main process, and that way you will have **multiple processes** in the same container.
But it's not possible to have a running container without **at least one running process**. If the main process stops, the container stops.
## Build a Docker Image for FastAPI { #build-a-docker-image-for-fastapi }
Okay, let's build something now! 🚀
I'll show you how to build a **Docker image** for FastAPI **from scratch**, based on the **official Python** image.
This is what you would want to do in **most cases**, for example:
* Using **Kubernetes** or similar tools
* When running on a **Raspberry Pi**
* Using a cloud service that would run a container image for you, etc.
### Package Requirements { #package-requirements }
You would normally have the **package requirements** for your application in some file.
It would depend mainly on the tool you use to **install** those requirements.
The most common way to do it is to have a file `requirements.txt` with the package names and their versions, one per line.
You would of course use the same ideas you read in [About FastAPI versions](versions.md) to set the ranges of versions.
For example, your `requirements.txt` could look like:
```
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
And you would normally install those package dependencies with `pip`, for example:
/// info
There are other formats and tools to define and install package dependencies.
///
### Create the **FastAPI** Code { #create-the-fastapi-code }
* Create an `app` directory and enter it.
* Create an empty file `__init__.py`.
* 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Now in the same project directory create a file `Dockerfile` with:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Start from the official Python base image.
2. Set the current working directory to `/code`.
This is where we'll put the `requirements.txt` file and the `app` directory.
3. Copy the file with the requirements to the `/code` directory.
Copy **only** the file with the requirements first, not the rest of the code.
As this file **doesn't change often**, Docker will detect it and use the **cache** for this step, enabling the cache for the next step too.
4. Install the package dependencies in the requirements file.
The `--no-cache-dir` option tells `pip` to not save the downloaded packages locally, as that is only if `pip` was going to be run again to install the same packages, but that's not the case when working with containers.
/// note
The `--no-cache-dir` is only related to `pip`, it has nothing to do with Docker or containers.
///
The `--upgrade` option tells `pip` to upgrade the packages if they are already installed.
Because the previous step copying the file could be detected by the **Docker cache**, this step will also **use the Docker cache** when available.
Using the cache in this step will **save** you a lot of **time** when building the image again and again during development, instead of **downloading and installing** all the dependencies **every time**.
5. Copy the `./app` directory inside the `/code` directory.
As this has all the code which is what **changes most frequently** the Docker **cache** won't be used for this or any **following steps** easily.
So, it's important to put this **near the end** of the `Dockerfile`, to optimize the container image build times.
6. Set the **command** to use `fastapi run`, which uses Uvicorn underneath.
`CMD` takes a list of strings, each of these strings is what you would type in the command line separated by spaces.
This command will be run from the **current working directory**, the same `/code` directory you set above with `WORKDIR /code`.
/// tip
Review what each line does by clicking each number bubble in the code. 👆
///
/// warning
Make sure to **always** use the **exec form** of the `CMD` instruction, as explained below.
///
#### Use `CMD` - Exec Form { #use-cmd-exec-form }
The [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) Docker instruction can be written using two forms:
✅ **Exec** form:
```Dockerfile
# ✅ Do this
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ Don't do this
CMD fastapi run app/main.py --port 80
```
Make sure to always use the **exec** form to ensure that FastAPI can shutdown gracefully and [lifespan events](../advanced/events.md) are triggered.
You can read more about it in the [Docker docs for shell and exec form](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
This can be quite noticeable when using `docker compose`. See this Docker Compose FAQ section for more technical details: [Why do my services take 10 seconds to recreate or stop?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Directory Structure { #directory-structure }
You should now have a directory structure like:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Behind a TLS Termination Proxy { #behind-a-tls-termination-proxy }
If you are running your container behind a TLS Termination Proxy (load balancer) like Nginx or Traefik, add the option `--proxy-headers`, this will tell Uvicorn (through the FastAPI CLI) to trust the headers sent by that proxy telling it that the application is running behind HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Docker Cache { #docker-cache }
There's an important trick in this `Dockerfile`, we first copy the **file with the dependencies alone**, not the rest of the code. Let me tell you why is that.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker and other tools **build** these container images **incrementally**, adding **one layer on top of the other**, starting from the top of the `Dockerfile` and adding any files created by each of the instructions of the `Dockerfile`.
Docker and similar tools also use an **internal cache** when building the image, if a file hasn't changed since the last time building the container image, then it will **reuse the same layer** created the last time, instead of copying the file again and creating a new layer from scratch.
Just avoiding the copy of files doesn't necessarily improve things too much, but because it used the cache for that step, it can **use the cache for the next step**. For example, it could use the cache for the instruction that installs dependencies with:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
The file with the package requirements **won't change frequently**. So, by copying only that file, Docker will be able to **use the cache** for that step.
And then, Docker will be able to **use the cache for the next step** that downloads and install those dependencies. And here's where we **save a lot of time**. ✨ ...and avoid boredom waiting. 😪😆
Downloading and installing the package dependencies **could take minutes**, but using the **cache** would **take seconds** at most.
And as you would be building the container image again and again during development to check that your code changes are working, there's a lot of accumulated time this would save.
Then, near the end of the `Dockerfile`, we copy all the code. As this is what **changes most frequently**, we put it near the end, because almost always, anything after this step will not be able to use the cache.
```Dockerfile
COPY ./app /code/app
```
### Build the Docker Image { #build-the-docker-image }
Now that all the files are in place, let's build the container image.
* Go to the project directory (in where your `Dockerfile` is, containing your `app` directory).
* Build your FastAPI image:
/// tip
Notice the `.` at the end, it's equivalent to `./`, it tells Docker the directory to use to build the container image.
In this case, it's the same current directory (`.`).
///
### Start the Docker Container { #start-the-docker-container }
* Run a container based on your image:
## Check it { #check-it }
You should be able to check it in your Docker container's URL, for example: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) or [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (or equivalent, using your Docker host).
You will see something like:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Interactive API docs { #interactive-api-docs }
Now you can go to [http://192.168.99.100/docs](http://192.168.99.100/docs) or [http://127.0.0.1/docs](http://127.0.0.1/docs) (or equivalent, using your Docker host).
You will see the automatic interactive API documentation (provided by [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Alternative API docs { #alternative-api-docs }
And you can also go to [http://192.168.99.100/redoc](http://192.168.99.100/redoc) or [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (or equivalent, using your Docker host).
You will see the alternative automatic documentation (provided by [ReDoc](https://github.com/Rebilly/ReDoc)):

## Build a Docker Image with a Single-File FastAPI { #build-a-docker-image-with-a-single-file-fastapi }
If your FastAPI is a single file, for example, `main.py` without an `./app` directory, your file structure could look like this:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copy the `main.py` file to the `/code` directory directly (without any `./app` directory).
2. Use `fastapi run` to serve your application in the single file `main.py`.
When you pass the file to `fastapi run` it will detect automatically that it is a single file and not part of a package and will know how to import it and serve your FastAPI app. 😎
## Deployment Concepts { #deployment-concepts }
Let's talk again about some of the same [Deployment Concepts](concepts.md) in terms of containers.
Containers are mainly a tool to simplify the process of **building and deploying** an application, but they don't enforce a particular approach to handle these **deployment concepts**, and there are several possible strategies.
The **good news** is that with each different strategy there's a way to cover all of the deployment concepts. 🎉
Let's review these **deployment concepts** in terms of containers:
* HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
## HTTPS { #https }
If we focus just on the **container image** for a FastAPI application (and later the running **container**), HTTPS normally would be handled **externally** by another tool.
It could be another container, for example with [Traefik](https://traefik.io/), handling **HTTPS** and **automatic** acquisition of **certificates**.
/// tip
Traefik has integrations with Docker, Kubernetes, and others, so it's very easy to set up and configure HTTPS for your containers with it.
///
Alternatively, HTTPS could be handled by a cloud provider as one of their services (while still running the application in a container).
## Running on Startup and Restarts { #running-on-startup-and-restarts }
There is normally another tool in charge of **starting and running** your container.
It could be **Docker** directly, **Docker Compose**, **Kubernetes**, a **cloud service**, etc.
In most (or all) cases, there's a simple option to enable running the container on startup and enabling restarts on failures. For example, in Docker, it's the command line option `--restart`.
Without using containers, making applications run on startup and with restarts can be cumbersome and difficult. But when **working with containers** in most cases that functionality is included by default. ✨
## Replication - Number of Processes { #replication-number-of-processes }
If you have a cluster of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Uvicorn with workers) in each container.
One of those distributed container management systems like Kubernetes normally has some integrated way of handling **replication of containers** while still supporting **load balancing** for the incoming requests. All at the **cluster level**.
In those cases, you would probably want to build a **Docker image from scratch** as [explained above](#dockerfile), installing your dependencies, and running **a single Uvicorn process** instead of using multiple Uvicorn workers.
### Load Balancer { #load-balancer }
When using containers, you would normally have some component **listening on the main port**. It could possibly be another container that is also a **TLS Termination Proxy** to handle **HTTPS** or some similar tool.
As this component would take the **load** of requests and distribute that among the workers in a (hopefully) **balanced** way, it is also commonly called a **Load Balancer**.
/// tip
The same **TLS Termination Proxy** component used for HTTPS would probably also be a **Load Balancer**.
///
And when working with containers, the same system you use to start and manage them would already have internal tools to transmit the **network communication** (e.g. HTTP requests) from that **load balancer** (that could also be a **TLS Termination Proxy**) to the container(s) with your app.
### One Load Balancer - Multiple Worker Containers { #one-load-balancer-multiple-worker-containers }
When working with **Kubernetes** or similar distributed container management systems, using their internal networking mechanisms would allow the single **load balancer** that is listening on the main **port** to transmit communication (requests) to possibly **multiple containers** running your app.
Each of these containers running your app would normally have **just one process** (e.g. a Uvicorn process running your FastAPI application). They would all be **identical containers**, running the same thing, but each with its own process, memory, etc. That way you would take advantage of **parallelization** in **different cores** of the CPU, or even in **different machines**.
And the distributed container system with the **load balancer** would **distribute the requests** to each one of the containers with your app **in turns**. So, each request could be handled by one of the multiple **replicated containers** running your app.
And normally this **load balancer** would be able to handle requests that go to *other* apps in your cluster (e.g. to a different domain, or under a different URL path prefix), and would transmit that communication to the right containers for *that other* application running in your cluster.
### One Process per Container { #one-process-per-container }
In this type of scenario, you probably would want to have **a single (Uvicorn) process per container**, as you would already be handling replication at the cluster level.
So, in this case, you **would not** want to have a multiple workers in the container, for example with the `--workers` command line option. You would want to have just a **single Uvicorn process** per container (but probably multiple containers).
Having another process manager inside the container (as would be with multiple workers) would only add **unnecessary complexity** that you are most probably already taking care of with your cluster system.
### Containers with Multiple Processes and Special Cases { #containers-with-multiple-processes-and-special-cases }
Of course, there are **special cases** where you could want to have **a container** with several **Uvicorn worker processes** inside.
In those cases, you can use the `--workers` command line option to set the number of workers that you want to run:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Here we use the `--workers` command line option to set the number of workers to 4.
Here are some examples of when that could make sense:
#### A Simple App { #a-simple-app }
You could want a process manager in the container if your application is **simple enough** that can run it on a **single server**, not a cluster.
#### Docker Compose { #docker-compose }
You could be deploying to a **single server** (not a cluster) with **Docker Compose**, so you wouldn't have an easy way to manage replication of containers (with Docker Compose) while preserving the shared network and **load balancing**.
Then you could want to have **a single container** with a **process manager** starting **several worker processes** inside.
---
The main point is, **none** of these are **rules written in stone** that you have to blindly follow. You can use these ideas to **evaluate your own use case** and decide what is the best approach for your system, checking out how to manage the concepts of:
* Security - HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
## Memory { #memory }
If you run **a single process per container** you will have a more or less well-defined, stable, and limited amount of memory consumed by each of those containers (more than one if they are replicated).
And then you can set those same memory limits and requirements in your configurations for your container management system (for example in **Kubernetes**). That way it will be able to **replicate the containers** in the **available machines** taking into account the amount of memory needed by them, and the amount available in the machines in the cluster.
If your application is **simple**, this will probably **not be a problem**, and you might not need to specify hard memory limits. But if you are **using a lot of memory** (for example with **machine learning** models), you should check how much memory you are consuming and adjust the **number of containers** that runs in **each machine** (and maybe add more machines to your cluster).
If you run **multiple processes per container** you will have to make sure that the number of processes started doesn't **consume more memory** than what is available.
## Previous Steps Before Starting and Containers { #previous-steps-before-starting-and-containers }
If you are using containers (e.g. Docker, Kubernetes), then there are two main approaches you can use.
### Multiple Containers { #multiple-containers }
If you have **multiple containers**, probably each one running a **single process** (for example, in a **Kubernetes** cluster), then you would probably want to have a **separate container** doing the work of the **previous steps** in a single container, running a single process, **before** running the replicated worker containers.
/// info
If you are using Kubernetes, this would probably be an [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
If in your use case there's no problem in running those previous steps **multiple times in parallel** (for example if you are not running database migrations, but just checking if the database is ready yet), then you could also just put them in each container right before starting the main process.
### Single Container { #single-container }
If you have a simple setup, with a **single container** that then starts multiple **worker processes** (or also just one process), then you could run those previous steps in the same container, right before starting the process with the app.
### Base Docker Image { #base-docker-image }
There used to be an official FastAPI Docker image: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). But it is now deprecated. ⛔️
You should probably **not** use this base Docker image (or any other similar one).
If you are using **Kubernetes** (or others) and you are already setting **replication** at the cluster level, with multiple **containers**. In those cases, you are better off **building an image from scratch** as described above: [Build a Docker Image for FastAPI](#build-a-docker-image-for-fastapi).
And if you need to have multiple workers, you can simply use the `--workers` command line option.
/// note | Technical Details
The Docker image was created when Uvicorn didn't support managing and restarting dead workers, so it was needed to use Gunicorn with Uvicorn, which added quite some complexity, just to have Gunicorn manage and restart the Uvicorn worker processes.
But now that Uvicorn (and the `fastapi` command) support using `--workers`, there's no reason to use a base Docker image instead of building your own (it's pretty much the same amount of code 😅).
///
## Deploy the Container Image { #deploy-the-container-image }
After having a Container (Docker) Image there are several ways to deploy it.
For example:
* With **Docker Compose** in a single server
* With a **Kubernetes** cluster
* With a Docker Swarm Mode cluster
* With another tool like Nomad
* With a cloud service that takes your container image and deploys it
## Docker Image with `uv` { #docker-image-with-uv }
If you are using [uv](https://github.com/astral-sh/uv) to install and manage your project, you can follow their [uv Docker guide](https://docs.astral.sh/uv/guides/integration/docker/).
## Recap { #recap }
Using container systems (e.g. with **Docker** and **Kubernetes**) it becomes fairly straightforward to handle all the **deployment concepts**:
* HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
In most cases, you probably won't want to use any base image, and instead **build a container image from scratch** based on the official Python Docker image.
Taking care of the **order** of instructions in the `Dockerfile` and the **Docker cache** you can **minimize build times**, to maximize your productivity (and avoid boredom). 😎
================================================
FILE: docs/en/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
You can deploy your FastAPI app to [FastAPI Cloud](https://fastapicloud.com) with **one command**, go and join the waiting list if you haven't. 🚀
## Login { #login }
Make sure you already have a **FastAPI Cloud** account (we invited you from the waiting list 😉).
Then log in:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Deploy { #deploy }
Now deploy your app, with **one command**:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
That's it! Now you can access your app at that URL. ✨
## About FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** is built by the same author and team behind **FastAPI**.
It streamlines the process of **building**, **deploying**, and **accessing** an API with minimal effort.
It brings the same **developer experience** of building apps with FastAPI to **deploying** them to the cloud. 🎉
It will also take care of most of the things you would need when deploying an app, like:
* HTTPS
* Replication, with autoscaling based on requests
* etc.
FastAPI Cloud is the primary sponsor and funding provider for the *FastAPI and friends* open source projects. ✨
## Deploy to other cloud providers { #deploy-to-other-cloud-providers }
FastAPI is open source and based on standards. You can deploy FastAPI apps to any cloud provider you choose.
Follow your cloud provider's guides to deploy FastAPI apps with them. 🤓
## Deploy your own server { #deploy-your-own-server }
I will also teach you later in this **Deployment** guide all the details, so you can understand what is going on, what needs to happen, or how to deploy FastAPI apps on your own, also with your own servers. 🤓
================================================
FILE: docs/en/docs/deployment/https.md
================================================
# About HTTPS { #about-https }
It is easy to assume that HTTPS is something that is just "enabled" or not.
But it is way more complex than that.
/// tip
If you are in a hurry or don't care, continue with the next sections for step by step instructions to set everything up with different techniques.
///
To **learn the basics of HTTPS**, from a consumer perspective, check [https://howhttps.works/](https://howhttps.works/).
Now, from a **developer's perspective**, here are several things to keep in mind while thinking about HTTPS:
* For HTTPS, **the server** needs to **have "certificates"** generated by a **third party**.
* Those certificates are actually **acquired** from the third party, not "generated".
* Certificates have a **lifetime**.
* They **expire**.
* And then they need to be **renewed**, **acquired again** from the third party.
* The encryption of the connection happens at the **TCP level**.
* That's one layer **below HTTP**.
* So, the **certificate and encryption** handling is done **before HTTP**.
* **TCP doesn't know about "domains"**. Only about IP addresses.
* The information about the **specific domain** requested goes in the **HTTP data**.
* The **HTTPS certificates** "certify" a **certain domain**, but the protocol and encryption happen at the TCP level, **before knowing** which domain is being dealt with.
* **By default**, that would mean that you can only have **one HTTPS certificate per IP address**.
* No matter how big your server is or how small each application you have on it might be.
* There is a **solution** to this, however.
* There's an **extension** to the **TLS** protocol (the one handling the encryption at the TCP level, before HTTP) called **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* This SNI extension allows one single server (with a **single IP address**) to have **several HTTPS certificates** and serve **multiple HTTPS domains/applications**.
* For this to work, a **single** component (program) running on the server, listening on the **public IP address**, must have **all the HTTPS certificates** in the server.
* **After** obtaining a secure connection, the communication protocol is **still HTTP**.
* The contents are **encrypted**, even though they are being sent with the **HTTP protocol**.
It is a common practice to have **one program/HTTP server** running on the server (the machine, host, etc.) and **managing all the HTTPS parts**: receiving the **encrypted HTTPS requests**, sending the **decrypted HTTP requests** to the actual HTTP application running in the same server (the **FastAPI** application, in this case), take the **HTTP response** from the application, **encrypt it** using the appropriate **HTTPS certificate** and sending it back to the client using **HTTPS**. This server is often called a **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)**.
Some of the options you could use as a TLS Termination Proxy are:
* Traefik (that can also handle certificate renewals)
* Caddy (that can also handle certificate renewals)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Before Let's Encrypt, these **HTTPS certificates** were sold by trusted third parties.
The process to acquire one of these certificates used to be cumbersome, require quite some paperwork and the certificates were quite expensive.
But then **[Let's Encrypt](https://letsencrypt.org/)** was created.
It is a project from the Linux Foundation. It provides **HTTPS certificates for free**, in an automated way. These certificates use all the standard cryptographic security, and are short-lived (about 3 months), so the **security is actually better** because of their reduced lifespan.
The domains are securely verified and the certificates are generated automatically. This also allows automating the renewal of these certificates.
The idea is to automate the acquisition and renewal of these certificates so that you can have **secure HTTPS, for free, forever**.
## HTTPS for Developers { #https-for-developers }
Here's an example of how an HTTPS API could look like, step by step, paying attention mainly to the ideas important for developers.
### Domain Name { #domain-name }
It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider).
You would probably get a cloud server (a virtual machine) or something similar, and it would have a fixed **public IP address**.
In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**.
You would probably do this just once, the first time, when setting everything up.
/// tip
This Domain Name part is way before HTTPS, but as everything depends on the domain and the IP address, it's worth mentioning it here.
///
### DNS { #dns }
Now let's focus on all the actual HTTPS parts.
First, the browser would check with the **DNS servers** what is the **IP for the domain**, in this case, `someapp.example.com`.
The DNS servers would tell the browser to use some specific **IP address**. That would be the public IP address used by your server, that you configured in the DNS servers.
### TLS Handshake Start { #tls-handshake-start }
The browser would then communicate with that IP address on **port 443** (the HTTPS port).
The first part of the communication is just to establish the connection between the client and the server and to decide the cryptographic keys they will use, etc.
This interaction between the client and the server to establish the TLS connection is called the **TLS handshake**.
### TLS with SNI Extension { #tls-with-sni-extension }
**Only one process** in the server can be listening on a specific **port** in a specific **IP address**. There could be other processes listening on other ports in the same IP address, but only one for each combination of IP address and port.
TLS (HTTPS) uses the specific port `443` by default. So that's the port we would need.
As only one process can be listening on this port, the process that would do it would be the **TLS Termination Proxy**.
The TLS Termination Proxy would have access to one or more **TLS certificates** (HTTPS certificates).
Using the **SNI extension** discussed above, the TLS Termination Proxy would check which of the TLS (HTTPS) certificates available it should use for this connection, using the one that matches the domain expected by the client.
In this case, it would use the certificate for `someapp.example.com`.
The client already **trusts** the entity that generated that TLS certificate (in this case Let's Encrypt, but we'll see about that later), so it can **verify** that the certificate is valid.
Then, using the certificate, the client and the TLS Termination Proxy **decide how to encrypt** the rest of the **TCP communication**. This completes the **TLS Handshake** part.
After this, the client and the server have an **encrypted TCP connection**, this is what TLS provides. And then they can use that connection to start the actual **HTTP communication**.
And that's what **HTTPS** is, it's just plain **HTTP** inside a **secure TLS connection** instead of a pure (unencrypted) TCP connection.
/// tip
Notice that the encryption of the communication happens at the **TCP level**, not at the HTTP level.
///
### HTTPS Request { #https-request }
Now that the client and server (specifically the browser and the TLS Termination Proxy) have an **encrypted TCP connection**, they can start the **HTTP communication**.
So, the client sends an **HTTPS request**. This is just an HTTP request through an encrypted TLS connection.
### Decrypt the Request { #decrypt-the-request }
The TLS Termination Proxy would use the encryption agreed to **decrypt the request**, and would transmit the **plain (decrypted) HTTP request** to the process running the application (for example a process with Uvicorn running the FastAPI application).
### HTTP Response { #http-response }
The application would process the request and send a **plain (unencrypted) HTTP response** to the TLS Termination Proxy.
### HTTPS Response { #https-response }
The TLS Termination Proxy would then **encrypt the response** using the cryptography agreed before (that started with the certificate for `someapp.example.com`), and send it back to the browser.
Next, the browser would verify that the response is valid and encrypted with the right cryptographic key, etc. It would then **decrypt the response** and process it.
The client (browser) will know that the response comes from the correct server because it is using the cryptography they agreed using the **HTTPS certificate** before.
### Multiple Applications { #multiple-applications }
In the same server (or servers), there could be **multiple applications**, for example, other API programs or a database.
Only one process can be handling the specific IP and port (the TLS Termination Proxy in our example) but the other applications/processes can be running on the server(s) too, as long as they don't try to use the same **combination of public IP and port**.
That way, the TLS Termination Proxy could handle HTTPS and certificates for **multiple domains**, for multiple applications, and then transmit the requests to the right application in each case.
### Certificate Renewal { #certificate-renewal }
At some point in the future, each certificate would **expire** (about 3 months after acquiring it).
And then, there would be another program (in some cases it's another program, in some cases it could be the same TLS Termination Proxy) that would talk to Let's Encrypt, and renew the certificate(s).
The **TLS certificates** are **associated with a domain name**, not with an IP address.
So, to renew the certificates, the renewal program needs to **prove** to the authority (Let's Encrypt) that it indeed **"owns" and controls that domain**.
To do that, and to accommodate different application needs, there are several ways it can do it. Some popular ways are:
* **Modify some DNS records**.
* For this, the renewal program needs to support the APIs of the DNS provider, so, depending on the DNS provider you are using, this might or might not be an option.
* **Run as a server** (at least during the certificate acquisition process) on the public IP address associated with the domain.
* As we said above, only one process can be listening on a specific IP and port.
* This is one of the reasons why it's very useful when the same TLS Termination Proxy also takes care of the certificate renewal process.
* Otherwise, you might have to stop the TLS Termination Proxy momentarily, start the renewal program to acquire the certificates, then configure them with the TLS Termination Proxy, and then restart the TLS Termination Proxy. This is not ideal, as your app(s) will not be available during the time that the TLS Termination Proxy is off.
All this renewal process, while still serving the app, is one of the main reasons why you would want to have a **separate system to handle HTTPS** with a TLS Termination Proxy instead of just using the TLS certificates with the application server directly (e.g. Uvicorn).
## Proxy Forwarded Headers { #proxy-forwarded-headers }
When using a proxy to handle HTTPS, your **application server** (for example Uvicorn via FastAPI CLI) doesn't known anything about the HTTPS process, it communicates with plain HTTP with the **TLS Termination Proxy**.
This **proxy** would normally set some HTTP headers on the fly before transmitting the request to the **application server**, to let the application server know that the request is being **forwarded** by the proxy.
/// note | Technical Details
The proxy headers are:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Nevertheless, as the **application server** doesn't know it is behind a trusted **proxy**, by default, it wouldn't trust those headers.
But you can configure the **application server** to trust the *forwarded* headers sent by the **proxy**. If you are using FastAPI CLI, you can use the *CLI Option* `--forwarded-allow-ips` to tell it from which IPs it should trust those *forwarded* headers.
For example, if the **application server** is only receiving communication from the trusted **proxy**, you can set it to `--forwarded-allow-ips="*"` to make it trust all incoming IPs, as it will only receive requests from whatever is the IP used by the **proxy**.
This way the application would be able to know what is its own public URL, if it is using HTTPS, the domain, etc.
This would be useful for example to properly handle redirects.
/// tip
You can learn more about this in the documentation for [Behind a Proxy - Enable Proxy Forwarded Headers](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Recap { #recap }
Having **HTTPS** is very important, and quite **critical** in most cases. Most of the effort you as a developer have to put around HTTPS is just about **understanding these concepts** and how they work.
But once you know the basic information of **HTTPS for developers** you can easily combine and configure different tools to help you manage everything in a simple way.
In some of the next chapters, I'll show you several concrete examples of how to set up **HTTPS** for **FastAPI** applications. 🔒
================================================
FILE: docs/en/docs/deployment/index.md
================================================
# Deployment { #deployment }
Deploying a **FastAPI** application is relatively easy.
## What Does Deployment Mean { #what-does-deployment-mean }
To **deploy** an application means to perform the necessary steps to make it **available to the users**.
For a **web API**, it normally involves putting it in a **remote machine**, with a **server program** that provides good performance, stability, etc, so that your **users** can **access** the application efficiently and without interruptions or problems.
This is in contrast to the **development** stages, where you are constantly changing the code, breaking it and fixing it, stopping and restarting the development server, etc.
## Deployment Strategies { #deployment-strategies }
There are several ways to do it depending on your specific use case and the tools that you use.
You could **deploy a server** yourself using a combination of tools, you could use a **cloud service** that does part of the work for you, or other possible options.
For example, we, the team behind FastAPI, built [**FastAPI Cloud**](https://fastapicloud.com), to make deploying FastAPI apps to the cloud as streamlined as possible, with the same developer experience of working with FastAPI.
I will show you some of the main concepts you should probably keep in mind when deploying a **FastAPI** application (although most of it applies to any other type of web application).
You will see more details to keep in mind and some of the techniques to do it in the next sections. ✨
================================================
FILE: docs/en/docs/deployment/manually.md
================================================
# Run a Server Manually { #run-a-server-manually }
## Use the `fastapi run` Command { #use-the-fastapi-run-command }
In short, use `fastapi run` to serve your FastAPI application:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
That would work for most of the cases. 😎
You could use that command for example to start your **FastAPI** app in a container, in a server, etc.
## ASGI Servers { #asgi-servers }
Let's go a little deeper into the details.
FastAPI uses a standard for building Python web frameworks and servers called ASGI. FastAPI is an ASGI web framework.
The main thing you need to run a **FastAPI** application (or any other ASGI application) in a remote server machine is an ASGI server program like **Uvicorn**, this is the one that comes by default in the `fastapi` command.
There are several alternatives, including:
* [Uvicorn](https://www.uvicorn.dev/): a high performance ASGI server.
* [Hypercorn](https://hypercorn.readthedocs.io/): an ASGI server compatible with HTTP/2 and Trio among other features.
* [Daphne](https://github.com/django/daphne): the ASGI server built for Django Channels.
* [Granian](https://github.com/emmett-framework/granian): A Rust HTTP server for Python applications.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit is a lightweight and versatile web application runtime.
## Server Machine and Server Program { #server-machine-and-server-program }
There's a small detail about names to keep in mind. 💡
The word "**server**" is commonly used to refer to both the remote/cloud computer (the physical or virtual machine) and also the program that is running on that machine (e.g. Uvicorn).
Just keep in mind that when you read "server" in general, it could refer to one of those two things.
When referring to the remote machine, it's common to call it **server**, but also **machine**, **VM** (virtual machine), **node**. Those all refer to some type of remote machine, normally running Linux, where you run programs.
## Install the Server Program { #install-the-server-program }
When you install FastAPI, it comes with a production server, Uvicorn, and you can start it with the `fastapi run` command.
But you can also install an ASGI server manually.
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then you can install the server application.
For example, to install Uvicorn:
A similar process would apply to any other ASGI server program.
/// tip
By adding the `standard`, Uvicorn will install and use some recommended extra dependencies.
That including `uvloop`, the high-performance drop-in replacement for `asyncio`, that provides the big concurrency performance boost.
When you install FastAPI with something like `pip install "fastapi[standard]"` you already get `uvicorn[standard]` as well.
///
## Run the Server Program { #run-the-server-program }
If you installed an ASGI server manually, you would normally need to pass an import string in a special format for it to import your FastAPI application:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note
The command `uvicorn main:app` refers to:
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
It is equivalent to:
```Python
from main import app
```
///
Each alternative ASGI server program would have a similar command, you can read more in their respective documentation.
/// warning
Uvicorn and other servers support a `--reload` option that is useful during development.
The `--reload` option consumes much more resources, is more unstable, etc.
It helps a lot during **development**, but you **shouldn't** use it in **production**.
///
## Deployment Concepts { #deployment-concepts }
These examples run the server program (e.g Uvicorn), starting **a single process**, listening on all the IPs (`0.0.0.0`) on a predefined port (e.g. `80`).
This is the basic idea. But you will probably want to take care of some additional things, like:
* Security - HTTPS
* Running on startup
* Restarts
* Replication (the number of processes running)
* Memory
* Previous steps before starting
I'll tell you more about each of these concepts, how to think about them, and some concrete examples with strategies to handle them in the next chapters. 🚀
================================================
FILE: docs/en/docs/deployment/server-workers.md
================================================
# Server Workers - Uvicorn with Workers { #server-workers-uvicorn-with-workers }
Let's check back those deployment concepts from before:
* Security - HTTPS
* Running on startup
* Restarts
* **Replication (the number of processes running)**
* Memory
* Previous steps before starting
Up to this point, with all the tutorials in the docs, you have probably been running a **server program**, for example, using the `fastapi` command, that runs Uvicorn, running a **single process**.
When deploying applications you will probably want to have some **replication of processes** to take advantage of **multiple cores** and to be able to handle more requests.
As you saw in the previous chapter about [Deployment Concepts](concepts.md), there are multiple strategies you can use.
Here I'll show you how to use **Uvicorn** with **worker processes** using the `fastapi` command or the `uvicorn` command directly.
/// info
If you are using containers, for example with Docker or Kubernetes, I'll tell you more about that in the next chapter: [FastAPI in Containers - Docker](docker.md).
In particular, when running on **Kubernetes** you will probably **not** want to use workers and instead run **a single Uvicorn process per container**, but I'll tell you about it later in that chapter.
///
## Multiple Workers { #multiple-workers }
You can start multiple workers with the `--workers` command line option:
//// tab | `fastapi`
If you use the `fastapi` command:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
If you prefer to use the `uvicorn` command directly:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
The only new option here is `--workers` telling Uvicorn to start 4 worker processes.
You can also see that it shows the **PID** of each process, `27365` for the parent process (this is the **process manager**) and one for each worker process: `27368`, `27369`, `27370`, and `27367`.
## Deployment Concepts { #deployment-concepts }
Here you saw how to use multiple **workers** to **parallelize** the execution of the application, take advantage of **multiple cores** in the CPU, and be able to serve **more requests**.
From the list of deployment concepts from above, using workers would mainly help with the **replication** part, and a little bit with the **restarts**, but you still need to take care of the others:
* **Security - HTTPS**
* **Running on startup**
* ***Restarts***
* Replication (the number of processes running)
* **Memory**
* **Previous steps before starting**
## Containers and Docker { #containers-and-docker }
In the next chapter about [FastAPI in Containers - Docker](docker.md) I'll explain some strategies you could use to handle the other **deployment concepts**.
I'll show you how to **build your own image from scratch** to run a single Uvicorn process. It is a simple process and is probably what you would want to do when using a distributed container management system like **Kubernetes**.
## Recap { #recap }
You can use multiple worker processes with the `--workers` CLI option with the `fastapi` or `uvicorn` commands to take advantage of **multi-core CPUs**, to run **multiple processes in parallel**.
You could use these tools and ideas if you are setting up **your own deployment system** while taking care of the other deployment concepts yourself.
Check out the next chapter to learn about **FastAPI** with containers (e.g. Docker and Kubernetes). You will see that those tools have simple ways to solve the other **deployment concepts** as well. ✨
================================================
FILE: docs/en/docs/deployment/versions.md
================================================
# About FastAPI versions { #about-fastapi-versions }
**FastAPI** is already being used in production in many applications and systems. And the test coverage is kept at 100%. But its development is still moving quickly.
New features are added frequently, bugs are fixed regularly, and the code is still continuously improving.
That's why the current versions are still `0.x.x`, this reflects that each version could potentially have breaking changes. This follows the [Semantic Versioning](https://semver.org/) conventions.
You can create production applications with **FastAPI** right now (and you have probably been doing it for some time), you just have to make sure that you use a version that works correctly with the rest of your code.
## Pin your `fastapi` version { #pin-your-fastapi-version }
The first thing you should do is to "pin" the version of **FastAPI** you are using to the specific latest version that you know works correctly for your application.
For example, let's say you are using version `0.112.0` in your app.
If you use a `requirements.txt` file you could specify the version with:
```txt
fastapi[standard]==0.112.0
```
that would mean that you would use exactly the version `0.112.0`.
Or you could also pin it with:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
that would mean that you would use the versions `0.112.0` or above, but less than `0.113.0`, for example, a version `0.112.2` would still be accepted.
If you use any other tool to manage your installations, like `uv`, Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages.
## Available versions { #available-versions }
You can see the available versions (e.g. to check what is the current latest) in the [Release Notes](../release-notes.md).
## About versions { #about-versions }
Following the Semantic Versioning conventions, any version below `1.0.0` could potentially add breaking changes.
FastAPI also follows the convention that any "PATCH" version change is for bug fixes and non-breaking changes.
/// tip
The "PATCH" is the last number, for example, in `0.2.3`, the PATCH version is `3`.
///
So, you should be able to pin to a version like:
```txt
fastapi>=0.45.0,<0.46.0
```
Breaking changes and new features are added in "MINOR" versions.
/// tip
The "MINOR" is the number in the middle, for example, in `0.2.3`, the MINOR version is `2`.
///
## Upgrading the FastAPI versions { #upgrading-the-fastapi-versions }
You should add tests for your app.
With **FastAPI** it's very easy (thanks to Starlette), check the docs: [Testing](../tutorial/testing.md)
After you have tests, then you can upgrade the **FastAPI** version to a more recent one, and make sure that all your code is working correctly by running your tests.
If everything is working, or after you make the necessary changes, and all your tests are passing, then you can pin your `fastapi` to that new recent version.
## About Starlette { #about-starlette }
You shouldn't pin the version of `starlette`.
Different versions of **FastAPI** will use a specific newer version of Starlette.
So, you can just let **FastAPI** use the correct Starlette version.
## About Pydantic { #about-pydantic }
Pydantic includes the tests for **FastAPI** with its own tests, so new versions of Pydantic (above `1.0.0`) are always compatible with FastAPI.
You can pin Pydantic to any version above `1.0.0` that works for you.
For example:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/en/docs/editor-support.md
================================================
# Editor Support { #editor-support }
The official [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) enhances your FastAPI development workflow with *path operation* discovery, navigation, as well as FastAPI Cloud deployment, and live log streaming.
For more details about the extension, refer to the README on the [GitHub repository](https://github.com/fastapi/fastapi-vscode).
## Setup and Installation { #setup-and-installation }
The **FastAPI Extension** is available for both [VS Code](https://code.visualstudio.com/) and [Cursor](https://www.cursor.com/). It can be installed directly from the Extensions panel in each editor by searching for "FastAPI" and selecting the extension published by **FastAPI Labs**. The extension also works in browser-based editors such as [vscode.dev](https://vscode.dev) and [github.dev](https://github.dev).
### Application Discovery { #application-discovery }
By default, the extension will automatically discover FastAPI applications in your workspace by scanning for files that instantiate `FastAPI()`. If auto-detection doesn't work for your project structure, you can specify an entrypoint via `[tool.fastapi]` in `pyproject.toml` or the `fastapi.entryPoint` VS Code setting using module notation (e.g. `myapp.main:app`).
## Features { #features }
- **Path Operation Explorer** - A sidebar tree view of all *path operations* in your application. Click to jump to any route or router definition.
- **Route Search** - Search by path, method, or name with Ctrl + Shift + E (on macOS: Cmd + Shift + E).
- **CodeLens Navigation** - Clickable links above test client calls (e.g. `client.get('/items')`) that jump to the matching *path operation* for quick navigation between tests and implementation.
- **Deploy to FastAPI Cloud** - One-click deployment of your app to [FastAPI Cloud](https://fastapicloud.com/).
- **Stream Application Logs** - Real-time log streaming from your FastAPI Cloud-deployed application with level filtering and text search.
If you'd like to familiarize yourself with the extension's features, you can checkout the extension walkthrough by opening the Command Palette (Ctrl + Shift + P or on macOS: Cmd + Shift + P) and selecting "Welcome: Open walkthrough..." and then choosing the "Get started with FastAPI" walkthrough.
================================================
FILE: docs/en/docs/environment-variables.md
================================================
# Environment Variables { #environment-variables }
/// tip
If you already know what "environment variables" are and how to use them, feel free to skip this.
///
An environment variable (also known as "**env var**") is a variable that lives **outside** of the Python code, in the **operating system**, and could be read by your Python code (or by other programs as well).
Environment variables could be useful for handling application **settings**, as part of the **installation** of Python, etc.
## Create and Use Env Vars { #create-and-use-env-vars }
You can **create** and use environment variables in the **shell (terminal)**, without needing Python:
//// tab | Linux, macOS, Windows Bash
```console
// You could create an env var MY_NAME with
$ export MY_NAME="Wade Wilson"
// Then you could use it with other programs, like
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Create an env var MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Use it with other programs, like
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Read env vars in Python { #read-env-vars-in-python }
You could also create environment variables **outside** of Python, in the terminal (or with any other method), and then **read them in Python**.
For example you could have a file `main.py` with:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip
The second argument to [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) is the default value to return.
If not provided, it's `None` by default, here we provide `"World"` as the default value to use.
///
Then you could call that Python program:
//// tab | Linux, macOS, Windows Bash
```console
// Here we don't set the env var yet
$ python main.py
// As we didn't set the env var, we get the default value
Hello World from Python
// But if we create an environment variable first
$ export MY_NAME="Wade Wilson"
// And then call the program again
$ python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Here we don't set the env var yet
$ python main.py
// As we didn't set the env var, we get the default value
Hello World from Python
// But if we create an environment variable first
$ $Env:MY_NAME = "Wade Wilson"
// And then call the program again
$ python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
```
////
As environment variables can be set outside of the code, but can be read by the code, and don't have to be stored (committed to `git`) with the rest of the files, it's common to use them for configurations or **settings**.
You can also create an environment variable only for a **specific program invocation**, that is only available to that program, and only for its duration.
To do that, create it right before the program itself, on the same line:
```console
// Create an env var MY_NAME in line for this program call
$ MY_NAME="Wade Wilson" python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
// The env var no longer exists afterwards
$ python main.py
Hello World from Python
```
/// tip
You can read more about it at [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Types and Validation { #types-and-validation }
These environment variables can only handle **text strings**, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS).
That means that **any value** read in Python from an environment variable **will be a `str`**, and any conversion to a different type or any validation has to be done in code.
You will learn more about using environment variables for handling **application settings** in the [Advanced User Guide - Settings and Environment Variables](./advanced/settings.md).
## `PATH` Environment Variable { #path-environment-variable }
There is a **special** environment variable called **`PATH`** that is used by the operating systems (Linux, macOS, Windows) to find programs to run.
The value of the variable `PATH` is a long string that is made of directories separated by a colon `:` on Linux and macOS, and by a semicolon `;` on Windows.
For example, the `PATH` environment variable could look like this:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
This means that the system should look for programs in the directories:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
This means that the system should look for programs in the directories:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
When you type a **command** in the terminal, the operating system **looks for** the program in **each of those directories** listed in the `PATH` environment variable.
For example, when you type `python` in the terminal, the operating system looks for a program called `python` in the **first directory** in that list.
If it finds it, then it will **use it**. Otherwise it keeps looking in the **other directories**.
### Installing Python and Updating the `PATH` { #installing-python-and-updating-the-path }
When you install Python, you might be asked if you want to update the `PATH` environment variable.
//// tab | Linux, macOS
Let's say you install Python and it ends up in a directory `/opt/custompython/bin`.
If you say yes to update the `PATH` environment variable, then the installer will add `/opt/custompython/bin` to the `PATH` environment variable.
It could look like this:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
This way, when you type `python` in the terminal, the system will find the Python program in `/opt/custompython/bin` (the last directory) and use that one.
////
//// tab | Windows
Let's say you install Python and it ends up in a directory `C:\opt\custompython\bin`.
If you say yes to update the `PATH` environment variable, then the installer will add `C:\opt\custompython\bin` to the `PATH` environment variable.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
This way, when you type `python` in the terminal, the system will find the Python program in `C:\opt\custompython\bin` (the last directory) and use that one.
////
So, if you type:
```console
$ python
```
//// tab | Linux, macOS
The system will **find** the `python` program in `/opt/custompython/bin` and run it.
It would be roughly equivalent to typing:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
The system will **find** the `python` program in `C:\opt\custompython\bin\python` and run it.
It would be roughly equivalent to typing:
```console
$ C:\opt\custompython\bin\python
```
////
This information will be useful when learning about [Virtual Environments](virtual-environments.md).
## Conclusion { #conclusion }
With this you should have a basic understanding of what **environment variables** are and how to use them in Python.
You can also read more about them in the [Wikipedia for Environment Variable](https://en.wikipedia.org/wiki/Environment_variable).
In many cases it's not very obvious how environment variables would be useful and applicable right away. But they keep showing up in many different scenarios when you are developing, so it's good to know about them.
For example, you will need this information in the next section, about [Virtual Environments](virtual-environments.md).
================================================
FILE: docs/en/docs/external-links.md
================================================
# External Links
**FastAPI** has a great community constantly growing.
There are many posts, articles, tools, and projects, related to **FastAPI**.
You could easily use a search engine or video platform to find many resources related to FastAPI.
/// info
Before, this page used to list links to external articles.
But now that FastAPI is the backend framework with the most GitHub stars across languages, and the most starred and used framework in Python, it no longer makes sense to attempt to list all articles written about it.
///
## GitHub Repositories
Most starred [GitHub repositories with the topic `fastapi`](https://github.com/topics/fastapi):
{% for repo in topic_repos %}
★ {{repo.stars}} - {{repo.name}} by @{{repo.owner_login}}.
{% endfor %}
================================================
FILE: docs/en/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** is a command line program that you can use to serve your FastAPI app, manage your FastAPI project, and more.
When you install FastAPI (e.g. with `pip install "fastapi[standard]"`), it comes with a command line program you can run in the terminal.
To run your FastAPI app for development, you can use the `fastapi dev` command:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip
For production you would use `fastapi run` instead of `fastapi dev`. 🚀
///
Internally, **FastAPI CLI** uses [Uvicorn](https://www.uvicorn.dev), a high-performance, production-ready, ASGI server. 😎
The `fastapi` CLI will try to detect automatically the FastAPI app to run, assuming it's an object called `app` in a file `main.py` (or a couple other variants).
But you can configure explicitly the app to use.
## Configure the app `entrypoint` in `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
You can configure where your app is located in a `pyproject.toml` file like:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
That `entrypoint` will tell the `fastapi` command that it should import the app like:
```python
from main import app
```
If your code was structured like:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Then you would set the `entrypoint` as:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
which would be equivalent to:
```python
from backend.main import app
```
### `fastapi dev` with path { #fastapi-dev-with-path }
You can also pass the file path to the `fastapi dev` command, and it will guess the FastAPI app object to use:
```console
$ fastapi dev main.py
```
But you would have to remember to pass the correct path every time you call the `fastapi` command.
Additionally, other tools might not be able to find it, for example the [VS Code Extension](editor-support.md) or [FastAPI Cloud](https://fastapicloud.com), so it is recommended to use the `entrypoint` in `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
Running `fastapi dev` initiates development mode.
By default, **auto-reload** is enabled, automatically reloading the server when you make changes to your code. This is resource-intensive and could be less stable than when it's disabled. You should only use it for development. It also listens on the IP address `127.0.0.1`, which is the IP for your machine to communicate with itself alone (`localhost`).
## `fastapi run` { #fastapi-run }
Executing `fastapi run` starts FastAPI in production mode.
By default, **auto-reload** is disabled. It also listens on the IP address `0.0.0.0`, which means all the available IP addresses, this way it will be publicly accessible to anyone that can communicate with the machine. This is how you would normally run it in production, for example, in a container.
In most cases you would (and should) have a "termination proxy" handling HTTPS for you on top, this will depend on how you deploy your application, your provider might do this for you, or you might need to set it up yourself.
/// tip
You can learn more about it in the [deployment documentation](deployment/index.md).
///
================================================
FILE: docs/en/docs/fastapi-people.md
================================================
---
hide:
- navigation
---
# FastAPI People
FastAPI has an amazing community that welcomes people from all backgrounds.
## Creator
Hey! 👋
This is me:
I'm the creator of **FastAPI**. You can read more about that in [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author).
...But here I want to show you the community.
---
**FastAPI** receives a lot of support from the community. And I want to highlight their contributions.
These are the people that:
* [Help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github).
* [Create Pull Requests](help-fastapi.md#create-a-pull-request).
* Review Pull Requests, [especially important for translations](contributing.md#translations).
* Help [manage the repository](management-tasks.md) (team members).
All these tasks help maintain the repository.
A round of applause to them. 👏 🙇
## Team
This is the current list of team members. 😎
They have different levels of involvement and permissions, they can perform [repository management tasks](./management-tasks.md) and together we [manage the FastAPI repository](./management.md).
Although the team members have the permissions to perform privileged tasks, all the [help from others maintaining FastAPI](./help-fastapi.md#help-maintain-fastapi) is very much appreciated! 🙇♂️
## FastAPI Experts
These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github). 🙇
They have proven to be **FastAPI Experts** by helping many others. ✨
/// tip
You could become an official FastAPI Expert too!
Just [help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github). 🤓
///
You can see the **FastAPI Experts** for:
* [Last Month](#fastapi-experts-last-month) 🤓
* [3 Months](#fastapi-experts-3-months) 😎
* [6 Months](#fastapi-experts-6-months) 🧐
* [1 Year](#fastapi-experts-1-year) 🧑🔬
* [**All Time**](#fastapi-experts-all-time) 🧙
### FastAPI Experts - Last Month
These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last month. 🤓
{% for user in people.last_month_experts[:10] %}
{% if user.login not in skip_users %}
### FastAPI Experts - 3 Months
These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last 3 months. 😎
{% for user in people.three_months_experts[:10] %}
{% if user.login not in skip_users %}
### FastAPI Experts - 6 Months
These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last 6 months. 🧐
{% for user in people.six_months_experts[:10] %}
{% if user.login not in skip_users %}
### FastAPI Experts - 1 Year
These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last year. 🧑🔬
{% for user in people.one_year_experts[:20] %}
{% if user.login not in skip_users %}
### FastAPI Experts - All Time
Here are the all time **FastAPI Experts**. 🤓🤯
These are the users that have [helped others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) through *all time*. 🧙
{% for user in people.experts[:50] %}
{% if user.login not in skip_users %}
## Top Contributors
Here are the **Top Contributors**. 👷
These users have [created the most Pull Requests](help-fastapi.md#create-a-pull-request) that have been *merged*.
They have contributed source code, documentation, etc. 📦
{% for user in (contributors.values() | list)[:50] %}
{% if user.login not in skip_users %}
There are hundreds of other contributors, you can see them all in the [FastAPI GitHub Contributors page](https://github.com/fastapi/fastapi/graphs/contributors). 👷
## Top Translation Reviewers
These users are the **Top Translation Reviewers**. 🕵️
Translation reviewers have the [**power to approve translations**](contributing.md#translations) of the documentation. Without them, there wouldn't be documentation in several other languages.
{% for user in (translation_reviewers.values() | list)[:50] %}
{% if user.login not in skip_users %}
## Sponsors
These are the **Sponsors**. 😎
They are supporting my work with **FastAPI** (and others), mainly through [GitHub Sponsors](https://github.com/sponsors/tiangolo).
{% if sponsors %}
{% if sponsors.gold %}
### Gold Sponsors
{% for sponsor in sponsors.gold -%}
{% endfor %}
{% endif %}
{% if sponsors.silver %}
### Silver Sponsors
{% for sponsor in sponsors.silver -%}
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Bronze Sponsors
{% for sponsor in sponsors.bronze -%}
{% endfor %}
{% endif %}
{% endif %}
### Individual Sponsors
{% if github_sponsors %}
{% for group in github_sponsors.sponsors %}
{% for user in group %}
{% if user.login not in sponsors_badge.logins %}
{% endfor %}
{% endif %}
## About the data - technical details
The main intention of this page is to highlight the effort of the community to help others.
Especially including efforts that are normally less visible, and in many cases more arduous, like helping others with questions and reviewing Pull Requests with translations.
The data is calculated each month, you can read the [source code here](https://github.com/fastapi/fastapi/blob/master/scripts/).
Here I'm also highlighting contributions from sponsors.
I also reserve the right to update the algorithm, sections, thresholds, etc (just in case 🤷).
================================================
FILE: docs/en/docs/features.md
================================================
# Features { #features }
## FastAPI features { #fastapi-features }
**FastAPI** gives you the following:
### Based on open standards { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) for API creation, including declarations of path operations, parameters, request bodies, security, etc.
* Automatic data model documentation with [**JSON Schema**](https://json-schema.org/) (as OpenAPI itself is based on JSON Schema).
* Designed around these standards, after a meticulous study. Instead of an afterthought layer on top.
* This also allows using automatic **client code generation** in many languages.
### Automatic docs { #automatic-docs }
Interactive API documentation and exploration web user interfaces. As the framework is based on OpenAPI, there are multiple options, 2 included by default.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), with interactive exploration, call and test your API directly from the browser.

* Alternative API documentation with [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Just Modern Python { #just-modern-python }
It's all based on standard **Python type** declarations (thanks to Pydantic). No new syntax to learn. Just standard modern Python.
If you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI), check the short tutorial: [Python Types](python-types.md).
You write standard Python with types:
```Python
from datetime import date
from pydantic import BaseModel
# Declare a variable as a str
# and get editor support inside the function
def main(user_id: str):
return user_id
# A Pydantic model
class User(BaseModel):
id: int
name: str
joined: date
```
That can then be used like:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info
`**second_user_data` means:
Pass the keys and values of the `second_user_data` dict directly as key-value arguments, equivalent to: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Editor support { #editor-support }
All the framework was designed to be easy and intuitive to use, all the decisions were tested on multiple editors even before starting development, to ensure the best development experience.
In the Python developer surveys, it's clear [that one of the most used features is "autocompletion"](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
The whole **FastAPI** framework is based to satisfy that. Autocompletion works everywhere.
You will rarely need to come back to the docs.
Here's how your editor might help you:
* in [Visual Studio Code](https://code.visualstudio.com/):

* in [PyCharm](https://www.jetbrains.com/pycharm/):

You will get completion in code you might even consider impossible before. As for example, the `price` key inside a JSON body (that could have been nested) that comes from a request.
No more typing the wrong key names, coming back and forth between docs, or scrolling up and down to find if you finally used `username` or `user_name`.
### Short { #short }
It has sensible **defaults** for everything, with optional configurations everywhere. All the parameters can be fine-tuned to do what you need and to define the API you need.
But by default, it all **"just works"**.
### Validation { #validation }
* Validation for most (or all?) Python **data types**, including:
* JSON objects (`dict`).
* JSON array (`list`) defining item types.
* String (`str`) fields, defining min and max lengths.
* Numbers (`int`, `float`) with min and max values, etc.
* Validation for more exotic types, like:
* URL.
* Email.
* UUID.
* ...and others.
All the validation is handled by the well-established and robust **Pydantic**.
### Security and authentication { #security-and-authentication }
Security and authentication integrated. Without any compromise with databases or data models.
All the security schemes defined in OpenAPI, including:
* HTTP Basic.
* **OAuth2** (also with **JWT tokens**). Check the tutorial on [OAuth2 with JWT](tutorial/security/oauth2-jwt.md).
* API keys in:
* Headers.
* Query parameters.
* Cookies, etc.
Plus all the security features from Starlette (including **session cookies**).
All built as reusable tools and components that are easy to integrate with your systems, data stores, relational and NoSQL databases, etc.
### Dependency Injection { #dependency-injection }
FastAPI includes an extremely easy to use, but extremely powerful Dependency Injection system.
* Even dependencies can have dependencies, creating a hierarchy or **"graph" of dependencies**.
* All **automatically handled** by the framework.
* All the dependencies can require data from requests and **augment the path operation** constraints and automatic documentation.
* **Automatic validation** even for *path operation* parameters defined in dependencies.
* Support for complex user authentication systems, **database connections**, etc.
* **No compromise** with databases, frontends, etc. But easy integration with all of them.
### Unlimited "plug-ins" { #unlimited-plug-ins }
Or in other way, no need for them, import and use the code you need.
Any integration is designed to be so simple to use (with dependencies) that you can create a "plug-in" for your application in 2 lines of code using the same structure and syntax used for your *path operations*.
### Tested { #tested }
* 100% test coverage.
* 100% type annotated code base.
* Used in production applications.
## Starlette features { #starlette-features }
**FastAPI** is fully compatible with (and based on) [**Starlette**](https://www.starlette.dev/). So, any additional Starlette code you have, will also work.
`FastAPI` is actually a sub-class of `Starlette`. So, if you already know or use Starlette, most of the functionality will work the same way.
With **FastAPI** you get all of **Starlette**'s features (as FastAPI is just Starlette on steroids):
* Seriously impressive performance. It is [one of the fastest Python frameworks available, on par with **NodeJS** and **Go**](https://github.com/encode/starlette#performance).
* **WebSocket** support.
* In-process background tasks.
* Startup and shutdown events.
* Test client built on HTTPX.
* **CORS**, GZip, Static Files, Streaming responses.
* **Session and Cookie** support.
* 100% test coverage.
* 100% type annotated codebase.
## Pydantic features { #pydantic-features }
**FastAPI** is fully compatible with (and based on) [**Pydantic**](https://docs.pydantic.dev/). So, any additional Pydantic code you have, will also work.
Including external libraries also based on Pydantic, as ORMs, ODMs for databases.
This also means that in many cases you can pass the same object you get from a request **directly to the database**, as everything is validated automatically.
The same applies the other way around, in many cases you can just pass the object you get from the database **directly to the client**.
With **FastAPI** you get all of **Pydantic**'s features (as FastAPI is based on Pydantic for all the data handling):
* **No brainfuck**:
* No new schema definition micro-language to learn.
* If you know Python types you know how to use Pydantic.
* Plays nicely with your **IDE/linter/brain**:
* Because pydantic data structures are just instances of classes you define; auto-completion, linting, mypy and your intuition should all work properly with your validated data.
* Validate **complex structures**:
* Use of hierarchical Pydantic models, Python `typing`’s `List` and `Dict`, etc.
* And validators allow complex data schemas to be clearly and easily defined, checked and documented as JSON Schema.
* You can have deeply **nested JSON** objects and have them all validated and annotated.
* **Extensible**:
* Pydantic allows custom data types to be defined or you can extend validation with methods on a model decorated with the validator decorator.
* 100% test coverage.
================================================
FILE: docs/en/docs/help-fastapi.md
================================================
# Help FastAPI - Get Help { #help-fastapi-get-help }
Do you like **FastAPI**?
Would you like to help FastAPI, other users, and the author?
Or would you like to get help with **FastAPI**?
There are very simple ways to help (several involve just one or two clicks).
And there are several ways to get help too.
## Subscribe to the newsletter { #subscribe-to-the-newsletter }
You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](newsletter.md) to stay updated about:
* News about FastAPI and friends 🚀
* Guides 📝
* Features ✨
* Breaking changes 🚨
* Tips and tricks ✅
## Follow FastAPI on X (Twitter) { #follow-fastapi-on-x-twitter }
[Follow @fastapi on **X (Twitter)**](https://x.com/fastapi) to get the latest news about **FastAPI**. 🐦
## Star **FastAPI** in GitHub { #star-fastapi-in-github }
You can "star" FastAPI in GitHub (clicking the star button at the top right): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
By adding a star, other users will be able to find it more easily and see that it has been already useful for others.
## Watch the GitHub repository for releases { #watch-the-github-repository-for-releases }
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
There you can select "Releases only".
By doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **FastAPI** with bug fixes and new features.
## Connect with the author { #connect-with-the-author }
You can connect with [me (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), the author.
You can:
* [Follow me on **GitHub**](https://github.com/tiangolo).
* See other Open Source projects I have created that could help you.
* Follow me to see when I create a new Open Source project.
* [Follow me on **X (Twitter)**](https://x.com/tiangolo) or [Mastodon](https://fosstodon.org/@tiangolo).
* Tell me how you use FastAPI (I love to hear that).
* Hear when I make announcements or release new tools.
* You can also [follow @fastapi on X (Twitter)](https://x.com/fastapi) (a separate account).
* [Follow me on **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Hear when I make announcements or release new tools (although I use X (Twitter) more often 🤷♂).
* Read what I write (or follow me) on [**Dev.to**](https://dev.to/tiangolo) or [**Medium**](https://medium.com/@tiangolo).
* Read other ideas, articles, and read about tools I have created.
* Follow me to read when I publish something new.
## Tweet about **FastAPI** { #tweet-about-fastapi }
[Tweet about **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) and let me and others know why you like it. 🎉
I love to hear about how **FastAPI** is being used, what you have liked in it, in which project/company are you using it, etc.
## Vote for FastAPI { #vote-for-fastapi }
* [Vote for **FastAPI** in Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Vote for **FastAPI** in AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Say you use **FastAPI** on StackShare](https://stackshare.io/pypi-fastapi).
## Help others with questions in GitHub { #help-others-with-questions-in-github }
You can try and help others with their questions in:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
In many cases you might already know the answer for those questions. 🤓
If you are helping a lot of people with their questions, you will become an official [FastAPI Expert](fastapi-people.md#fastapi-experts). 🎉
Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗
The idea is for the **FastAPI** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.
---
Here's how to help others with questions (in discussions or issues):
### Understand the question { #understand-the-question }
* Check if you can understand what is the **purpose** and use case of the person asking.
* Then check if the question (the vast majority are questions) is **clear**.
* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.
* If you can't understand the question, ask for more **details**.
### Reproduce the problem { #reproduce-the-problem }
For most of the cases and most of the questions there's something related to the person's **original code**.
In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.
* You can ask them to provide a [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example), that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.
* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just keep in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.
### Suggest solutions { #suggest-solutions }
* After being able to understand the question, you can give them a possible **answer**.
* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.
### Ask to close { #ask-to-close }
If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸
* Now, if that solved their problem, you can ask them to:
* In GitHub Discussions: mark the comment as the **answer**.
* In GitHub Issues: **close** the issue.
## Watch the GitHub repository { #watch-the-github-repository }
You can "watch" FastAPI in GitHub (clicking the "watch" button at the top right): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc.
Then you can try and help them solve those questions.
## Ask Questions { #ask-questions }
You can [create a new question](https://github.com/fastapi/fastapi/discussions/new?category=questions) in the GitHub repository, for example to:
* Ask a **question** or ask about a **problem**.
* Suggest a new **feature**.
**Note**: if you do it, then I'm going to ask you to also help others. 😉
## Review Pull Requests { #review-pull-requests }
You can help me review pull requests from others.
Again, please try your best to be kind. 🤗
---
Here's what to keep in mind and how to review a pull request:
### Understand the problem { #understand-the-problem }
* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in a GitHub Discussion or issue.
* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.
### Don't worry about style { #dont-worry-about-style }
* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.
* Also don't worry about style rules, there are already automatized tools checking that.
And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.
### Check the code { #check-the-code }
* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.
* Then **comment** saying that you did that, that's how I will know you really checked it.
/// info
Unfortunately, I can't simply trust PRs that just have several approvals.
Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
///
* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
### Tests { #tests }
* Help me check that the PR has **tests**.
* Check that the tests **fail** before the PR. 🚨
* Then check that the tests **pass** after the PR. ✅
* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.
* Then also comment what you tried, that way I'll know that you checked it. 🤓
## Create a Pull Request { #create-a-pull-request }
You can [contribute](contributing.md) to the source code with Pull Requests, for example:
* To fix a typo you found on the documentation.
* To share an article, video, or podcast you created or found about FastAPI by [editing this file](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Make sure you add your link to the start of the corresponding section.
* To help [translate the documentation](contributing.md#translations) to your language.
* You can also help to review the translations created by others.
* To propose new documentation sections.
* To fix an existing issue/bug.
* Make sure to add tests.
* To add a new feature.
* Make sure to add tests.
* Make sure to add documentation if it's relevant.
## Help Maintain FastAPI { #help-maintain-fastapi }
Help me maintain **FastAPI**! 🤓
There's a lot of work to do, and for most of it, **YOU** can do it.
The main tasks that you can do right now are:
* [Help others with questions in GitHub](#help-others-with-questions-in-github) (see the section above).
* [Review Pull Requests](#review-pull-requests) (see the section above).
Those two tasks are what **consume time the most**. That's the main work of maintaining FastAPI.
If you can help me with that, **you are helping me maintain FastAPI** and making sure it keeps **advancing faster and better**. 🚀
## Join the chat { #join-the-chat }
Join the 👥 [Discord chat server](https://discord.gg/VQjSZaeJmf) 👥 and hang out with others in the FastAPI community.
/// tip
For questions, ask them in [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), there's a much better chance you will receive help by the [FastAPI Experts](fastapi-people.md#fastapi-experts).
Use the chat only for other general conversations.
///
### Don't use the chat for questions { #dont-use-the-chat-for-questions }
Keep in mind that as chats allow more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers.
In GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat systems. 😅
Conversations in the chat systems are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation. And only the ones in GitHub count to become a [FastAPI Expert](fastapi-people.md#fastapi-experts), so you will most probably receive more attention in GitHub.
On the other side, there are thousands of users in the chat systems, so there's a high chance you'll find someone to talk to there, almost all the time. 😄
## Sponsor the author { #sponsor-the-author }
If your **product/company** depends on or is related to **FastAPI** and you want to reach its users, you can sponsor the author (me) through [GitHub sponsors](https://github.com/sponsors/tiangolo). Depending on the tier, you could get some extra benefits, like a badge in the docs. 🎁
---
Thanks! 🚀
================================================
FILE: docs/en/docs/history-design-future.md
================================================
# History, Design and Future { #history-design-and-future }
Some time ago, [a **FastAPI** user asked](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> What’s the history of this project? It seems to have come from nowhere to awesome in a few weeks [...]
Here's a little bit of that history.
## Alternatives { #alternatives }
I have been creating APIs with complex requirements for several years (Machine Learning, distributed systems, asynchronous jobs, NoSQL databases, etc), leading several teams of developers.
As part of that, I needed to investigate, test and use many alternatives.
The history of **FastAPI** is in great part the history of its predecessors.
As said in the section [Alternatives](alternatives.md):
**FastAPI** wouldn't exist if not for the previous work of others.
There have been many tools created before that have helped inspire its creation.
I have been avoiding the creation of a new framework for several years. First I tried to solve all the features covered by **FastAPI** using many different frameworks, plug-ins, and tools.
But at some point, there was no other option than creating something that provided all these features, taking the best ideas from previous tools, and combining them in the best way possible, using language features that weren't even available before (Python 3.6+ type hints).
## Investigation { #investigation }
By using all the previous alternatives I had the chance to learn from all of them, take ideas, and combine them in the best way I could find for myself and the teams of developers I have worked with.
For example, it was clear that ideally it should be based on standard Python type hints.
Also, the best approach was to use already existing standards.
So, before even starting to code **FastAPI**, I spent several months studying the specs for OpenAPI, JSON Schema, OAuth2, etc. Understanding their relationship, overlap, and differences.
## Design { #design }
Then I spent some time designing the developer "API" I wanted to have as a user (as a developer using FastAPI).
I tested several ideas in the most popular Python editors: PyCharm, VS Code, Jedi based editors.
By the last [Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), that covers about 80% of the users.
It means that **FastAPI** was specifically tested with the editors used by 80% of the Python developers. And as most of the other editors tend to work similarly, all its benefits should work for virtually all editors.
That way I could find the best ways to reduce code duplication as much as possible, to have completion everywhere, type and error checks, etc.
All in a way that provided the best development experience for all the developers.
## Requirements { #requirements }
After testing several alternatives, I decided that I was going to use [**Pydantic**](https://docs.pydantic.dev/) for its advantages.
Then I contributed to it, to make it fully compliant with JSON Schema, to support different ways to define constraint declarations, and to improve editor support (type checks, autocompletion) based on the tests in several editors.
During the development, I also contributed to [**Starlette**](https://www.starlette.dev/), the other key requirement.
## Development { #development }
By the time I started creating **FastAPI** itself, most of the pieces were already in place, the design was defined, the requirements and tools were ready, and the knowledge about the standards and specifications was clear and fresh.
## Future { #future }
By this point, it's already clear that **FastAPI** with its ideas is being useful for many people.
It is being chosen over previous alternatives for suiting many use cases better.
Many developers and teams already depend on **FastAPI** for their projects (including me and my team).
But still, there are many improvements and features to come.
**FastAPI** has a great future ahead.
And [your help](help-fastapi.md) is greatly appreciated.
================================================
FILE: docs/en/docs/how-to/authentication-error-status-code.md
================================================
# Use Old 403 Authentication Error Status Codes { #use-old-403-authentication-error-status-codes }
Before FastAPI version `0.122.0`, when the integrated security utilities returned an error to the client after a failed authentication, they used the HTTP status code `403 Forbidden`.
Starting with FastAPI version `0.122.0`, they use the more appropriate HTTP status code `401 Unauthorized`, and return a sensible `WWW-Authenticate` header in the response, following the HTTP specifications, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
But if for some reason your clients depend on the old behavior, you can revert to it by overriding the method `make_not_authenticated_error` in your security classes.
For example, you can create a subclass of `HTTPBearer` that returns a `403 Forbidden` error instead of the default `401 Unauthorized` error:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip
Notice that the function returns the exception instance, it doesn't raise it. The raising is done in the rest of the internal code.
///
================================================
FILE: docs/en/docs/how-to/conditional-openapi.md
================================================
# Conditional OpenAPI { #conditional-openapi }
If you needed to, you could use settings and environment variables to configure OpenAPI conditionally depending on the environment, and even disable it entirely.
## About security, APIs, and docs { #about-security-apis-and-docs }
Hiding your documentation user interfaces in production *shouldn't* be the way to protect your API.
That doesn't add any extra security to your API, the *path operations* will still be available where they are.
If there's a security flaw in your code, it will still exist.
Hiding the documentation just makes it more difficult to understand how to interact with your API, and could make it more difficult for you to debug it in production. It could be considered simply a form of [Security through obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity).
If you want to secure your API, there are several better things you can do, for example:
* Make sure you have well defined Pydantic models for your request bodies and responses.
* Configure any required permissions and roles using dependencies.
* Never store plaintext passwords, only password hashes.
* Implement and use well-known cryptographic tools, like pwdlib and JWT tokens, etc.
* Add more granular permission controls with OAuth2 scopes where needed.
* ...etc.
Nevertheless, you might have a very specific use case where you really need to disable the API docs for some environment (e.g. for production) or depending on configurations from environment variables.
## Conditional OpenAPI from settings and env vars { #conditional-openapi-from-settings-and-env-vars }
You can easily use the same Pydantic settings to configure your generated OpenAPI and the docs UIs.
For example:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`.
And then we use it when creating the `FastAPI` app.
Then you could disable OpenAPI (including the UI docs) by setting the environment variable `OPENAPI_URL` to the empty string, like:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Then if you go to the URLs at `/openapi.json`, `/docs`, or `/redoc` you will just get a `404 Not Found` error like:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/en/docs/how-to/configure-swagger-ui.md
================================================
# Configure Swagger UI { #configure-swagger-ui }
You can configure some extra [Swagger UI parameters](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
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 { #disable-syntax-highlighting }
For example, you could disable syntax highlighting in Swagger UI.
Without changing the settings, syntax highlighting is enabled by default:
But you can disable it by setting `syntaxHighlight` to `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...and then Swagger UI won't show the syntax highlighting anymore:
## Change the Theme { #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):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
That configuration would change the syntax highlighting color theme:
## Change Default Swagger UI Parameters { #change-default-swagger-ui-parameters }
FastAPI includes some default configuration parameters appropriate for most of the use cases.
It includes these default configurations:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
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`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Other Swagger UI Parameters { #other-swagger-ui-parameters }
To see all the other possible configurations you can use, read the official [docs for Swagger UI parameters](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## JavaScript-only settings { #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.
================================================
FILE: docs/en/docs/how-to/custom-docs-ui-assets.md
================================================
# Custom Docs UI Static Assets (Self-Hosting) { #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 CDN.
But it's possible to customize it, you can set a specific CDN, or serve the files yourself.
## Custom CDN for JavaScript and CSS { #custom-cdn-for-javascript-and-css }
Let's say that you want to use a different CDN, 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 { #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:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Include the custom docs { #include-the-custom-docs }
Now you can create the *path operations* for the custom docs.
You can reuse 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...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// 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 { #create-a-path-operation-to-test-it }
Now, to be able to test that everything works, create a *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Test it { #test-it }
Now, you should be able to go to your docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), and reload the page, it will load those assets from the new CDN.
## Self-hosting JavaScript and CSS for docs { #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 { #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-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:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
And **ReDoc** uses the file:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
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 { #serve-the-static-files }
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Test the static files { #test-the-static-files }
Start your application and go to [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
You should see a very long JavaScript file for **ReDoc**.
It could start with something like:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
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 { #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:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Include the custom docs for static files { #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 reuse 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...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// 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 { #create-a-path-operation-to-test-static-files }
Now, to be able to test that everything works, create a *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Test Static Files UI { #test-static-files-ui }
Now, you should be able to disconnect your WiFi, go to your docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), and reload the page.
And even without Internet, you would be able to see the docs for your API and interact with it.
================================================
FILE: docs/en/docs/how-to/custom-request-and-route.md
================================================
# Custom Request and APIRoute class { #custom-request-and-apiroute-class }
In some cases, you may want to override the logic used by the `Request` and `APIRoute` classes.
In particular, this may be a good alternative to logic in a middleware.
For example, if you want to read or manipulate the request body before it is processed by your application.
/// danger
This is an "advanced" feature.
If you are just starting with **FastAPI** you might want to skip this section.
///
## Use cases { #use-cases }
Some use cases include:
* Converting non-JSON request bodies to JSON (e.g. [`msgpack`](https://msgpack.org/index.html)).
* Decompressing gzip-compressed request bodies.
* Automatically logging all request bodies.
## Handling custom request body encodings { #handling-custom-request-body-encodings }
Let's see how to make use of a custom `Request` subclass to decompress gzip requests.
And an `APIRoute` subclass to use that custom request class.
### Create a custom `GzipRequest` class { #create-a-custom-gziprequest-class }
/// tip
This is a toy example to demonstrate how it works, if you need Gzip support, you can use the provided [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware).
///
First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header.
If there's no `gzip` in the header, it will not try to decompress the body.
That way, the same route class can handle gzip compressed or uncompressed requests.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Create a custom `GzipRoute` class { #create-a-custom-gziproute-class }
Next, we create a custom subclass of `fastapi.routing.APIRoute` that will make use of the `GzipRequest`.
This time, it will overwrite the method `APIRoute.get_route_handler()`.
This method returns a function. And that function is what will receive a request and return a response.
Here we use it to create a `GzipRequest` from the original request.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Technical Details
A `Request` has a `request.scope` attribute, that's just a Python `dict` containing the metadata related to the request.
A `Request` also has a `request.receive`, that's a function to "receive" the body of the request.
The `scope` `dict` and `receive` function are both part of the ASGI specification.
And those two things, `scope` and `receive`, are what is needed to create a new `Request` instance.
To learn more about the `Request` check [Starlette's docs about Requests](https://www.starlette.dev/requests/).
///
The only thing the function returned by `GzipRequest.get_route_handler` does differently is convert the `Request` to a `GzipRequest`.
Doing this, our `GzipRequest` will take care of decompressing the data (if necessary) before passing it to our *path operations*.
After that, all of the processing logic is the same.
But because of our changes in `GzipRequest.body`, the request body will be automatically decompressed when it is loaded by **FastAPI** when needed.
## Accessing the request body in an exception handler { #accessing-the-request-body-in-an-exception-handler }
/// tip
To solve this same problem, it's probably a lot easier to use the `body` in a custom handler for `RequestValidationError` ([Handling Errors](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
But this example is still valid and it shows how to interact with the internal components.
///
We can also use this same approach to access the request body in an exception handler.
All we need to do is handle the request inside a `try`/`except` block:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Custom `APIRoute` class in a router { #custom-apiroute-class-in-a-router }
You can also set the `route_class` parameter of an `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/en/docs/how-to/extending-openapi.md
================================================
# Extending OpenAPI { #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-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 { #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 [ReDoc's OpenAPI extension to include a custom logo](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### Normal **FastAPI** { #normal-fastapi }
First, write all your **FastAPI** application as normally:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Generate the OpenAPI schema { #generate-the-openapi-schema }
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modify the OpenAPI schema { #modify-the-openapi-schema }
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Cache the OpenAPI schema { #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.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Override the method { #override-the-method }
Now you can replace the `.openapi()` method with your new function.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Check it { #check-it }
Once you go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) you will see that you are using your custom logo (in this example, **FastAPI**'s logo):
================================================
FILE: docs/en/docs/how-to/general.md
================================================
# General - How To - Recipes { #general-how-to-recipes }
Here are several pointers to other places in the docs, for general or frequent questions.
## Filter Data - Security { #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).
## Optimize Response Performance - Response Model - Return Type { #optimize-response-performance-response-model-return-type }
To optimize performance when returning JSON data, use a return type or response model, that way Pydantic will handle the serialization to JSON on the Rust side, without going through Python. Read more in the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md).
## Documentation Tags - OpenAPI { #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).
## Documentation Summary and Description - OpenAPI { #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).
## Documentation Response description - OpenAPI { #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).
## Documentation Deprecate a *Path Operation* - OpenAPI { #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).
## Convert any Data to JSON-compatible { #convert-any-data-to-json-compatible }
To convert any data to JSON-compatible, read the docs for [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md).
## OpenAPI Metadata - Docs { #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).
## OpenAPI Custom URL { #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).
## OpenAPI Docs URLs { #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).
================================================
FILE: docs/en/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
As **FastAPI** is based on the **ASGI** standard, it's very easy to integrate any **GraphQL** library also compatible with ASGI.
You can combine normal FastAPI *path operations* with GraphQL on the same application.
/// tip
**GraphQL** solves some very specific use cases.
It has **advantages** and **disadvantages** when compared to common **web APIs**.
Make sure you evaluate if the **benefits** for your use case compensate the **drawbacks**. 🤓
///
## GraphQL Libraries { #graphql-libraries }
Here are some of the **GraphQL** libraries that have **ASGI** support. You could use them with **FastAPI**:
* [Strawberry](https://strawberry.rocks/) 🍓
* With [docs for FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* With [docs for FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* With [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) to provide ASGI integration
* [Graphene](https://graphene-python.org/)
* With [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL with Strawberry { #graphql-with-strawberry }
If you need or want to work with **GraphQL**, [**Strawberry**](https://strawberry.rocks/) is the **recommended** library as it has the design closest to **FastAPI's** design, it's all based on **type annotations**.
Depending on your use case, you might prefer to use a different library, but if you asked me, I would probably suggest you try **Strawberry**.
Here's a small preview of how you could integrate Strawberry with FastAPI:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
You can learn more about Strawberry in the [Strawberry documentation](https://strawberry.rocks/).
And also the docs about [Strawberry with FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Older `GraphQLApp` from Starlette { #older-graphqlapp-from-starlette }
Previous versions of Starlette included a `GraphQLApp` class to integrate with [Graphene](https://graphene-python.org/).
It was deprecated from Starlette, but if you have code that used it, you can easily **migrate** to [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), that covers the same use case and has an **almost identical interface**.
/// tip
If you need GraphQL, I still would recommend you check out [Strawberry](https://strawberry.rocks/), as it's based on type annotations instead of custom classes and types.
///
## Learn More { #learn-more }
You can learn more about **GraphQL** in the [official GraphQL documentation](https://graphql.org/).
You can also read more about each those libraries described above in their links.
================================================
FILE: docs/en/docs/how-to/index.md
================================================
# How To - Recipes { #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) chapter by chapter instead.
///
================================================
FILE: docs/en/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Migrate from Pydantic v1 to Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
If you have an old FastAPI app, you might be using Pydantic version 1.
FastAPI version 0.100.0 had support for either Pydantic v1 or v2. It would use whichever you had installed.
FastAPI version 0.119.0 introduced partial support for Pydantic v1 from inside of Pydantic v2 (as `pydantic.v1`), to facilitate the migration to v2.
FastAPI 0.126.0 dropped support for Pydantic v1, while still supporting `pydantic.v1` for a little while.
/// warning
The Pydantic team stopped support for Pydantic v1 for the latest versions of Python, starting with **Python 3.14**.
This includes `pydantic.v1`, which is no longer supported in Python 3.14 and above.
If you want to use the latest features of Python, you will need to make sure you use Pydantic v2.
///
If you have an old FastAPI app with Pydantic v1, here I'll show you how to migrate it to Pydantic v2, and the **features in FastAPI 0.119.0** to help you with a gradual migration.
## Official Guide { #official-guide }
Pydantic has an official [Migration Guide](https://docs.pydantic.dev/latest/migration/) from v1 to v2.
It also includes what has changed, how validations are now more correct and strict, possible caveats, etc.
You can read it to understand better what has changed.
## Tests { #tests }
Make sure you have [tests](../tutorial/testing.md) for your app and you run them on continuous integration (CI).
This way, you can do the upgrade and make sure everything is still working as expected.
## `bump-pydantic` { #bump-pydantic }
In many cases, when you use regular Pydantic models without customizations, you will be able to automate most of the process of migrating from Pydantic v1 to Pydantic v2.
You can use [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) from the same Pydantic team.
This tool will help you to automatically change most of the code that needs to be changed.
After this, you can run the tests and check if everything works. If it does, you are done. 😎
## Pydantic v1 in v2 { #pydantic-v1-in-v2 }
Pydantic v2 includes everything from Pydantic v1 as a submodule `pydantic.v1`. But this is no longer supported in versions above Python 3.13.
This means that you can install the latest version of Pydantic v2 and import and use the old Pydantic v1 components from this submodule, as if you had the old Pydantic v1 installed.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### FastAPI support for Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Since FastAPI 0.119.0, there's also partial support for Pydantic v1 from inside of Pydantic v2, to facilitate the migration to v2.
So, you could upgrade Pydantic to the latest version 2, and change the imports to use the `pydantic.v1` submodule, and in many cases it would just work.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning
Have in mind that as the Pydantic team no longer supports Pydantic v1 in recent versions of Python, starting from Python 3.14, using `pydantic.v1` is also not supported in Python 3.14 and above.
///
### Pydantic v1 and v2 on the same app { #pydantic-v1-and-v2-on-the-same-app }
It's **not supported** by Pydantic to have a model of Pydantic v2 with its own fields defined as Pydantic v1 models or vice versa.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...but, you can have separated models using Pydantic v1 and v2 in the same app.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
In some cases, it's even possible to have both Pydantic v1 and v2 models in the same **path operation** in your FastAPI app:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
In this example above, the input model is a Pydantic v1 model, and the output model (defined in `response_model=ItemV2`) is a Pydantic v2 model.
### Pydantic v1 parameters { #pydantic-v1-parameters }
If you need to use some of the FastAPI-specific tools for parameters like `Body`, `Query`, `Form`, etc. with Pydantic v1 models, you can import them from `fastapi.temp_pydantic_v1_params` while you finish the migration to Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migrate in steps { #migrate-in-steps }
/// tip
First try with `bump-pydantic`, if your tests pass and that works, then you're done in one command. ✨
///
If `bump-pydantic` doesn't work for your use case, you can use the support for both Pydantic v1 and v2 models in the same app to do the migration to Pydantic v2 gradually.
You could fist upgrade Pydantic to use the latest version 2, and change the imports to use `pydantic.v1` for all your models.
Then, you can start migrating your models from Pydantic v1 to v2 in groups, in gradual steps. 🚶
================================================
FILE: docs/en/docs/how-to/separate-openapi-schemas.md
================================================
# Separate OpenAPI Schemas for Input and Output or Not { #separate-openapi-schemas-for-input-and-output-or-not }
Since **Pydantic v2** was released, the generated OpenAPI is a bit more exact and **correct** than before. 😎
In fact, in some cases, it will even have **two JSON Schemas** in OpenAPI for the same Pydantic model, for input and output, depending on if they have **default values**.
Let's see how that works and how to change it if you need to do that.
## Pydantic Models for Input and Output { #pydantic-models-for-input-and-output }
Let's say you have a Pydantic model with default values, like this one:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Model for Input { #model-for-input }
If you use this model as an input like here:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
...then the `description` field will **not be required**. Because it has a default value of `None`.
### Input Model in Docs { #input-model-in-docs }
You can confirm that in the docs, the `description` field doesn't have a **red asterisk**, it's not marked as required:
### Model for Output { #model-for-output }
But if you use the same model as an output, like here:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
...then because `description` has a default value, if you **don't return anything** for that field, it will still have that **default value**.
### Model for Output Response Data { #model-for-output-response-data }
If you interact with the docs and check the response, even though the code didn't add anything in one of the `description` fields, the JSON response contains the default value (`null`):
This means that it will **always have a value**, it's just that sometimes the value could be `None` (or `null` in JSON).
That means that, clients using your API don't have to check if the value exists or not, they can **assume the field will always be there**, but just that in some cases it will have the default value of `None`.
The way to describe this in OpenAPI, is to mark that field as **required**, because it will always be there.
Because of that, the JSON Schema for a model can be different depending on if it's used for **input or output**:
* for **input** the `description` will **not be required**
* for **output** it will be **required** (and possibly `None`, or in JSON terms, `null`)
### Model for Output in Docs { #model-for-output-in-docs }
You can check the output model in the docs too, **both** `name` and `description` are marked as **required** with a **red asterisk**:
### Model for Input and Output in Docs { #model-for-input-and-output-in-docs }
And if you check all the available Schemas (JSON Schemas) in OpenAPI, you will see that there are two, one `Item-Input` and one `Item-Output`.
For `Item-Input`, `description` is **not required**, it doesn't have a red asterisk.
But for `Item-Output`, `description` is **required**, it has a red asterisk.
With this feature from **Pydantic v2**, your API documentation is more **precise**, and if you have autogenerated clients and SDKs, they will be more precise too, with a better **developer experience** and consistency. 🎉
## Do not Separate Schemas { #do-not-separate-schemas }
Now, there are some cases where you might want to have the **same schema for input and output**.
Probably the main use case for this is if you already have some autogenerated client code/SDKs and you don't want to update all the autogenerated client code/SDKs yet, you probably will want to do it at some point, but maybe not right now.
In that case, you can disable this feature in **FastAPI**, with the parameter `separate_input_output_schemas=False`.
/// info
Support for `separate_input_output_schemas` was added in FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Same Schema for Input and Output Models in Docs { #same-schema-for-input-and-output-models-in-docs }
And now there will be one single schema for input and output for the model, only `Item`, and it will have `description` as **not required**:
================================================
FILE: docs/en/docs/how-to/testing-database.md
================================================
# Testing a Database { #testing-a-database }
You can study about databases, SQL, and SQLModel in the [SQLModel docs](https://sqlmodel.tiangolo.com/). 🤓
There's a mini [tutorial on using SQLModel with FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
That tutorial includes a section about [testing SQL databases](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/en/docs/index.md
================================================
# FastAPI { #fastapi }
FastAPI framework, high performance, easy to learn, fast to code, ready for production
---
**Documentation**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
**Source Code**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
The key features are:
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. Completion everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (previously known as Swagger) and [JSON Schema](https://json-schema.org/).
* estimation based on tests conducted by an internal development team, building production applications.
## Sponsors { #sponsors }
### Keystone Sponsor { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Gold Sponsors { #gold-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
### Silver Sponsors { #silver-sponsors }
{% for sponsor in sponsors.silver -%}
{% endfor %}
"I'm using FastAPI a ton these days. I'm actually planning to use it for all of my team's ML services at Microsoft. Some of them are getting integrated into the core Windows product and some Office products."
"We adopted the FastAPI library to spawn a REST server that can be queried to obtain predictions." [for Ludwig]
— Piero Molino, Yaroslav Dudin, Sai Sumanth Miryala, Uber(ref)
"Netflix is pleased to announce the open-source release of our crisis management orchestration framework: Dispatch!" [built with FastAPI]
— Kevin Glisson, Marc Vilanova, Forest Monsen, Netflix(ref)
"If anyone is looking to build a production Python API, I would highly recommend FastAPI. It is beautifully designed, simple to use and highly scalable — it has become a key component in our API-first development strategy."
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber(ref)
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
## FastAPI Conf { #fastapi-conf }
[**FastAPI Conf '26**](https://fastapiconf.com) is happening on **October 28, 2026** in **Amsterdam, NL**. All about FastAPI, right from the source. 🎤
## FastAPI mini documentary { #fastapi-mini-documentary }
There's a [FastAPI mini documentary](https://www.youtube.com/watch?v=mpR8ngthqiE) released at the end of 2025, you can watch it online:
## **Typer**, the FastAPI of CLIs { #typer-the-fastapi-of-clis }
If you are building a CLI app to be used in the terminal instead of a web API, check out [**Typer**](https://typer.tiangolo.com/).
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements { #requirements }
FastAPI stands on the shoulders of giants:
* [Starlette](https://www.starlette.dev/) for the web parts.
* [Pydantic](https://docs.pydantic.dev/) for the data parts.
## Installation { #installation }
Create and activate a [virtual environment](https://fastapi.tiangolo.com/virtual-environments/) and then install FastAPI:
**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals.
## Example { #example }
### Create it { #create-it }
Create a file `main.py` 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Or use async def...
If your code uses `async` / `await`, use `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Note**:
If you don't know, check the _"In a hurry?"_ section about [`async` and `await` in the docs](https://fastapi.tiangolo.com/async/#in-a-hurry).
### Run it { #run-it }
Run the server with:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
About the command fastapi dev...
The command `fastapi dev` reads your `main.py` file automatically, detects the **FastAPI** app in it, and starts a server using [Uvicorn](https://www.uvicorn.dev).
By default, `fastapi dev` will start with auto-reload enabled for local development.
You can read more about it in the [FastAPI CLI docs](https://fastapi.tiangolo.com/fastapi-cli/).
### Check it { #check-it }
Open your browser at [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
You will see the JSON response as:
```JSON
{"item_id": 5, "q": "somequery"}
```
You already created an API that:
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
* Both _paths_ take `GET` operations (also known as HTTP _methods_).
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
### Interactive API docs { #interactive-api-docs }
Now go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see the automatic interactive API documentation (provided by [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Alternative API docs { #alternative-api-docs }
And now, go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
You will see the alternative automatic documentation (provided by [ReDoc](https://github.com/Rebilly/ReDoc)):

## Example upgrade { #example-upgrade }
Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
The `fastapi dev` server should reload automatically.
### Interactive API docs upgrade { #interactive-api-docs-upgrade }
Now go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* The interactive API documentation will be automatically updated, including the new body:

* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:

* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:

### Alternative API docs upgrade { #alternative-api-docs-upgrade }
And now, go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* The alternative documentation will also reflect the new query parameter and body:

### Recap { #recap }
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python**.
For example, for an `int`:
```Python
item_id: int
```
or for a more complex `Item` model:
```Python
item: Item
```
...and with that single declaration you get:
* Editor support, including:
* Completion.
* Type checks.
* Validation of data:
* Automatic and clear errors when the data is invalid.
* Validation even for deeply nested JSON objects.
* Conversion of input data: coming from the network to Python data and types. Reading from:
* JSON.
* Path parameters.
* Query parameters.
* Cookies.
* Headers.
* Forms.
* Files.
* Conversion of output data: converting from Python data and types to network data (as JSON):
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
* `datetime` objects.
* `UUID` objects.
* Database models.
* ...and many more.
* Automatic interactive API documentation, including 2 alternative user interfaces:
* Swagger UI.
* ReDoc.
---
Coming back to the previous code example, **FastAPI** will:
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
* If it is not, the client will see a useful, clear error.
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
* As the `q` parameter is declared with `= None`, it is optional.
* Without the `None` it would be required (as is the body in the case with `PUT`).
* For `PUT` requests to `/items/{item_id}`, read the body as JSON:
* Check that it has a required attribute `name` that should be a `str`.
* Check that it has a required attribute `price` that has to be a `float`.
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
* All this would also work for deeply nested JSON objects.
* Convert from and to JSON automatically.
* Document everything with OpenAPI, that can be used by:
* Interactive documentation systems.
* Automatic client code generation systems, for many languages.
* Provide 2 interactive documentation web interfaces directly.
---
We just scratched the surface, but you already get the idea of how it all works.
Try changing the line with:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...from:
```Python
... "item_name": item.name ...
```
...to:
```Python
... "item_price": item.price ...
```
...and see how your editor will auto-complete the attributes and know their types:

For a more complete example including more features, see the Tutorial - User Guide.
**Spoiler alert**: the tutorial - user guide includes:
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
* How to set **validation constraints** as `maximum_length` or `regex`.
* A very powerful and easy to use **Dependency Injection** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **GraphQL** integration with [Strawberry](https://strawberry.rocks) and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* extremely easy tests based on HTTPX and `pytest`
* **CORS**
* **Cookie Sessions**
* ...and more.
### Deploy your app (optional) { #deploy-your-app-optional }
You can optionally deploy your FastAPI app to [FastAPI Cloud](https://fastapicloud.com), go and join the waiting list if you haven't. 🚀
If you already have a **FastAPI Cloud** account (we invited you from the waiting list 😉), you can deploy your application with one command.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
That's it! Now you can access your app at that URL. ✨
#### About FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** is built by the same author and team behind **FastAPI**.
It streamlines the process of **building**, **deploying**, and **accessing** an API with minimal effort.
It brings the same **developer experience** of building apps with FastAPI to **deploying** them to the cloud. 🎉
FastAPI Cloud is the primary sponsor and funding provider for the *FastAPI and friends* open source projects. ✨
#### Deploy to other cloud providers { #deploy-to-other-cloud-providers }
FastAPI is open source and based on standards. You can deploy FastAPI apps to any cloud provider you choose.
Follow your cloud provider's guides to deploy FastAPI apps with them. 🤓
## Performance { #performance }
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as [one of the fastest Python frameworks available](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section [Benchmarks](https://fastapi.tiangolo.com/benchmarks/).
## Dependencies { #dependencies }
FastAPI depends on Pydantic and Starlette.
### `standard` Dependencies { #standard-dependencies }
When you install FastAPI with `pip install "fastapi[standard]"` it comes with the `standard` group of optional dependencies:
Used by Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - for email validation.
Used by Starlette:
* [`httpx`](https://www.python-httpx.org) - Required if you want to use the `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - Required if you want to use the default template configuration.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - Required if you want to support form "parsing", with `request.form()`.
Used by FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) - for the server that loads and serves your application. This includes `uvicorn[standard]`, which includes some dependencies (e.g. `uvloop`) needed for high performance serving.
* `fastapi-cli[standard]` - to provide the `fastapi` command.
* This includes `fastapi-cloud-cli`, which allows you to deploy your FastAPI application to [FastAPI Cloud](https://fastapicloud.com).
### Without `standard` Dependencies { #without-standard-dependencies }
If you don't want to include the `standard` optional dependencies, you can install with `pip install fastapi` instead of `pip install "fastapi[standard]"`.
### Without `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
If you want to install FastAPI with the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Additional Optional Dependencies { #additional-optional-dependencies }
There are some additional dependencies you might want to install.
Additional optional Pydantic dependencies:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - for settings management.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - for extra types to be used with Pydantic.
Additional optional FastAPI dependencies:
* [`orjson`](https://github.com/ijl/orjson) - Required if you want to use `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - Required if you want to use `UJSONResponse`.
## License { #license }
This project is licensed under the terms of the MIT license.
================================================
FILE: docs/en/docs/js/custom.js
================================================
function setupTermynal() {
document.querySelectorAll(".use-termynal").forEach(node => {
node.style.display = "block";
new Termynal(node, {
lineDelay: 500
});
});
const progressLiteralStart = "---> 100%";
const promptLiteralStart = "$ ";
const customPromptLiteralStart = "# ";
const termynalActivateClass = "termy";
let termynals = [];
function createTermynals() {
document
.querySelectorAll(`.${termynalActivateClass} .highlight code`)
.forEach(node => {
const text = node.textContent;
const lines = text.split("\n");
const useLines = [];
let buffer = [];
function saveBuffer() {
if (buffer.length) {
let isBlankSpace = true;
buffer.forEach(line => {
if (line) {
isBlankSpace = false;
}
});
dataValue = {};
if (isBlankSpace) {
dataValue["delay"] = 0;
}
if (buffer[buffer.length - 1] === "") {
// A last single won't have effect
// so put an additional one
buffer.push("");
}
const bufferValue = buffer.join(" ");
dataValue["value"] = bufferValue;
useLines.push(dataValue);
buffer = [];
}
}
for (let line of lines) {
if (line === progressLiteralStart) {
saveBuffer();
useLines.push({
type: "progress"
});
} else if (line.startsWith(promptLiteralStart)) {
saveBuffer();
const value = line.replace(promptLiteralStart, "").trimEnd();
useLines.push({
type: "input",
value: value
});
} else if (line.startsWith("// ")) {
saveBuffer();
const value = "💬 " + line.replace("// ", "").trimEnd();
useLines.push({
value: value,
class: "termynal-comment",
delay: 0
});
} else if (line.startsWith(customPromptLiteralStart)) {
saveBuffer();
const promptStart = line.indexOf(promptLiteralStart);
if (promptStart === -1) {
console.error("Custom prompt found but no end delimiter", line)
}
const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "")
let value = line.slice(promptStart + promptLiteralStart.length);
useLines.push({
type: "input",
value: value,
prompt: prompt
});
} else {
buffer.push(line);
}
}
saveBuffer();
const inputCommands = useLines
.filter(line => line.type === "input")
.map(line => line.value)
.join("\n");
node.textContent = inputCommands;
const div = document.createElement("div");
node.style.display = "none";
node.after(div);
const termynal = new Termynal(div, {
lineData: useLines,
noInit: true,
lineDelay: 500
});
termynals.push(termynal);
});
}
function loadVisibleTermynals() {
termynals = termynals.filter(termynal => {
if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) {
termynal.init();
return false;
}
return true;
});
}
window.addEventListener("scroll", loadVisibleTermynals);
createTermynals();
loadVisibleTermynals();
}
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
async function showRandomAnnouncement(groupId, timeInterval) {
const announceFastAPI = document.getElementById(groupId);
if (announceFastAPI) {
let children = [].slice.call(announceFastAPI.children);
children = shuffle(children)
let index = 0
const announceRandom = () => {
children.forEach((el, i) => { el.style.display = "none" });
children[index].style.display = "block"
index = (index + 1) % children.length
}
announceRandom()
setInterval(announceRandom, timeInterval
)
}
}
function handleSponsorImages() {
const announceRight = document.getElementById('announce-right');
if(!announceRight) return;
const sponsorImages = document.querySelectorAll('.sponsor-image');
const imagePromises = Array.from(sponsorImages).map(img => {
return new Promise((resolve, reject) => {
if (img.complete && img.naturalHeight !== 0) {
resolve();
} else {
img.addEventListener('load', () => {
if (img.naturalHeight !== 0) {
resolve();
} else {
reject();
}
});
img.addEventListener('error', reject);
}
});
});
Promise.all(imagePromises)
.then(() => {
announceRight.style.display = 'block';
showRandomAnnouncement('announce-right', 10000);
})
.catch(() => {
// do nothing
});
}
function openLinksInNewTab() {
const siteUrl = document.querySelector("link[rel='canonical']")?.href
|| window.location.origin;
const siteOrigin = new URL(siteUrl).origin;
document.querySelectorAll(".md-content a[href]").forEach(a => {
if (a.getAttribute("target") === "_self") return;
const href = a.getAttribute("href");
if (!href) return;
try {
const url = new URL(href, window.location.href);
// Skip same-page anchor links (only the hash differs)
if (url.origin === window.location.origin
&& url.pathname === window.location.pathname
&& url.search === window.location.search) return;
if (!a.hasAttribute("target")) {
a.setAttribute("target", "_blank");
a.setAttribute("rel", "noopener");
}
if (url.origin !== siteOrigin) {
a.dataset.externalLink = "";
} else {
a.dataset.internalLink = "";
}
} catch (_) {}
});
}
function setupOpinionsTabs() {
const root = document.querySelector('.fastapi-opinions');
if (!root) return;
const tabs = Array.from(root.querySelectorAll('[role="tab"]'));
const panels = Array.from(root.querySelectorAll('[role="tabpanel"]'));
if (!tabs.length) return;
function activate(tab, focus) {
tabs.forEach(t => {
const selected = t === tab;
t.setAttribute('aria-selected', selected ? 'true' : 'false');
t.setAttribute('tabindex', selected ? '0' : '-1');
});
const targetId = tab.getAttribute('aria-controls');
panels.forEach(p => {
if (p.id === targetId) p.removeAttribute('hidden');
else p.setAttribute('hidden', '');
});
if (focus) tab.focus();
}
tabs.forEach((tab, i) => {
tab.addEventListener('click', () => activate(tab, false));
tab.addEventListener('keydown', (e) => {
let next = null;
if (e.key === 'ArrowRight') next = tabs[(i + 1) % tabs.length];
else if (e.key === 'ArrowLeft') next = tabs[(i - 1 + tabs.length) % tabs.length];
else if (e.key === 'Home') next = tabs[0];
else if (e.key === 'End') next = tabs[tabs.length - 1];
if (next) {
e.preventDefault();
activate(next, true);
}
});
});
}
async function main() {
setupTermynal();
showRandomAnnouncement('announce-left', 5000)
handleSponsorImages();
openLinksInNewTab();
setupOpinionsTabs();
}
document$.subscribe(() => {
main()
})
================================================
FILE: docs/en/docs/js/init_kapa_widget.js
================================================
document.addEventListener("DOMContentLoaded", function () {
var script = document.createElement("script");
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js";
script.setAttribute("data-website-id", "91f47f27-b405-4299-bf5f-a1c0ec07b3cc");
script.setAttribute("data-project-name", "FastAPI");
script.setAttribute("data-project-color", "#009485");
script.setAttribute("data-project-logo", "https://fastapi.tiangolo.com/img/favicon.png");
script.setAttribute("data-bot-protection-mechanism", "hcaptcha");
script.setAttribute("data-button-height", "3rem");
script.setAttribute("data-button-width", "3rem");
script.setAttribute("data-button-border-radius", "50%");
script.setAttribute("data-button-padding", "0");
script.setAttribute("data-button-image", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E");
script.setAttribute("data-button-image-height", "20px");
script.setAttribute("data-button-image-width", "20px");
script.setAttribute("data-button-text", "Ask AI");
script.setAttribute("data-button-text-font-size", "0.5rem");
script.setAttribute("data-button-text-font-family", "Roboto, sans-serif");
script.setAttribute("data-button-text-color", "#FFFFFF");
script.setAttribute("data-modal-border-radius", "0.5rem");
script.setAttribute("data-modal-header-bg-color", "#009485");
script.setAttribute("data-modal-title", "FastAPI AI Assistant");
script.setAttribute("data-modal-title-color", "#FFFFFF");
script.setAttribute("data-modal-title-font-family", "Roboto, sans-serif");
script.setAttribute("data-modal-example-questions", "How to define a route?,How to validate models?,How to handle responses?,How to deploy FastAPI?");
script.setAttribute("data-modal-disclaimer", "AI-generated answers based on FastAPI [documentation](https://fastapi.tiangolo.com/) and [community discussions](https://github.com/fastapi/fastapi/discussions). Always verify important information.");
script.async = true;
document.head.appendChild(script);
});
================================================
FILE: docs/en/docs/js/termynal.js
================================================
/**
* termynal.js
* A lightweight, modern and extensible animated terminal window, using
* async/await.
*
* @author Ines Montani
* @version 0.0.1
* @license MIT
*/
'use strict';
/** Generate a terminal widget. */
class Termynal {
/**
* Construct the widget's settings.
* @param {(string|Node)=} container - Query selector or container element.
* @param {Object=} options - Custom settings.
* @param {string} options.prefix - Prefix to use for data attributes.
* @param {number} options.startDelay - Delay before animation, in ms.
* @param {number} options.typeDelay - Delay between each typed character, in ms.
* @param {number} options.lineDelay - Delay between each line, in ms.
* @param {number} options.progressLength - Number of characters displayed as progress bar.
* @param {string} options.progressChar – Character to use for progress bar, defaults to █.
* @param {number} options.progressPercent - Max percent of progress.
* @param {string} options.cursor – Character to use for cursor, defaults to ▋.
* @param {Object[]} lineData - Dynamically loaded line data objects.
* @param {boolean} options.noInit - Don't initialise the animation.
*/
constructor(container = '#termynal', options = {}) {
this.container = (typeof container === 'string') ? document.querySelector(container) : container;
this.pfx = `data-${options.prefix || 'ty'}`;
this.originalStartDelay = this.startDelay = options.startDelay
|| parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600;
this.originalTypeDelay = this.typeDelay = options.typeDelay
|| parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90;
this.originalLineDelay = this.lineDelay = options.lineDelay
|| parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500;
this.progressLength = options.progressLength
|| parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40;
this.progressChar = options.progressChar
|| this.container.getAttribute(`${this.pfx}-progressChar`) || '█';
this.progressPercent = options.progressPercent
|| parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100;
this.cursor = options.cursor
|| this.container.getAttribute(`${this.pfx}-cursor`) || '▋';
this.lineData = this.lineDataToElements(options.lineData || []);
this.loadLines()
if (!options.noInit) this.init()
}
loadLines() {
// Load all the lines and create the container so that the size is fixed
// Otherwise it would be changing and the user viewport would be constantly
// moving as she/he scrolls
const finish = this.generateFinish()
finish.style.visibility = 'hidden'
this.container.appendChild(finish)
// Appends dynamically loaded lines to existing line elements.
this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData);
for (let line of this.lines) {
line.style.visibility = 'hidden'
this.container.appendChild(line)
}
const restart = this.generateRestart()
restart.style.visibility = 'hidden'
this.container.appendChild(restart)
this.container.setAttribute('data-termynal', '');
}
/**
* Initialise the widget, get lines, clear container and start animation.
*/
init() {
/**
* Calculates width and height of Termynal container.
* If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.
*/
const containerStyle = getComputedStyle(this.container);
this.container.style.width = containerStyle.width !== '0px' ?
containerStyle.width : undefined;
this.container.style.minHeight = containerStyle.height !== '0px' ?
containerStyle.height : undefined;
this.container.setAttribute('data-termynal', '');
this.container.innerHTML = '';
for (let line of this.lines) {
line.style.visibility = 'visible'
}
this.start();
}
/**
* Start the animation and rener the lines depending on their data attributes.
*/
async start() {
this.addFinish()
await this._wait(this.startDelay);
for (let line of this.lines) {
const type = line.getAttribute(this.pfx);
const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay;
if (type == 'input') {
line.setAttribute(`${this.pfx}-cursor`, this.cursor);
await this.type(line);
await this._wait(delay);
}
else if (type == 'progress') {
await this.progress(line);
await this._wait(delay);
}
else {
this.container.appendChild(line);
await this._wait(delay);
}
line.removeAttribute(`${this.pfx}-cursor`);
}
this.addRestart()
this.finishElement.style.visibility = 'hidden'
this.lineDelay = this.originalLineDelay
this.typeDelay = this.originalTypeDelay
this.startDelay = this.originalStartDelay
}
generateRestart() {
const restart = document.createElement('a')
restart.onclick = (e) => {
e.preventDefault()
this.container.innerHTML = ''
this.init()
}
restart.href = "javascript:void(0)"
restart.setAttribute('data-terminal-control', '')
restart.innerHTML = "restart ↻"
return restart
}
generateFinish() {
const finish = document.createElement('a')
finish.onclick = (e) => {
e.preventDefault()
this.lineDelay = 0
this.typeDelay = 0
this.startDelay = 0
}
finish.href = "javascript:void(0)"
finish.setAttribute('data-terminal-control', '')
finish.innerHTML = "fast →"
this.finishElement = finish
return finish
}
addRestart() {
const restart = this.generateRestart()
this.container.appendChild(restart)
}
addFinish() {
const finish = this.generateFinish()
this.container.appendChild(finish)
}
/**
* Animate a typed line.
* @param {Node} line - The line element to render.
*/
async type(line) {
const chars = [...line.textContent];
line.textContent = '';
this.container.appendChild(line);
for (let char of chars) {
const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay;
await this._wait(delay);
line.textContent += char;
}
}
/**
* Animate a progress bar.
* @param {Node} line - The line element to render.
*/
async progress(line) {
const progressLength = line.getAttribute(`${this.pfx}-progressLength`)
|| this.progressLength;
const progressChar = line.getAttribute(`${this.pfx}-progressChar`)
|| this.progressChar;
const chars = progressChar.repeat(progressLength);
const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`)
|| this.progressPercent;
line.textContent = '';
this.container.appendChild(line);
for (let i = 1; i < chars.length + 1; i++) {
await this._wait(this.typeDelay);
const percent = Math.round(i / chars.length * 100);
line.textContent = `${chars.slice(0, i)} ${percent}%`;
if (percent>progressPercent) {
break;
}
}
}
/**
* Helper function for animation delays, called with `await`.
* @param {number} time - Timeout, in ms.
*/
_wait(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
/**
* Converts line data objects into line elements.
*
* @param {Object[]} lineData - Dynamically loaded lines.
* @param {Object} line - Line data object.
* @returns {Element[]} - Array of line elements.
*/
lineDataToElements(lineData) {
return lineData.map(line => {
let div = document.createElement('div');
div.innerHTML = `${line.value || ''}`;
return div.firstElementChild;
});
}
/**
* Helper function for generating attributes string.
*
* @param {Object} line - Line data object.
* @returns {string} - String of attributes.
*/
_attributes(line) {
let attrs = '';
for (let prop in line) {
// Custom add class
if (prop === 'class') {
attrs += ` class=${line[prop]} `
continue
}
if (prop === 'type') {
attrs += `${this.pfx}="${line[prop]}" `
} else if (prop !== 'value') {
attrs += `${this.pfx}-${prop}="${line[prop]}" `
}
}
return attrs;
}
}
/**
* HTML API: If current script has container(s) specified, initialise Termynal.
*/
if (document.currentScript.hasAttribute('data-termynal-container')) {
const containers = document.currentScript.getAttribute('data-termynal-container');
containers.split('|')
.forEach(container => new Termynal(container))
}
================================================
FILE: docs/en/docs/learn/index.md
================================================
# Learn { #learn }
Here are the introductory sections and the tutorials to learn **FastAPI**.
You could consider this a **book**, a **course**, the **official** and recommended way to learn FastAPI. 😎
================================================
FILE: docs/en/docs/management-tasks.md
================================================
# Repository Management Tasks
These are the tasks that can be performed to manage the FastAPI repository by [team members](./fastapi-people.md#team).
/// tip
This section is useful only to a handful of people, team members with permissions to manage the repository. You can probably skip it. 😉
///
...so, you are a [team member of FastAPI](./fastapi-people.md#team)? Wow, you are so cool! 😎
You can help with everything on [Help FastAPI - Get Help](./help-fastapi.md) the same ways as external contributors. But additionally, there are some tasks that only you (as part of the team) can perform.
Here are the general instructions for the tasks you can perform.
Thanks a lot for your help. 🙇
## Be Nice
First of all, be nice. 😊
You probably are super nice if you were added to the team, but it's worth mentioning it. 🤓
### When Things are Difficult
When things are great, everything is easier, so that doesn't need much instructions. But when things are difficult, here are some guidelines.
Try to find the good side. In general, if people are not being unfriendly, try to thank their effort and interest, even if you disagree with the main subject (discussion, PR), just thank them for being interested in the project, or for having dedicated some time to try to do something.
It's difficult to convey emotion in text, use emojis to help. 😅
In discussions and PRs, in many cases, people bring their frustration and show it without filter, in many cases exaggerating, complaining, being entitled, etc. That's really not nice, and when it happens, it lowers our priority to solve their problems. But still, try to breath, and be gentle with your answers.
Try to avoid using bitter sarcasm or potentially passive-aggressive comments. If something is wrong, it's better to be direct (try to be gentle) than sarcastic.
Try to be as specific and objective as possible, avoid generalizations.
For conversations that are more difficult, for example to reject a PR, you can ask me (@tiangolo) to handle it directly.
## Edit PR Titles
* Edit the PR title to start with an emoji from [gitmoji](https://gitmoji.dev/).
* Use the emoji character, not the GitHub code. So, use `🐛` instead of `:bug:`. This is so that it shows up correctly outside of GitHub, for example in the release notes.
* For translations use the `🌐` emoji ("globe with meridians").
* Start the title with a verb. For example `Add`, `Refactor`, `Fix`, etc. This way the title will say the action that the PR does. Like `Add support for teleporting`, instead of `Teleporting wasn't working, so this PR fixes it`.
* Edit the text of the PR title to start in "imperative", like giving an order. So, instead of `Adding support for teleporting` use `Add support for teleporting`.
* Try to make the title descriptive about what it achieves. If it's a feature, try to describe it, for example `Add support for teleporting` instead of `Create TeleportAdapter class`.
* Do not finish the title with a period (`.`).
* When the PR is for a translation, start with the `🌐` and then `Add {language} translation for` and then the translated file path. For example:
```Markdown
🌐 Add Spanish translation for `docs/es/docs/teleporting.md`
```
Once the PR is merged, a GitHub Action ([latest-changes](https://github.com/tiangolo/latest-changes)) will use the PR title to update the latest changes automatically.
So, having a nice PR title will not only look nice in GitHub, but also in the release notes. 📝
## Add Labels to PRs
The same GitHub Action [latest-changes](https://github.com/tiangolo/latest-changes) uses one label in the PR to decide the section in the release notes to put this PR in.
Make sure you use a supported label from the [latest-changes list of labels](https://github.com/tiangolo/latest-changes#using-labels):
* `breaking`: Breaking Changes
* Existing code will break if they update the version without changing their code. This rarely happens, so this label is not frequently used.
* `security`: Security Fixes
* This is for security fixes, like vulnerabilities. It would almost never be used.
* `feature`: Features
* New features, adding support for things that didn't exist before.
* `bug`: Fixes
* Something that was supported didn't work, and this fixes it. There are many PRs that claim to be bug fixes because the user is doing something in an unexpected way that is not supported, but they considered it what should be supported by default. Many of these are actually features or refactors. But in some cases there's an actual bug.
* `refactor`: Refactors
* This is normally for changes to the internal code that don't change the behavior. Normally it improves maintainability, or enables future features, etc.
* `upgrade`: Upgrades
* This is for upgrades to direct dependencies from the project, or extra optional dependencies, normally in `pyproject.toml`. So, things that would affect final users, they would end up receiving the upgrade in their code base once they update. But this is not for upgrades to internal dependencies used for development, testing, docs, etc. Those internal dependencies or GitHub Action versions should be marked as `internal`, not `upgrade`.
* `docs`: Docs
* Changes in docs. This includes updating the docs, fixing typos. But it doesn't include changes to translations.
* You can normally quickly detect it by going to the "Files changed" tab in the PR and checking if the updated file(s) starts with `docs/en/docs`. The original version of the docs is always in English, so in `docs/en/docs`.
* `lang-all`: Translations
* Use this for translations. You can normally quickly detect it by going to the "Files changed" tab in the PR and checking if the updated file(s) starts with `docs/{some lang}/docs` but not `docs/en/docs`. For example, `docs/es/docs`.
* `internal`: Internal
* Use this for changes that only affect how the repo is managed. For example upgrades to internal dependencies, changes in GitHub Actions or scripts, etc.
/// tip
Some tools like Dependabot, will add some labels, like `dependencies`, but have in mind that this label is not used by the `latest-changes` GitHub Action, so it won't be used in the release notes. Please make sure one of the labels above is added.
///
## Add Labels to Translation PRs
When there's a PR for a translation, apart from adding the `lang-all` label, also add a label for the language.
There will be a label for each language using the language code, like `lang-{lang code}`, for example, `lang-es` for Spanish, `lang-fr` for French, etc.
* Add the specific language label.
* Add the label `awaiting-review`.
The label `awaiting-review` is special, only used for translations. A GitHub Action will detect it, then it will read the language label, and it will update the GitHub Discussions managing the translations for that language to notify people that there's a new translation to review.
Once a native speaker comes, reviews the PR, and approves it, the GitHub Action will come and remove the `awaiting-review` label, and add the `approved-1` label.
This way, we can notice when there are new translations ready, because they have the `approved-1` label.
## Merge Translation PRs
Translations are generated automatically with LLMs and scripts.
There's one GitHub Action that can be manually run to add or update translations for a language: [`translate.yml`](https://github.com/fastapi/fastapi/actions/workflows/translate.yml).
For these language translation PRs, confirm that:
* The PR was automated (authored by @tiangolo), not made by another user.
* It has the labels `lang-all` and `lang-{lang code}`.
For PRs that update language-specific LLM prompts, confirm that:
* The PR has the labels `lang-all` and `lang-{lang code}`.
* It is approved by at least one native speaker.
* In some cases you might need to translate several pages with new prompt to make sure it works as expected.
If the PR meets the above conditions, you can merge it. 😎
## Review PRs
* If a PR doesn't explain what it does or why, if it seems like it could be useful, ask for more information. Otherwise, feel free to close it.
* If a PR seems to be spam, meaningless, only to change statistics (to appear as "contributor") or similar, you can simply mark it as `invalid`, and it will be automatically closed.
* If a PR seems to be AI generated, and seems like reviewing it would take more time from you than the time it took to write the prompt, mark it as `maybe-ai`, and it will be automatically closed.
* A PR should have a specific use case that it is solving.
* If the PR is for a feature, it should have docs.
* Unless it's a feature we want to discourage, like support for a corner case that we don't want users to use.
* The docs should include a source example file, not write Python directly in Markdown.
* If the source example(s) file can have different syntax for different Python versions, there should be different versions of the file, and they should be shown in tabs in the docs.
* There should be tests testing the source example.
* Before the PR is applied, the new tests should fail.
* After applying the PR, the new tests should pass.
* Coverage should stay at 100%.
* If you see the PR makes sense, or we discussed it and considered it should be accepted, you can add commits on top of the PR to tweak it, to add docs, tests, format, refactor, remove extra files, etc.
* Feel free to comment in the PR to ask for more information, to suggest changes, etc.
* Once you think the PR is ready, move it in the internal GitHub project for me to review it.
## FastAPI People PRs
Every month, a GitHub Action updates the FastAPI People data. Those PRs look like this one: [👥 Update FastAPI People](https://github.com/fastapi/fastapi/pull/11669).
If the tests are passing, you can merge it right away.
## Dependabot PRs
Dependabot will create PRs to update dependencies for several things, and those PRs all look similar, but some are way more delicate than others.
* If the PR is for a direct dependency, so, Dependabot is modifying `pyproject.toml` in the main dependencies, **don't merge it**. 😱 Let me check it first. There's a good chance that some additional tweaks or updates are needed.
* If the PR updates one of the internal dependencies, for example the group `dev` in `pyproject.toml`, or GitHub Action versions, if the tests are passing, the release notes (shown in a summary in the PR) don't show any obvious potential breaking change, you can merge it. 😎
## Mark GitHub Discussions Answers
When a question in GitHub Discussions has been answered, mark the answer by clicking "Mark as answer".
You can filter discussions by [`Questions` that are `Unanswered`](https://github.com/tiangolo/fastapi/discussions/categories/questions?discussions_q=category:Questions+is:open+is:unanswered).
================================================
FILE: docs/en/docs/management.md
================================================
# Repository Management
Here's a short description of how the FastAPI repository is managed and maintained.
## Owner
I, [@tiangolo](https://github.com/tiangolo), am the creator and owner of the FastAPI repository. 🤓
I normally give the final review to each PR before merging them. I make the final decisions on the project, I'm the [BDFL](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life). 😅
## Team
There's a team of people that help manage and maintain the project. 😎
They have different levels of permissions and [specific instructions](./management-tasks.md).
Some of the tasks they can perform include:
* Adding labels to PRs.
* Editing PR titles.
* Adding commits on top of PRs to tweak them.
* Mark answers in GitHub Discussions questions, etc.
* Merge some specific types of PRs.
You can see the current team members in [FastAPI People - Team](./fastapi-people.md#team).
Joining the team is by invitation only, and I could update or remove permissions, instructions, or membership.
## FastAPI Experts
The people that help others the most in GitHub Discussions can become [**FastAPI Experts**](./fastapi-people.md#fastapi-experts).
This is normally the best way to contribute to the project.
## External Contributions
External contributions are very welcome and appreciated, including answering questions, submitting PRs, etc. 🙇♂️
There are many ways to [help maintain FastAPI](./help-fastapi.md#help-maintain-fastapi).
================================================
FILE: docs/en/docs/newsletter.md
================================================
# FastAPI and friends newsletter
================================================
FILE: docs/en/docs/project-generation.md
================================================
# Full Stack FastAPI Template { #full-stack-fastapi-template }
Templates, while typically come with a specific setup, are designed to be flexible and customizable. This allows you to modify and adapt them to your project's requirements, making them an excellent starting point. 🏁
You can use this template to get started, as it includes a lot of the initial set up, security, database and some API endpoints already done for you.
GitHub Repository: [Full Stack FastAPI Template](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI Template - Technology Stack and Features { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com) for the Python backend API.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) for the Python SQL database interactions (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), used by FastAPI, for the data validation and settings management.
- 💾 [PostgreSQL](https://www.postgresql.org) as the SQL database.
- 🚀 [React](https://react.dev) for the frontend.
- 💃 Using TypeScript, hooks, Vite, and other parts of a modern frontend stack.
- 🎨 [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.com) for the frontend components.
- 🤖 An automatically generated frontend client.
- 🧪 [Playwright](https://playwright.dev) for End-to-End testing.
- 🦇 Dark mode support.
- 🐋 [Docker Compose](https://www.docker.com) for development and production.
- 🔒 Secure password hashing by default.
- 🔑 JWT (JSON Web Token) authentication.
- 📫 Email based password recovery.
- ✅ Tests with [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) as a reverse proxy / load balancer.
- 🚢 Deployment instructions using Docker Compose, including how to set up a frontend Traefik proxy to handle automatic HTTPS certificates.
- 🏭 CI (continuous integration) and CD (continuous deployment) based on GitHub Actions.
================================================
FILE: docs/en/docs/python-types.md
================================================
# Python Types Intro { #python-types-intro }
Python has support for optional "type hints" (also called "type annotations").
These **"type hints"** or annotations are a special syntax that allow declaring the type of a variable.
By declaring types for your variables, editors and tools can give you better support.
This is just a **quick tutorial / refresher** about Python type hints. It covers only the minimum necessary to use them with **FastAPI**... which is actually very little.
**FastAPI** is all based on these type hints, they give it many advantages and benefits.
But even if you never use **FastAPI**, you would benefit from learning a bit about them.
/// note
If you are a Python expert, and you already know everything about type hints, skip to the next chapter.
///
## Motivation { #motivation }
Let's start with a simple example:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Calling this program outputs:
```
John Doe
```
The function does the following:
* Takes a `first_name` and `last_name`.
* Converts the first letter of each one to upper case with `title()`.
* Concatenates them with a space in the middle.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Edit it { #edit-it }
It's a very simple program.
But now imagine that you were writing it from scratch.
At some point you would have started the definition of the function, you had the parameters ready...
But then you have to call "that method that converts the first letter to upper case".
Was it `upper`? Was it `uppercase`? `first_uppercase`? `capitalize`?
Then, you try with the old programmer's friend, editor autocompletion.
You type the first parameter of the function, `first_name`, then a dot (`.`) and then hit `Ctrl+Space` to trigger the completion.
But, sadly, you get nothing useful:
### Add types { #add-types }
Let's modify a single line from the previous version.
We will change exactly this fragment, the parameters of the function, from:
```Python
first_name, last_name
```
to:
```Python
first_name: str, last_name: str
```
That's it.
Those are the "type hints":
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
That is not the same as declaring default values like would be with:
```Python
first_name="john", last_name="doe"
```
It's a different thing.
We are using colons (`:`), not equals (`=`).
And adding type hints normally doesn't change what happens from what would happen without them.
But now, imagine you are again in the middle of creating that function, but with type hints.
At the same point, you try to trigger the autocomplete with `Ctrl+Space` and you see:
With that, you can scroll, seeing the options, until you find the one that "rings a bell":
## More motivation { #more-motivation }
Check this function, it already has type hints:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Declaring types { #declaring-types }
You just saw the main place to declare type hints. As function parameters.
This is also the main place you would use them with **FastAPI**.
### Simple types { #simple-types }
You can declare all the standard Python types, not only `str`.
You can use, for example:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### `typing` module { #typing-module }
For some additional use cases, you might need to import some things from the standard library `typing` module, for example when you want to declare that something has "any type", you can use `Any` from `typing`:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic types { #generic-types }
Some types can take "type parameters" in square brackets, to define their internal types, for example a "list of strings" would be declared `list[str]`.
These types that can take type parameters are called **Generic types** or **Generics**.
You can use the same builtin types as generics (with square brackets and types inside):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
For example, let's define a variable to be a `list` of `str`.
Declare the variable, with the same colon (`:`) syntax.
As the type, put `list`.
As the list is a type that contains some internal types, you put them in square brackets:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info
Those internal types in the square brackets are called "type parameters".
In this case, `str` is the type parameter passed to `list`.
///
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
By doing that, your editor can provide support even while processing items from the list:
Without types, that's almost impossible to achieve.
Notice that the variable `item` is one of the elements in the list `items`.
And still, the editor knows it is a `str`, and provides support for that.
#### Tuple and Set { #tuple-and-set }
You would do the same to declare `tuple`s and `set`s:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
This means:
* The variable `items_t` is a `tuple` with 3 items, an `int`, another `int`, and a `str`.
* The variable `items_s` is a `set`, and each of its items is of type `bytes`.
#### Dict { #dict }
To define a `dict`, you pass 2 type parameters, separated by commas.
The first type parameter is for the keys of the `dict`.
The second type parameter is for the values of the `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
This means:
* The variable `prices` is a `dict`:
* The keys of this `dict` are of type `str` (let's say, the name of each item).
* The values of this `dict` are of type `float` (let's say, the price of each item).
#### Union { #union }
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
To define it you use the vertical bar (`|`) to separate both types.
This is called a "union", because the variable can be anything in the union of those two sets of types.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
This means that `item` could be an `int` or a `str`.
#### Possibly `None` { #possibly-none }
You can declare that a value could have a type, like `str`, but that it could also be `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Using `str | None` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
### Classes as types { #classes-as-types }
You can also declare a class as the type of a variable.
Let's say you have a class `Person`, with a name:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Then you can declare a variable to be of type `Person`:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
And then, again, you get all the editor support:
Notice that this means "`one_person` is an **instance** of the class `Person`".
It doesn't mean "`one_person` is the **class** called `Person`".
## Pydantic models { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) is a Python library to perform data validation.
You declare the "shape" of the data as classes with attributes.
And each attribute has a type.
Then you create an instance of that class with some values and it will validate the values, convert them to the appropriate type (if that's the case) and give you an object with all the data.
And you get all the editor support with that resulting object.
An example from the official Pydantic docs:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info
To learn more about [Pydantic, check its docs](https://docs.pydantic.dev/).
///
**FastAPI** is all based on Pydantic.
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md).
## Type Hints with Metadata Annotations { #type-hints-with-metadata-annotations }
Python also has a feature that allows putting **additional metadata** in these type hints using `Annotated`.
You can import `Annotated` from `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.
But you can use this space in `Annotated` to provide **FastAPI** with additional metadata about how you want your application to behave.
The important thing to remember is that **the first *type parameter*** you pass to `Annotated` is the **actual type**. The rest, is just metadata for other tools.
For now, you just need to know that `Annotated` exists, and that it's standard Python. 😎
Later you will see how **powerful** it can be.
/// tip
The fact that this is **standard Python** means that you will still get the **best possible developer experience** in your editor, with the tools you use to analyze and refactor your code, etc. ✨
And also that your code will be very compatible with many other Python tools and libraries. 🚀
///
## Type hints in **FastAPI** { #type-hints-in-fastapi }
**FastAPI** takes advantage of these type hints to do several things.
With **FastAPI** you declare parameters with type hints and you get:
* **Editor support**.
* **Type checks**.
...and **FastAPI** uses the same declarations to:
* **Define requirements**: from request path parameters, query parameters, headers, bodies, dependencies, etc.
* **Convert data**: from the request to the required type.
* **Validate data**: coming from each request:
* Generating **automatic errors** returned to the client when the data is invalid.
* **Document** the API using OpenAPI:
* which is then used by the automatic interactive documentation user interfaces.
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/index.md).
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you.
/// info
If you already went through all the tutorial and came back to see more about types, a good resource is [the "cheat sheet" from `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/en/docs/reference/apirouter.md
================================================
# `APIRouter` class
Here's the reference information for the `APIRouter` class, with all its parameters, attributes and methods.
You can import the `APIRouter` class directly from `fastapi`:
```python
from fastapi import APIRouter
```
::: fastapi.APIRouter
options:
members:
- websocket
- include_router
- get
- put
- post
- delete
- options
- head
- patch
- trace
- on_event
================================================
FILE: docs/en/docs/reference/background.md
================================================
# Background Tasks - `BackgroundTasks`
You can declare a parameter in a *path operation function* or dependency function with the type `BackgroundTasks`, and then you can use it to schedule the execution of background tasks after the response is sent.
You can import it directly from `fastapi`:
```python
from fastapi import BackgroundTasks
```
::: fastapi.BackgroundTasks
================================================
FILE: docs/en/docs/reference/dependencies.md
================================================
# Dependencies - `Depends()` and `Security()`
## `Depends()`
Dependencies are handled mainly with the special function `Depends()` that takes a callable.
Here is the reference for it and its parameters.
You can import it directly from `fastapi`:
```python
from fastapi import Depends
```
::: fastapi.Depends
## `Security()`
For many scenarios, you can handle security (authorization, authentication, etc.) with dependencies, using `Depends()`.
But when you want to also declare OAuth2 scopes, you can use `Security()` instead of `Depends()`.
You can import `Security()` directly from `fastapi`:
```python
from fastapi import Security
```
::: fastapi.Security
================================================
FILE: docs/en/docs/reference/encoders.md
================================================
# Encoders - `jsonable_encoder`
::: fastapi.encoders.jsonable_encoder
================================================
FILE: docs/en/docs/reference/exceptions.md
================================================
# Exceptions - `HTTPException` and `WebSocketException`
These are the exceptions that you can raise to show errors to the client.
When you raise an exception, as would happen with normal Python, the rest of the execution is aborted. This way you can raise these exceptions from anywhere in the code to abort a request and show the error to the client.
You can use:
* `HTTPException`
* `WebSocketException`
These exceptions can be imported directly from `fastapi`:
```python
from fastapi import HTTPException, WebSocketException
```
::: fastapi.HTTPException
::: fastapi.WebSocketException
================================================
FILE: docs/en/docs/reference/fastapi.md
================================================
# `FastAPI` class
Here's the reference information for the `FastAPI` class, with all its parameters, attributes and methods.
You can import the `FastAPI` class directly from `fastapi`:
```python
from fastapi import FastAPI
```
::: fastapi.FastAPI
options:
members:
- openapi_version
- webhooks
- state
- dependency_overrides
- openapi
- websocket
- include_router
- get
- put
- post
- delete
- options
- head
- patch
- trace
- on_event
- middleware
- exception_handler
================================================
FILE: docs/en/docs/reference/httpconnection.md
================================================
# `HTTPConnection` class
When you want to define dependencies that should be compatible with both HTTP and WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a `Request` or a `WebSocket`.
You can import it from `fastapi.requests`:
```python
from fastapi.requests import HTTPConnection
```
::: fastapi.requests.HTTPConnection
================================================
FILE: docs/en/docs/reference/index.md
================================================
# Reference
Here's the reference or code API, the classes, functions, parameters, attributes, and
all the FastAPI parts you can use in your applications.
If you want to **learn FastAPI** you are much better off reading the
[FastAPI Tutorial](https://fastapi.tiangolo.com/tutorial/).
================================================
FILE: docs/en/docs/reference/middleware.md
================================================
# Middleware
There are several middlewares available provided by Starlette directly.
Read more about them in the [FastAPI docs for Middleware](https://fastapi.tiangolo.com/advanced/middleware/).
::: fastapi.middleware.cors.CORSMiddleware
It can be imported from `fastapi`:
```python
from fastapi.middleware.cors import CORSMiddleware
```
::: fastapi.middleware.gzip.GZipMiddleware
It can be imported from `fastapi`:
```python
from fastapi.middleware.gzip import GZipMiddleware
```
::: fastapi.middleware.httpsredirect.HTTPSRedirectMiddleware
It can be imported from `fastapi`:
```python
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
```
::: fastapi.middleware.trustedhost.TrustedHostMiddleware
It can be imported from `fastapi`:
```python
from fastapi.middleware.trustedhost import TrustedHostMiddleware
```
================================================
FILE: docs/en/docs/reference/openapi/docs.md
================================================
# OpenAPI `docs`
Utilities to handle OpenAPI automatic UI documentation, including Swagger UI (by default at `/docs`) and ReDoc (by default at `/redoc`).
::: fastapi.openapi.docs.get_swagger_ui_html
::: fastapi.openapi.docs.get_redoc_html
::: fastapi.openapi.docs.get_swagger_ui_oauth2_redirect_html
::: fastapi.openapi.docs.swagger_ui_default_parameters
================================================
FILE: docs/en/docs/reference/openapi/index.md
================================================
# OpenAPI
There are several utilities to handle OpenAPI.
You normally don't need to use them unless you have a specific advanced use case that requires it.
================================================
FILE: docs/en/docs/reference/openapi/models.md
================================================
# OpenAPI `models`
OpenAPI Pydantic models used to generate and validate the generated OpenAPI.
::: fastapi.openapi.models
================================================
FILE: docs/en/docs/reference/parameters.md
================================================
# Request Parameters
Here's the reference information for the request parameters.
These are the special functions that you can put in *path operation function* parameters or dependency functions with `Annotated` to get data from the request.
It includes:
* `Query()`
* `Path()`
* `Body()`
* `Cookie()`
* `Header()`
* `Form()`
* `File()`
You can import them all directly from `fastapi`:
```python
from fastapi import Body, Cookie, File, Form, Header, Path, Query
```
::: fastapi.Query
::: fastapi.Path
::: fastapi.Body
::: fastapi.Cookie
::: fastapi.Header
::: fastapi.Form
::: fastapi.File
================================================
FILE: docs/en/docs/reference/request.md
================================================
# `Request` class
You can declare a parameter in a *path operation function* or dependency to be of type `Request` and then you can access the raw request object directly, without any validation, etc.
Read more about it in the [FastAPI docs about using Request directly](https://fastapi.tiangolo.com/advanced/using-request-directly/)
You can import it directly from `fastapi`:
```python
from fastapi import Request
```
/// tip
When you want to define dependencies that should be compatible with both HTTP and WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a `Request` or a `WebSocket`.
///
::: fastapi.Request
================================================
FILE: docs/en/docs/reference/response.md
================================================
# `Response` class
You can declare a parameter in a *path operation function* or dependency to be of type `Response` and then you can set data for the response like headers or cookies.
You can also use it directly to create an instance of it and return it from your *path operations*.
Read more about it in the [FastAPI docs about returning a custom Response](https://fastapi.tiangolo.com/advanced/response-directly/#returning-a-custom-response)
You can import it directly from `fastapi`:
```python
from fastapi import Response
```
::: fastapi.Response
================================================
FILE: docs/en/docs/reference/responses.md
================================================
# Custom Response Classes - File, HTML, Redirect, Streaming, etc.
There are several custom response classes you can use to create an instance and return them directly from your *path operations*.
Read more about it in the [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/).
You can import them directly from `fastapi.responses`:
```python
from fastapi.responses import (
FileResponse,
HTMLResponse,
JSONResponse,
ORJSONResponse,
PlainTextResponse,
RedirectResponse,
Response,
StreamingResponse,
UJSONResponse,
)
```
## FastAPI Responses
There were a couple of custom FastAPI response classes that were intended to optimize JSON performance.
However, they are now deprecated as you will now get better performance by using a [Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/).
That way, Pydantic will serialize the data into JSON bytes on the Rust side, which will achieve better performance than these custom JSON responses.
Read more about it in [Custom Response - HTML, Stream, File, others - `orjson` or Response Model](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model).
::: fastapi.responses.UJSONResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.ORJSONResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
## Starlette Responses
You can read more about all of them in the [FastAPI docs for Custom Response](https://fastapi.tiangolo.com/advanced/custom-response/) and in the [Starlette docs about Responses](https://starlette.dev/responses/).
::: fastapi.responses.FileResponse
options:
members:
- chunk_size
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.HTMLResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.JSONResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.PlainTextResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.RedirectResponse
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.Response
options:
members:
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
::: fastapi.responses.StreamingResponse
options:
members:
- body_iterator
- charset
- status_code
- media_type
- body
- background
- raw_headers
- render
- init_headers
- headers
- set_cookie
- delete_cookie
================================================
FILE: docs/en/docs/reference/security/index.md
================================================
# Security Tools
When you need to declare dependencies with OAuth2 scopes you use `Security()`.
But you still need to define what is the dependable, the callable that you pass as a parameter to `Depends()` or `Security()`.
There are multiple tools that you can use to create those dependables, and they get integrated into OpenAPI so they are shown in the automatic docs UI, they can be used by automatically generated clients and SDKs, etc.
You can import them from `fastapi.security`:
```python
from fastapi.security import (
APIKeyCookie,
APIKeyHeader,
APIKeyQuery,
HTTPAuthorizationCredentials,
HTTPBasic,
HTTPBasicCredentials,
HTTPBearer,
HTTPDigest,
OAuth2,
OAuth2AuthorizationCodeBearer,
OAuth2PasswordBearer,
OAuth2PasswordRequestForm,
OAuth2PasswordRequestFormStrict,
OpenIdConnect,
SecurityScopes,
)
```
Read more about them in the [FastAPI docs about Security](https://fastapi.tiangolo.com/tutorial/security/).
## API Key Security Schemes
::: fastapi.security.APIKeyCookie
::: fastapi.security.APIKeyHeader
::: fastapi.security.APIKeyQuery
## HTTP Authentication Schemes
::: fastapi.security.HTTPBasic
::: fastapi.security.HTTPBearer
::: fastapi.security.HTTPDigest
## HTTP Credentials
::: fastapi.security.HTTPAuthorizationCredentials
::: fastapi.security.HTTPBasicCredentials
## OAuth2 Authentication
::: fastapi.security.OAuth2
::: fastapi.security.OAuth2AuthorizationCodeBearer
::: fastapi.security.OAuth2PasswordBearer
## OAuth2 Password Form
::: fastapi.security.OAuth2PasswordRequestForm
::: fastapi.security.OAuth2PasswordRequestFormStrict
## OAuth2 Security Scopes in Dependencies
::: fastapi.security.SecurityScopes
## OpenID Connect
::: fastapi.security.OpenIdConnect
================================================
FILE: docs/en/docs/reference/staticfiles.md
================================================
# Static Files - `StaticFiles`
You can use the `StaticFiles` class to serve static files, like JavaScript, CSS, images, etc.
Read more about it in the [FastAPI docs for Static Files](https://fastapi.tiangolo.com/tutorial/static-files/).
You can import it directly from `fastapi.staticfiles`:
```python
from fastapi.staticfiles import StaticFiles
```
::: fastapi.staticfiles.StaticFiles
================================================
FILE: docs/en/docs/reference/status.md
================================================
# Status Codes
You can import the `status` module from `fastapi`:
```python
from fastapi import status
```
`status` is provided directly by Starlette.
It contains a group of named constants (variables) with integer status codes.
For example:
* 200: `status.HTTP_200_OK`
* 403: `status.HTTP_403_FORBIDDEN`
* etc.
It can be convenient to quickly access HTTP (and WebSocket) status codes in your app, using autocompletion for the name without having to remember the integer status codes by memory.
Read more about it in the [FastAPI docs about Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
## Example
```python
from fastapi import FastAPI, status
app = FastAPI()
@app.get("/items/", status_code=status.HTTP_418_IM_A_TEAPOT)
def read_items():
return [{"name": "Plumbus"}, {"name": "Portal Gun"}]
```
::: fastapi.status
================================================
FILE: docs/en/docs/reference/templating.md
================================================
# Templating - `Jinja2Templates`
You can use the `Jinja2Templates` class to render Jinja templates.
Read more about it in the [FastAPI docs for Templates](https://fastapi.tiangolo.com/advanced/templates/).
You can import it directly from `fastapi.templating`:
```python
from fastapi.templating import Jinja2Templates
```
::: fastapi.templating.Jinja2Templates
================================================
FILE: docs/en/docs/reference/testclient.md
================================================
# Test Client - `TestClient`
You can use the `TestClient` class to test FastAPI applications without creating an actual HTTP and socket connection, just communicating directly with the FastAPI code.
Read more about it in the [FastAPI docs for Testing](https://fastapi.tiangolo.com/tutorial/testing/).
You can import it directly from `fastapi.testclient`:
```python
from fastapi.testclient import TestClient
```
::: fastapi.testclient.TestClient
================================================
FILE: docs/en/docs/reference/uploadfile.md
================================================
# `UploadFile` class
You can define *path operation function* parameters to be of the type `UploadFile` to receive files from the request.
You can import it directly from `fastapi`:
```python
from fastapi import UploadFile
```
::: fastapi.UploadFile
options:
members:
- file
- filename
- size
- headers
- content_type
- read
- write
- seek
- close
================================================
FILE: docs/en/docs/reference/websockets.md
================================================
# WebSockets
When defining WebSockets, you normally declare a parameter of type `WebSocket` and with it you can read data from the client and send data to it.
Read more about it in the [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/)
It is provided directly by Starlette, but you can import it from `fastapi`:
```python
from fastapi import WebSocket
```
/// tip
When you want to define dependencies that should be compatible with both HTTP and WebSockets, you can define a parameter that takes an `HTTPConnection` instead of a `Request` or a `WebSocket`.
///
::: fastapi.WebSocket
options:
members:
- scope
- app
- url
- base_url
- headers
- query_params
- path_params
- cookies
- client
- state
- url_for
- client_state
- application_state
- receive
- send
- accept
- receive_text
- receive_bytes
- receive_json
- iter_text
- iter_bytes
- iter_json
- send_text
- send_bytes
- send_json
- close
## WebSockets - additional classes
Additional classes for handling WebSockets.
Provided directly by Starlette, but you can import it from `fastapi`:
```python
from fastapi.websockets import WebSocketDisconnect, WebSocketState
```
::: fastapi.websockets.WebSocketDisconnect
When a client disconnects, a `WebSocketDisconnect` exception is raised, you can catch it.
You can import it directly form `fastapi`:
```python
from fastapi import WebSocketDisconnect
```
Read more about it in the [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/#handling-disconnections-and-multiple-clients)
::: fastapi.websockets.WebSocketState
`WebSocketState` is an enumeration of the possible states of a WebSocket connection.
================================================
FILE: docs/en/docs/release-notes.md
================================================
[File too large to display: 639.6 KB]
================================================
FILE: docs/en/docs/resources/index.md
================================================
# Resources { #resources }
Additional resources, external links, and more. ✈️
================================================
FILE: docs/en/docs/translation-banner.md
================================================
/// details | 🌐 Translation by AI and humans
This translation was made by AI guided by humans. 🤝
It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖
You can improve this translation by [helping us guide the AI LLM better](https://fastapi.tiangolo.com/contributing/#translations).
[English version](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/en/docs/tutorial/background-tasks.md
================================================
# Background Tasks { #background-tasks }
You can define background tasks to be run *after* returning a response.
This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving the response.
This includes, for example:
* Email notifications sent after performing an action:
* As connecting to an email server and sending an email tends to be "slow" (several seconds), you can return the response right away and send the email notification in the background.
* Processing data:
* For example, let's say you receive a file that must go through a slow process, you can return a response of "Accepted" (HTTP 202) and process the file in the background.
## Using `BackgroundTasks` { #using-backgroundtasks }
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter.
## Create a task function { #create-a-task-function }
Create a function to be run as the background task.
It is just a standard function that can receive parameters.
It can be an `async def` or normal `def` function, **FastAPI** will know how to handle it correctly.
In this case, the task function will write to a file (simulating sending an email).
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Add the background task { #add-the-background-task }
Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` receives as arguments:
* A task function to be run in the background (`write_notification`).
* Any sequence of arguments that should be passed to the task function in order (`email`).
* Any keyword arguments that should be passed to the task function (`message="some notification"`).
## Dependency Injection { #dependency-injection }
Using `BackgroundTasks` also works with the dependency injection system, you can declare a parameter of type `BackgroundTasks` at multiple levels: in a *path operation function*, in a dependency (dependable), in a sub-dependency, etc.
**FastAPI** knows what to do in each case and how to reuse the same object, so that all the background tasks are merged together and are run in the background afterwards:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
In this example, the messages will be written to the `log.txt` file *after* the response is sent.
If there was a query in the request, it will be written to the log in a background task.
And then another background task generated at the *path operation function* will write a message using the `email` path parameter.
## Technical Details { #technical-details }
The class `BackgroundTasks` comes directly from [`starlette.background`](https://www.starlette.dev/background/).
It is imported/included directly into FastAPI so that you can import it from `fastapi` and avoid accidentally importing the alternative `BackgroundTask` (without the `s` at the end) from `starlette.background`.
By only using `BackgroundTasks` (and not `BackgroundTask`), it's then possible to use it as a *path operation function* parameter and have **FastAPI** handle the rest for you, just like when using the `Request` object directly.
It's still possible to use `BackgroundTask` alone in FastAPI, but you have to create the object in your code and return a Starlette `Response` including it.
You can see more details in [Starlette's official docs for Background Tasks](https://www.starlette.dev/background/).
## Caveat { #caveat }
If you need to perform heavy background computation and you don't necessarily need it to be run by the same process (for example, you don't need to share memory, variables, etc), you might benefit from using other bigger tools like [Celery](https://docs.celeryq.dev).
They tend to require more complex configurations, a message/job queue manager, like RabbitMQ or Redis, but they allow you to run background tasks in multiple processes, and especially, in multiple servers.
But if you need to access variables and objects from the same **FastAPI** app, or you need to perform small background tasks (like sending an email notification), you can simply just use `BackgroundTasks`.
## Recap { #recap }
Import and use `BackgroundTasks` with parameters in *path operation functions* and dependencies to add background tasks.
================================================
FILE: docs/en/docs/tutorial/bigger-applications.md
================================================
# Bigger Applications - Multiple Files { #bigger-applications-multiple-files }
If you are building an application or a web API, it's rarely the case that you can put everything in a single file.
**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility.
/// info
If you come from Flask, this would be the equivalent of Flask's Blueprints.
///
## An example file structure { #an-example-file-structure }
Let's say you have a file structure like this:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip
There are several `__init__.py` files: one in each directory or subdirectory.
This is what allows importing code from one file into another.
For example, in `app/main.py` you could have a line like:
```
from app.routers import items
```
///
* The `app` directory contains everything. And it has an empty file `app/__init__.py`, so it is a "Python package" (a collection of "Python modules"): `app`.
* It contains an `app/main.py` file. As it is inside a Python package (a directory with a file `__init__.py`), it is a "module" of that package: `app.main`.
* There's also an `app/dependencies.py` file, just like `app/main.py`, it is a "module": `app.dependencies`.
* There's a subdirectory `app/routers/` with another file `__init__.py`, so it's a "Python subpackage": `app.routers`.
* The file `app/routers/items.py` is inside a package, `app/routers/`, so, it's a submodule: `app.routers.items`.
* The same with `app/routers/users.py`, it's another submodule: `app.routers.users`.
* There's also a subdirectory `app/internal/` with another file `__init__.py`, so it's another "Python subpackage": `app.internal`.
* And the file `app/internal/admin.py` is another submodule: `app.internal.admin`.
The same file structure with comments:
```bash
.
├── app # "app" is a Python package
│ ├── __init__.py # this file makes "app" a "Python package"
│ ├── main.py # "main" module, e.g. import app.main
│ ├── dependencies.py # "dependencies" module, e.g. import app.dependencies
│ └── routers # "routers" is a "Python subpackage"
│ │ ├── __init__.py # makes "routers" a "Python subpackage"
│ │ ├── items.py # "items" submodule, e.g. import app.routers.items
│ │ └── users.py # "users" submodule, e.g. import app.routers.users
│ └── internal # "internal" is a "Python subpackage"
│ ├── __init__.py # makes "internal" a "Python subpackage"
│ └── admin.py # "admin" submodule, e.g. import app.internal.admin
```
## `APIRouter` { #apirouter }
Let's say the file dedicated to handling just users is the submodule at `/app/routers/users.py`.
You want to have the *path operations* related to your users separated from the rest of the code, to keep it organized.
But it's still part of the same **FastAPI** application/web API (it's part of the same "Python Package").
You can create the *path operations* for that module using `APIRouter`.
### Import `APIRouter` { #import-apirouter }
You import it and create an "instance" the same way you would with the class `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Path operations* with `APIRouter` { #path-operations-with-apirouter }
And then you use it to declare your *path operations*.
Use it the same way you would use the `FastAPI` class:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
You can think of `APIRouter` as a "mini `FastAPI`" class.
All the same options are supported.
All the same `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip
In this example, the variable is called `router`, but you can name it however you want.
///
We are going to include this `APIRouter` in the main `FastAPI` app, but first, let's check the dependencies and another `APIRouter`.
## Dependencies { #dependencies }
We see that we are going to need some dependencies used in several places of the application.
So we put them in their own `dependencies` module (`app/dependencies.py`).
We will now use a simple dependency to read a custom `X-Token` header:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip
We are using an invented header to simplify this example.
But in real cases you will get better results using the integrated [Security utilities](security/index.md).
///
## Another module with `APIRouter` { #another-module-with-apirouter }
Let's say you also have the endpoints dedicated to handling "items" from your application in the module at `app/routers/items.py`.
You have *path operations* for:
* `/items/`
* `/items/{item_id}`
It's all the same structure as with `app/routers/users.py`.
But we want to be smarter and simplify the code a bit.
We know all the *path operations* in this module have the same:
* Path `prefix`: `/items`.
* `tags`: (just one tag: `items`).
* Extra `responses`.
* `dependencies`: they all need that `X-Token` dependency we created.
So, instead of adding all that to each *path operation*, we can add it to the `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
As the path of each *path operation* has to start with `/`, like in:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...the prefix must not include a final `/`.
So, the prefix in this case is `/items`.
We can also add a list of `tags` and extra `responses` that will be applied to all the *path operations* included in this router.
And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them.
/// tip
Note that, much like [dependencies in *path operation decorators*](dependencies/dependencies-in-path-operation-decorators.md), no value will be passed to your *path operation function*.
///
The end result is that the item paths are now:
* `/items/`
* `/items/{item_id}`
...as we intended.
* They will be marked with a list of tags that contain a single string `"items"`.
* These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
* All of them will include the predefined `responses`.
* All these *path operations* will have the list of `dependencies` evaluated/executed before them.
* If you also declare dependencies in a specific *path operation*, **they will be executed too**.
* The router dependencies are executed first, then the [`dependencies` in the decorator](dependencies/dependencies-in-path-operation-decorators.md), and then the normal parameter dependencies.
* You can also add [`Security` dependencies with `scopes`](../advanced/security/oauth2-scopes.md).
/// tip
Having `dependencies` in the `APIRouter` can be used, for example, to require authentication for a whole group of *path operations*. Even if the dependencies are not added individually to each one of them.
///
/// check
The `prefix`, `tags`, `responses`, and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
///
### Import the dependencies { #import-the-dependencies }
This code lives in the module `app.routers.items`, the file `app/routers/items.py`.
And we need to get the dependency function from the module `app.dependencies`, the file `app/dependencies.py`.
So we use a relative import with `..` for the dependencies:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### How relative imports work { #how-relative-imports-work }
/// tip
If you know perfectly how imports work, continue to the next section below.
///
A single dot `.`, like in:
```Python
from .dependencies import get_token_header
```
would mean:
* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)...
* find the module `dependencies` (an imaginary file at `app/routers/dependencies.py`)...
* and from it, import the function `get_token_header`.
But that file doesn't exist, our dependencies are in a file at `app/dependencies.py`.
Remember how our app/file structure looks like:
---
The two dots `..`, like in:
```Python
from ..dependencies import get_token_header
```
mean:
* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)...
* go to the parent package (the directory `app/`)...
* and in there, find the module `dependencies` (the file at `app/dependencies.py`)...
* and from it, import the function `get_token_header`.
That works correctly! 🎉
---
The same way, if we had used three dots `...`, like in:
```Python
from ...dependencies import get_token_header
```
that would mean:
* Starting in the same package that this module (the file `app/routers/items.py`) lives in (the directory `app/routers/`)...
* go to the parent package (the directory `app/`)...
* then go to the parent of that package (there's no parent package, `app` is the top level 😱)...
* and in there, find the module `dependencies` (the file at `app/dependencies.py`)...
* and from it, import the function `get_token_header`.
That would refer to some package above `app/`, with its own file `__init__.py`, etc. But we don't have that. So, that would throw an error in our example. 🚨
But now you know how it works, so you can use relative imports in your own apps no matter how complex they are. 🤓
### Add some custom `tags`, `responses`, and `dependencies` { #add-some-custom-tags-responses-and-dependencies }
We are not adding the prefix `/items` nor the `tags=["items"]` to each *path operation* because we added them to the `APIRouter`.
But we can still add _more_ `tags` that will be applied to a specific *path operation*, and also some extra `responses` specific to that *path operation*:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip
This last path operation will have the combination of tags: `["items", "custom"]`.
And it will also have both responses in the documentation, one for `404` and one for `403`.
///
## The main `FastAPI` { #the-main-fastapi }
Now, let's see the module at `app/main.py`.
Here's where you import and use the class `FastAPI`.
This will be the main file in your application that ties everything together.
And as most of your logic will now live in its own specific module, the main file will be quite simple.
### Import `FastAPI` { #import-fastapi }
You import and create a `FastAPI` class as normally.
And we can even declare [global dependencies](dependencies/global-dependencies.md) that will be combined with the dependencies for each `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Import the `APIRouter` { #import-the-apirouter }
Now we import the other submodules that have `APIRouter`s:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
As the files `app/routers/users.py` and `app/routers/items.py` are submodules that are part of the same Python package `app`, we can use a single dot `.` to import them using "relative imports".
### How the importing works { #how-the-importing-works }
The section:
```Python
from .routers import items, users
```
means:
* Starting in the same package that this module (the file `app/main.py`) lives in (the directory `app/`)...
* look for the subpackage `routers` (the directory at `app/routers/`)...
* and from it, import the submodule `items` (the file at `app/routers/items.py`) and `users` (the file at `app/routers/users.py`)...
The module `items` will have a variable `router` (`items.router`). This is the same one we created in the file `app/routers/items.py`, it's an `APIRouter` object.
And then we do the same for the module `users`.
We could also import them like:
```Python
from app.routers import items, users
```
/// info
The first version is a "relative import":
```Python
from .routers import items, users
```
The second version is an "absolute import":
```Python
from app.routers import items, users
```
To learn more about Python Packages and Modules, read [the official Python documentation about Modules](https://docs.python.org/3/tutorial/modules.html).
///
### Avoid name collisions { #avoid-name-collisions }
We are importing the submodule `items` directly, instead of importing just its variable `router`.
This is because we also have another variable named `router` in the submodule `users`.
If we had imported one after the other, like:
```Python
from .routers.items import router
from .routers.users import router
```
the `router` from `users` would overwrite the one from `items` and we wouldn't be able to use them at the same time.
So, to be able to use both of them in the same file, we import the submodules directly:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Include the `APIRouter`s for `users` and `items` { #include-the-apirouters-for-users-and-items }
Now, let's include the `router`s from the submodules `users` and `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info
`users.router` contains the `APIRouter` inside of the file `app/routers/users.py`.
And `items.router` contains the `APIRouter` inside of the file `app/routers/items.py`.
///
With `app.include_router()` we can add each `APIRouter` to the main `FastAPI` application.
It will include all the routes from that router as part of it.
/// note | Technical Details
It will actually internally create a *path operation* for each *path operation* that was declared in the `APIRouter`.
So, behind the scenes, it will actually work as if everything was the same single app.
///
/// check
You don't have to worry about performance when including routers.
This will take microseconds and will only happen at startup.
So it won't affect performance. ⚡
///
### Include an `APIRouter` with a custom `prefix`, `tags`, `responses`, and `dependencies` { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Now, let's imagine your organization gave you the `app/internal/admin.py` file.
It contains an `APIRouter` with some admin *path operations* that your organization shares between several projects.
For this example it will be super simple. But let's say that because it is shared with other projects in the organization, we cannot modify it and add a `prefix`, `dependencies`, `tags`, etc. directly to the `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
But we still want to set a custom `prefix` when including the `APIRouter` so that all its *path operations* start with `/admin`, we want to secure it with the `dependencies` we already have for this project, and we want to include `tags` and `responses`.
We can declare all that without having to modify the original `APIRouter` by passing those parameters to `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
That way, the original `APIRouter` will stay unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization.
The result is that in our app, each of the *path operations* from the `admin` module will have:
* The prefix `/admin`.
* The tag `admin`.
* The dependency `get_token_header`.
* The response `418`. 🍵
But that will only affect that `APIRouter` in our app, not in any other code that uses it.
So, for example, other projects could use the same `APIRouter` with a different authentication method.
### Include a *path operation* { #include-a-path-operation }
We can also add *path operations* directly to the `FastAPI` app.
Here we do it... just to show that we can 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
and it will work correctly, together with all the other *path operations* added with `app.include_router()`.
/// info | Very Technical Details
**Note**: this is a very technical detail that you probably can **just skip**.
---
The `APIRouter`s are not "mounted", they are not isolated from the rest of the application.
This is because we want to include their *path operations* in the OpenAPI schema and the user interfaces.
As we cannot just isolate them and "mount" them independently of the rest, the *path operations* are "cloned" (re-created), not included directly.
///
## Configure the `entrypoint` in `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
As your FastAPI `app` object lives in `app/main.py`, you can configure the `entrypoint` in your `pyproject.toml` file like this:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
that is equivalent to importing like:
```python
from app.main import app
```
That way the `fastapi` command will know where to find your app.
/// Note
You could also pass the path to the command, like:
```console
$ fastapi dev app/main.py
```
But you would have to remember to pass the correct path every time you call the `fastapi` command.
Additionally, other tools might not be able to find it, for example the [VS Code Extension](../editor-support.md) or [FastAPI Cloud](https://fastapicloud.com), so it is recommended to use the `entrypoint` in `pyproject.toml`.
///
## Check the automatic API docs { #check-the-automatic-api-docs }
Now, run your app:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
And open the docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags:
## Include the same router multiple times with different `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
You can also use `.include_router()` multiple times with the *same* router using different prefixes.
This could be useful, for example, to expose the same API under different prefixes, e.g. `/api/v1` and `/api/latest`.
This is an advanced usage that you might not really need, but it's there in case you do.
## Include an `APIRouter` in another { #include-an-apirouter-in-another }
The same way you can include an `APIRouter` in a `FastAPI` application, you can include an `APIRouter` in another `APIRouter` using:
```Python
router.include_router(other_router)
```
Make sure you do it before including `router` in the `FastAPI` app, so that the *path operations* from `other_router` are also included.
================================================
FILE: docs/en/docs/tutorial/body-fields.md
================================================
# Body - Fields { #body-fields }
The same way you can declare additional validation and metadata in *path operation function* parameters with `Query`, `Path` and `Body`, you can declare validation and metadata inside of Pydantic models using Pydantic's `Field`.
## Import `Field` { #import-field }
First, you have to import it:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning
Notice that `Field` is imported directly from `pydantic`, not from `fastapi` as are all the rest (`Query`, `Path`, `Body`, etc).
///
## Declare model attributes { #declare-model-attributes }
You can then use `Field` with model attributes:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
/// note | Technical Details
Actually, `Query`, `Path` and others you'll see next create objects of subclasses of a common `Param` class, which is itself a subclass of Pydantic's `FieldInfo` class.
And Pydantic's `Field` returns an instance of `FieldInfo` as well.
`Body` also returns objects of a subclass of `FieldInfo` directly. And there are others you will see later that are subclasses of the `Body` class.
Remember that when you import `Query`, `Path`, and others from `fastapi`, those are actually functions that return special classes.
///
/// tip
Notice how each model's attribute with a type, default value and `Field` has the same structure as a *path operation function's* parameter, with `Field` instead of `Path`, `Query` and `Body`.
///
## Add extra information { #add-extra-information }
You can declare extra information in `Field`, `Query`, `Body`, etc. And it will be included in the generated JSON Schema.
You will learn more about adding extra information later in the docs, when learning to declare examples.
/// warning
Extra keys passed to `Field` will also be present in the resulting OpenAPI schema for your application.
As these keys may not necessarily be part of the OpenAPI specification, some OpenAPI tools, for example [the OpenAPI validator](https://validator.swagger.io/), may not work with your generated schema.
///
## Recap { #recap }
You can use Pydantic's `Field` to declare extra validations and metadata for model attributes.
You can also use the extra keyword arguments to pass additional JSON Schema metadata.
================================================
FILE: docs/en/docs/tutorial/body-multiple-params.md
================================================
# Body - Multiple Parameters { #body-multiple-parameters }
Now that we have seen how to use `Path` and `Query`, let's see more advanced uses of request body declarations.
## Mix `Path`, `Query` and body parameters { #mix-path-query-and-body-parameters }
First, of course, you can mix `Path`, `Query` and request body parameter declarations freely and **FastAPI** will know what to do.
And you can also declare body parameters as optional, by setting the default to `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note
Notice that, in this case, the `item` that would be taken from the body is optional. As it has a `None` default value.
///
## Multiple body parameters { #multiple-body-parameters }
In the previous example, the *path operations* would expect a JSON body with the attributes of an `Item`, like:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
But you can also declare multiple body parameters, e.g. `item` and `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
In this case, **FastAPI** will notice that there is more than one body parameter in the function (there are two parameters that are Pydantic models).
So, it will then use the parameter names as keys (field names) in the body, and expect a body like:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note
Notice that even though the `item` was declared the same way as before, it is now expected to be inside of the body with a key `item`.
///
**FastAPI** will do the automatic conversion from the request, so that the parameter `item` receives its specific content and the same for `user`.
It will perform the validation of the compound data, and will document it like that for the OpenAPI schema and automatic docs.
## Singular values in body { #singular-values-in-body }
The same way there is a `Query` and `Path` to define extra data for query and path parameters, **FastAPI** provides an equivalent `Body`.
For example, extending the previous model, you could decide that you want to have another key `importance` in the same body, besides the `item` and `user`.
If you declare it as is, because it is a singular value, **FastAPI** will assume that it is a query parameter.
But you can instruct **FastAPI** to treat it as another body key using `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
In this case, **FastAPI** will expect a body like:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Again, it will convert the data types, validate, document, etc.
## Multiple body params and query { #multiple-body-params-and-query }
Of course, you can also declare additional query parameters whenever you need, additional to any body parameters.
As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do:
```Python
q: str | None = None
```
For example:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info
`Body` also has all the same extra validation and metadata parameters as `Query`, `Path` and others you will see later.
///
## Embed a single body parameter { #embed-a-single-body-parameter }
Let's say you only have a single `item` body parameter from a Pydantic model `Item`.
By default, **FastAPI** will then expect its body directly.
But if you want it to expect a JSON with a key `item` and inside of it the model contents, as it does when you declare extra body parameters, you can use the special `Body` parameter `embed`:
```Python
item: Item = Body(embed=True)
```
as in:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
In this case **FastAPI** will expect a body like:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
instead of:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Recap { #recap }
You can add multiple body parameters to your *path operation function*, even though a request can only have a single body.
But **FastAPI** will handle it, give you the correct data in your function, and validate and document the correct schema in the *path operation*.
You can also declare singular values to be received as part of the body.
And you can instruct **FastAPI** to embed the body in a key even when there is only a single parameter declared.
================================================
FILE: docs/en/docs/tutorial/body-nested-models.md
================================================
# Body - Nested Models { #body-nested-models }
With **FastAPI**, you can define, validate, document, and use arbitrarily deeply nested models (thanks to Pydantic).
## List fields { #list-fields }
You can define an attribute to be a subtype. For example, a Python `list`:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
This will make `tags` be a list, although it doesn't declare the type of the elements of the list.
## List fields with type parameter { #list-fields-with-type-parameter }
But Python has a specific way to declare lists with internal types, or "type parameters":
### Declare a `list` with a type parameter { #declare-a-list-with-a-type-parameter }
To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`,
pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
```Python
my_list: list[str]
```
That's all standard Python syntax for type declarations.
Use that same standard syntax for model attributes with internal types.
So, in our example, we can make `tags` be specifically a "list of strings":
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Set types { #set-types }
But then we think about it, and realize that tags shouldn't repeat, they would probably be unique strings.
And Python has a special data type for sets of unique items, the `set`.
Then we can declare `tags` as a set of strings:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
And whenever you output that data, even if the source had duplicates, it will be output as a set of unique items.
And it will be annotated / documented accordingly too.
## Nested Models { #nested-models }
Each attribute of a Pydantic model has a type.
But that type can itself be another Pydantic model.
So, you can declare deeply nested JSON "objects" with specific attribute names, types and validations.
All that, arbitrarily nested.
### Define a submodel { #define-a-submodel }
For example, we can define an `Image` model:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Use the submodel as a type { #use-the-submodel-as-a-type }
And then we can use it as the type of an attribute:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
This would mean that **FastAPI** would expect a body similar to:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Again, doing just that declaration, with **FastAPI** you get:
* Editor support (completion, etc.), even for nested models
* Data conversion
* Data validation
* Automatic documentation
## Special types and validation { #special-types-and-validation }
Apart from normal singular types like `str`, `int`, `float`, etc. you can use more complex singular types that inherit from `str`.
To see all the options you have, checkout [Pydantic's Type Overview](https://docs.pydantic.dev/latest/concepts/types/). You will see some examples in the next chapter.
For example, as in the `Image` model we have a `url` field, we can declare it to be an instance of Pydantic's `HttpUrl` instead of a `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
## Attributes with lists of submodels { #attributes-with-lists-of-submodels }
You can also use Pydantic models as subtypes of `list`, `set`, etc.:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
This will expect (convert, validate, document, etc.) a JSON body like:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info
Notice how the `images` key now has a list of image objects.
///
## Deeply nested models { #deeply-nested-models }
You can define arbitrarily deeply nested models:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info
Notice how `Offer` has a list of `Item`s, which in turn have an optional list of `Image`s
///
## Bodies of pure lists { #bodies-of-pure-lists }
If the top level value of the JSON body you expect is a JSON `array` (a Python `list`), you can declare the type in the parameter of the function, the same as in Pydantic models:
```Python
images: list[Image]
```
as in:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Editor support everywhere { #editor-support-everywhere }
And you get editor support everywhere.
Even for items inside of lists:
You couldn't get this kind of editor support if you were working directly with `dict` instead of Pydantic models.
But you don't have to worry about them either, incoming dicts are converted automatically and your output is converted automatically to JSON too.
## Bodies of arbitrary `dict`s { #bodies-of-arbitrary-dicts }
You can also declare a body as a `dict` with keys of some type and values of some other type.
This way, you don't have to know beforehand what the valid field/attribute names are (as would be the case with Pydantic models).
This would be useful if you want to receive keys that you don't already know.
---
Another useful case is when you want to have keys of another type (e.g., `int`).
That's what we are going to see here.
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip
Keep in mind that JSON only supports `str` as keys.
But Pydantic has automatic data conversion.
This means that, even though your API clients can only send strings as keys, as long as those strings contain pure integers, Pydantic will convert them and validate them.
And the `dict` you receive as `weights` will actually have `int` keys and `float` values.
///
## Recap { #recap }
With **FastAPI** you have the maximum flexibility provided by Pydantic models, while keeping your code simple, short and elegant.
But with all the benefits:
* Editor support (completion everywhere!)
* Data conversion (a.k.a. parsing / serialization)
* Data validation
* Schema documentation
* Automatic docs
================================================
FILE: docs/en/docs/tutorial/body-updates.md
================================================
# Body - Updates { #body-updates }
## Update replacing with `PUT` { #update-replacing-with-put }
To update an item you can use the [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operation.
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` is used to receive data that should replace the existing data.
### Warning about replacing { #warning-about-replacing }
That means that if you want to update the item `bar` using `PUT` with a body containing:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`.
And the data would be saved with that "new" `tax` of `10.5`.
## Partial updates with `PATCH` { #partial-updates-with-patch }
You can also use the [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operation to *partially* update data.
This means that you can send only the data that you want to update, leaving the rest intact.
/// note
`PATCH` is less commonly used and known than `PUT`.
And many teams use only `PUT`, even for partial updates.
You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions.
But this guide shows you, more or less, how they are intended to be used.
///
### Using Pydantic's `exclude_unset` parameter { #using-pydantics-exclude-unset-parameter }
If you want to receive partial updates, it's very useful to use the parameter `exclude_unset` in Pydantic's model's `.model_dump()`.
Like `item.model_dump(exclude_unset=True)`.
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
Then you can use this to generate a `dict` with only the data that was set (sent in the request), omitting default values:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Using Pydantic's `update` parameter { #using-pydantics-update-parameter }
Now, you can create a copy of the existing model using `.model_copy()`, and pass the `update` parameter with a `dict` containing the data to update.
Like `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Partial updates recap { #partial-updates-recap }
In summary, to apply partial updates you would:
* (Optionally) use `PATCH` instead of `PUT`.
* Retrieve the stored data.
* Put that data in a Pydantic model.
* Generate a `dict` without default values from the input model (using `exclude_unset`).
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
* Create a copy of the stored model, updating its attributes with the received partial updates (using the `update` parameter).
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
* This is comparable to using the model's `.model_dump()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`.
* Save the data to your DB.
* Return the updated model.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip
You can actually use this same technique with an HTTP `PUT` operation.
But the example here uses `PATCH` because it was created for these use cases.
///
/// note
Notice that the input model is still validated.
So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`).
To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in [Extra Models](extra-models.md).
///
================================================
FILE: docs/en/docs/tutorial/body.md
================================================
# Request Body { #request-body }
When you need to send data from a client (let's say, a browser) to your API, you send it as a **request body**.
A **request** body is data sent by the client to your API. A **response** body is the data your API sends to the client.
Your API almost always has to send a **response** body. But clients don't necessarily need to send **request bodies** all the time, sometimes they only request a path, maybe with some query parameters, but don't send a body.
To declare a **request** body, you use [Pydantic](https://docs.pydantic.dev/) models with all their power and benefits.
/// info
To send data, you should use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`.
Sending a body with a `GET` request has an undefined behavior in the specifications, nevertheless, it is supported by FastAPI, only for very complex/extreme use cases.
As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using `GET`, and proxies in the middle might not support it.
///
## Import Pydantic's `BaseModel` { #import-pydantics-basemodel }
First, you need to import `BaseModel` from `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Create your data model { #create-your-data-model }
Then you declare your data model as a class that inherits from `BaseModel`.
Use standard Python types for all the attributes:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional.
For example, this model above declares a JSON "`object`" (or Python `dict`) like:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...as `description` and `tax` are optional (with a default value of `None`), this JSON "`object`" would also be valid:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Declare it as a parameter { #declare-it-as-a-parameter }
To add it to your *path operation*, declare it the same way you declared path and query parameters:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...and declare its type as the model you created, `Item`.
## Results { #results }
With just that Python type declaration, **FastAPI** will:
* Read the body of the request as JSON.
* Convert the corresponding types (if needed).
* Validate the data.
* If the data is invalid, it will return a nice and clear error, indicating exactly where and what was the incorrect data.
* Give you the received data in the parameter `item`.
* As you declared it in the function to be of type `Item`, you will also have all the editor support (completion, etc) for all of the attributes and their types.
* Generate [JSON Schema](https://json-schema.org) definitions for your model, you can also use them anywhere else you like if it makes sense for your project.
* Those schemas will be part of the generated OpenAPI schema, and used by the automatic documentation UIs.
## Automatic docs { #automatic-docs }
The JSON Schemas of your models will be part of your OpenAPI generated schema, and will be shown in the interactive API docs:
And will also be used in the API docs inside each *path operation* that needs them:
## Editor support { #editor-support }
In your editor, inside your function you will get type hints and completion everywhere (this wouldn't happen if you received a `dict` instead of a Pydantic model):
You also get error checks for incorrect type operations:
This is not by chance, the whole framework was built around that design.
And it was thoroughly tested at the design phase, before any implementation, to ensure it would work with all the editors.
There were even some changes to Pydantic itself to support this.
The previous screenshots were taken with [Visual Studio Code](https://code.visualstudio.com).
But you would get the same editor support with [PyCharm](https://www.jetbrains.com/pycharm/) and most of the other Python editors:
/// tip
If you use [PyCharm](https://www.jetbrains.com/pycharm/) as your editor, you can use the [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
It improves editor support for Pydantic models, with:
* auto-completion
* type checks
* refactoring
* searching
* inspections
///
## Use the model { #use-the-model }
Inside of the function, you can access all the attributes of the model object directly:
{* ../../docs_src/body/tutorial002_py310.py *}
## Request body + path parameters { #request-body-path-parameters }
You can declare path parameters and request body at the same time.
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Request body + path + query parameters { #request-body-path-query-parameters }
You can also declare **body**, **path** and **query** parameters, all at the same time.
**FastAPI** will recognize each of them and take the data from the correct place.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
The function parameters will be recognized as follows:
* If the parameter is also declared in the **path**, it will be used as a path parameter.
* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc) it will be interpreted as a **query** parameter.
* If the parameter is declared to be of the type of a **Pydantic model**, it will be interpreted as a request **body**.
/// note
FastAPI will know that the value of `q` is not required because of the default value `= None`.
The `str | None` is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
But adding the type annotations will allow your editor to give you better support and detect errors.
///
## Without Pydantic { #without-pydantic }
If you don't want to use Pydantic models, you can also use **Body** parameters. See the docs for [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/en/docs/tutorial/cookie-param-models.md
================================================
# Cookie Parameter Models { #cookie-parameter-models }
If you have a group of **cookies** that are related, you can create a **Pydantic model** to declare them. 🍪
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
/// note
This is supported since FastAPI version `0.115.0`. 🤓
///
/// tip
This same technique applies to `Query`, `Cookie`, and `Header`. 😎
///
## Cookies with a Pydantic Model { #cookies-with-a-pydantic-model }
Declare the **cookie** parameters that you need in a **Pydantic model**, and then declare the parameter as `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** will **extract** the data for **each field** from the **cookies** received in the request and give you the Pydantic model you defined.
## Check the Docs { #check-the-docs }
You can see the defined cookies in the docs UI at `/docs`:
/// info
Have in mind that, as **browsers handle cookies** in special ways and behind the scenes, they **don't** easily allow **JavaScript** to touch them.
If you go to the **API docs UI** at `/docs` you will be able to see the **documentation** for cookies for your *path operations*.
But even if you **fill the data** and click "Execute", because the docs UI works with **JavaScript**, the cookies won't be sent, and you will see an **error** message as if you didn't write any values.
///
## Forbid Extra Cookies { #forbid-extra-cookies }
In some special use cases (probably not very common), you might want to **restrict** the cookies that you want to receive.
Your API now has the power to control its own cookie consent. 🤪🍪
You can use Pydantic's model configuration to `forbid` any `extra` fields:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
If a client tries to send some **extra cookies**, they will receive an **error** response.
Poor cookie banners with all their effort to get your consent for the API to reject it. 🍪
For example, if the client tries to send a `santa_tracker` cookie with a value of `good-list-please`, the client will receive an **error** response telling them that the `santa_tracker` cookie is not allowed:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Summary { #summary }
You can use **Pydantic models** to declare **cookies** in **FastAPI**. 😎
================================================
FILE: docs/en/docs/tutorial/cookie-params.md
================================================
# Cookie Parameters { #cookie-parameters }
You can define Cookie parameters the same way you define `Query` and `Path` parameters.
## Import `Cookie` { #import-cookie }
First import `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Declare `Cookie` parameters { #declare-cookie-parameters }
Then declare the cookie parameters using the same structure as with `Path` and `Query`.
You can define the default value as well as all the extra validation or annotation parameters:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Technical Details
`Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class.
But remember that when you import `Query`, `Path`, `Cookie` and others from `fastapi`, those are actually functions that return special classes.
///
/// info
To declare cookies, you need to use `Cookie`, because otherwise the parameters would be interpreted as query parameters.
///
/// info
Have in mind that, as **browsers handle cookies** in special ways and behind the scenes, they **don't** easily allow **JavaScript** to touch them.
If you go to the **API docs UI** at `/docs` you will be able to see the **documentation** for cookies for your *path operations*.
But even if you **fill the data** and click "Execute", because the docs UI works with **JavaScript**, the cookies won't be sent, and you will see an **error** message as if you didn't write any values.
///
## Recap { #recap }
Declare cookies with `Cookie`, using the same common pattern as `Query` and `Path`.
================================================
FILE: docs/en/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS or "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend.
## Origin { #origin }
An origin is the combination of protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), and port (`80`, `443`, `8080`).
So, all these are different origins:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Even if they are all in `localhost`, they use different protocols or ports, so, they are different "origins".
## Steps { #steps }
So, let's say you have a frontend running in your browser at `http://localhost:8080`, and its JavaScript is trying to communicate with a backend running at `http://localhost` (because we don't specify a port, the browser will assume the default port `80`).
Then, the browser will send an HTTP `OPTIONS` request to the `:80`-backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (`http://localhost:8080`) then the `:8080`-browser will let the JavaScript in the frontend send its request to the `:80`-backend.
To achieve this, the `:80`-backend must have a list of "allowed origins".
In this case, the list would have to include `http://localhost:8080` for the `:8080`-frontend to work correctly.
## Wildcards { #wildcards }
It's also possible to declare the list as `"*"` (a "wildcard") to say that all are allowed.
But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc.
So, for everything to work correctly, it's better to specify explicitly the allowed origins.
## Use `CORSMiddleware` { #use-corsmiddleware }
You can configure it in your **FastAPI** application using the `CORSMiddleware`.
* Import `CORSMiddleware`.
* Create a list of allowed origins (as strings).
* Add it as a "middleware" to your **FastAPI** application.
You can also specify whether your backend allows:
* Credentials (Authorization headers, Cookies, etc).
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
* Specific HTTP headers or all of them with the wildcard `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.
The following arguments are supported:
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. e.g. `'https://.*\.example\.org'`.
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for [simple CORS requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`.
None of `allow_origins`, `allow_methods` and `allow_headers` can be set to `['*']` if `allow_credentials` is set to `True`. All of them must be [explicitly specified](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `600`.
The middleware responds to two particular types of HTTP request...
### CORS preflight requests { #cors-preflight-requests }
These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers.
In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a `200` or `400` response for informational purposes.
### Simple requests { #simple-requests }
Any request with an `Origin` header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response.
## More info { #more-info }
For more info about CORS, check the [Mozilla CORS documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Technical Details
You could also use `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
///
================================================
FILE: docs/en/docs/tutorial/debugging.md
================================================
# Debugging { #debugging }
You can connect the debugger in your editor, for example with Visual Studio Code or PyCharm.
## Call `uvicorn` { #call-uvicorn }
In your FastAPI application, import and run `uvicorn` directly:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### About `__name__ == "__main__"` { #about-name-main }
The main purpose of the `__name__ == "__main__"` is to have some code that is executed when your file is called with:
```console
$ python myapp.py
```
but is not called when another file imports it, like in:
```Python
from myapp import app
```
#### More details { #more-details }
Let's say your file is named `myapp.py`.
If you run it with:
```console
$ python myapp.py
```
then the internal variable `__name__` in your file, created automatically by Python, will have as value the string `"__main__"`.
So, the section:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
will run.
---
This won't happen if you import that module (file).
So, if you have another file `importer.py` with:
```Python
from myapp import app
# Some more code
```
in that case, the automatically created variable `__name__` inside of `myapp.py` will not have the value `"__main__"`.
So, the line:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
will not be executed.
/// info
For more information, check [the official Python docs](https://docs.python.org/3/library/__main__.html).
///
## Run your code with your debugger { #run-your-code-with-your-debugger }
Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly from the debugger.
---
For example, in Visual Studio Code, you can:
* Go to the "Debug" panel.
* "Add configuration...".
* Select "Python"
* Run the debugger with the option "`Python: Current File (Integrated Terminal)`".
It will then start the server with your **FastAPI** code, stop at your breakpoints, etc.
Here's how it might look:
---
If you use Pycharm, you can:
* Open the "Run" menu.
* Select the option "Debug...".
* Then a context menu shows up.
* Select the file to debug (in this case, `main.py`).
It will then start the server with your **FastAPI** code, stop at your breakpoints, etc.
Here's how it might look:
================================================
FILE: docs/en/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Classes as Dependencies { #classes-as-dependencies }
Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example.
## A `dict` from the previous example { #a-dict-from-the-previous-example }
In the previous example, we were returning a `dict` from our dependency ("dependable"):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
But then we get a `dict` in the parameter `commons` of the *path operation function*.
And we know that editors can't provide a lot of support (like completion) for `dict`s, because they can't know their keys and value types.
We can do better...
## What makes a dependency { #what-makes-a-dependency }
Up to now you have seen dependencies declared as functions.
But that's not the only way to declare dependencies (although it would probably be the more common).
The key factor is that a dependency should be a "callable".
A "**callable**" in Python is anything that Python can "call" like a function.
So, if you have an object `something` (that might _not_ be a function) and you can "call" it (execute it) like:
```Python
something()
```
or
```Python
something(some_argument, some_keyword_argument="foo")
```
then it is a "callable".
## Classes as dependencies { #classes-as-dependencies_1 }
You might notice that to create an instance of a Python class, you use that same syntax.
For example:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
In this case, `fluffy` is an instance of the class `Cat`.
And to create `fluffy`, you are "calling" `Cat`.
So, a Python class is also a **callable**.
Then, in **FastAPI**, you could use a Python class as a dependency.
What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined.
If you pass a "callable" as a dependency in **FastAPI**, it will analyze the parameters for that "callable", and process them in the same way as the parameters for a *path operation function*. Including sub-dependencies.
That also applies to callables with no parameters at all. The same as it would be for *path operation functions* with no parameters.
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Pay attention to the `__init__` method used to create the instance of the class:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...it has the same parameters as our previous `common_parameters`:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Those parameters are what **FastAPI** will use to "solve" the dependency.
In both cases, it will have:
* An optional `q` query parameter that is a `str`.
* A `skip` query parameter that is an `int`, with a default of `0`.
* A `limit` query parameter that is an `int`, with a default of `100`.
In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.
## Use it { #use-it }
Now you can declare your dependency using this class.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function.
## Type annotation vs `Depends` { #type-annotation-vs-depends }
Notice how we write `CommonQueryParams` twice in the above code:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
The last `CommonQueryParams`, in:
```Python
... Depends(CommonQueryParams)
```
...is what **FastAPI** will actually use to know what is the dependency.
It is from this one that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
---
In this case, the first `CommonQueryParams`, in:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons: CommonQueryParams ...
```
////
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `Depends(CommonQueryParams)` for that).
You could actually write just:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...as in:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc:
## Shortcut { #shortcut }
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself.
For those specific cases, you can do the following:
Instead of writing:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...you write:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python
commons: CommonQueryParams = Depends()
```
////
You declare the dependency as the type of the parameter, and you use `Depends()` without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
The same example would then look like:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...and **FastAPI** will know what to do.
/// tip
If that seems more confusing than helpful, disregard it, you don't *need* it.
It is just a shortcut. Because **FastAPI** cares about helping you minimize code repetition.
///
================================================
FILE: docs/en/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Dependencies in path operation decorators { #dependencies-in-path-operation-decorators }
In some cases you don't really need the return value of a dependency inside your *path operation function*.
Or the dependency doesn't return a value.
But you still need it to be executed/solved.
For those cases, instead of declaring a *path operation function* parameter with `Depends`, you can add a `list` of `dependencies` to the *path operation decorator*.
## Add `dependencies` to the *path operation decorator* { #add-dependencies-to-the-path-operation-decorator }
The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
These dependencies will be executed/solved the same way as normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
/// tip
Some editors check for unused function parameters, and show them as errors.
Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors.
It might also help avoid confusion for new developers that see an unused parameter in your code and could think it's unnecessary.
///
/// info
In this example we use invented custom headers `X-Key` and `X-Token`.
But in real cases, when implementing security, you would get more benefits from using the integrated [Security utilities (the next chapter)](../security/index.md).
///
## Dependencies errors and return values { #dependencies-errors-and-return-values }
You can use the same dependency *functions* you use normally.
### Dependency requirements { #dependency-requirements }
They can declare request requirements (like headers) or other sub-dependencies:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Raise exceptions { #raise-exceptions }
These dependencies can `raise` exceptions, the same as normal dependencies:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Return values { #return-values }
And they can return values or not, the values won't be used.
So, you can reuse a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Dependencies for a group of *path operations* { #dependencies-for-a-group-of-path-operations }
Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md)), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.
## Global Dependencies { #global-dependencies }
Next we will see how to add dependencies to the whole `FastAPI` application, so that they apply to each *path operation*.
================================================
FILE: docs/en/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Dependencies with yield { #dependencies-with-yield }
FastAPI supports dependencies that do some extra steps after finishing.
To do this, use `yield` instead of `return`, and write the extra steps (code) after.
/// tip
Make sure to use `yield` one single time per dependency.
///
/// note | Technical Details
Any function that is valid to use with:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) or
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
would be valid to use as a **FastAPI** dependency.
In fact, FastAPI uses those two decorators internally.
///
## A database dependency with `yield` { #a-database-dependency-with-yield }
For example, you could use this to create a database session and close it after finishing.
Only the code prior to and including the `yield` statement is executed before creating a response:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
The yielded value is what is injected into *path operations* and other dependencies:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
The code following the `yield` statement is executed after the response:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip
You can use `async` or regular functions.
**FastAPI** will do the right thing with each, the same as with normal dependencies.
///
## A dependency with `yield` and `try` { #a-dependency-with-yield-and-try }
If you use a `try` block in a dependency with `yield`, you'll receive any exception that was thrown when using the dependency.
For example, if some code at some point in the middle, in another dependency or in a *path operation*, made a database transaction "rollback" or created any other exception, you would receive the exception in your dependency.
So, you can look for that specific exception inside the dependency with `except SomeException`.
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Sub-dependencies with `yield` { #sub-dependencies-with-yield }
You can have sub-dependencies and "trees" of sub-dependencies of any size and shape, and any or all of them can use `yield`.
**FastAPI** will make sure that the "exit code" in each dependency with `yield` is run in the correct order.
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
And all of them can use `yield`.
In this case `dependency_c`, to execute its exit code, needs the value from `dependency_b` (here named `dep_b`) to still be available.
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
The same way, you could have some dependencies with `yield` and some other dependencies with `return`, and have some of those depend on some of the others.
And you could have a single dependency that requires several other dependencies with `yield`, etc.
You can have any combinations of dependencies that you want.
**FastAPI** will make sure everything is run in the correct order.
/// note | Technical Details
This works thanks to Python's [Context Managers](https://docs.python.org/3/library/contextlib.html).
**FastAPI** uses them internally to achieve this.
///
## Dependencies with `yield` and `HTTPException` { #dependencies-with-yield-and-httpexception }
You saw that you can use dependencies with `yield` and have `try` blocks that try to execute some code and then run some exit code after `finally`.
You can also use `except` to catch the exception that was raised and do something with it.
For example, you can raise a different exception, like `HTTPException`.
/// tip
This is a somewhat advanced technique, and in most of the cases you won't really need it, as you can raise exceptions (including `HTTPException`) from inside of the rest of your application code, for example, in the *path operation function*.
But it's there for you if you need it. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
If you want to catch exceptions and create a custom response based on that, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers).
## Dependencies with `yield` and `except` { #dependencies-with-yield-and-except }
If you catch an exception using `except` in a dependency with `yield` and you don't raise it again (or raise a new exception), FastAPI won't be able to notice there was an exception, the same way that would happen with regular Python:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
In this case, the client will see an *HTTP 500 Internal Server Error* response as it should, given that we are not raising an `HTTPException` or similar, but the server will **not have any logs** or any other indication of what was the error. 😱
### Always `raise` in Dependencies with `yield` and `except` { #always-raise-in-dependencies-with-yield-and-except }
If you catch an exception in a dependency with `yield`, unless you are raising another `HTTPException` or similar, **you should re-raise the original exception**.
You can re-raise the same exception using `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Now the client will get the same *HTTP 500 Internal Server Error* response, but the server will have our custom `InternalError` in the logs. 😎
## Execution of dependencies with `yield` { #execution-of-dependencies-with-yield }
The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info
Only **one response** will be sent to the client. It might be one of the error responses or it will be the response from the *path operation*.
After one of those responses is sent, no other response can be sent.
///
/// tip
If you raise any exception in the code from the *path operation function*, it will be passed to the dependencies with yield, including `HTTPException`. In most cases you will want to re-raise that same exception or a new one from the dependency with `yield` to make sure it's properly handled.
///
## Early exit and `scope` { #early-exit-and-scope }
Normally the exit code of dependencies with `yield` is executed **after the response** is sent to the client.
But if you know that you won't need to use the dependency after returning from the *path operation function*, you can use `Depends(scope="function")` to tell FastAPI that it should close the dependency after the *path operation function* returns, but **before** the **response is sent**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` receives a `scope` parameter that can be:
* `"function"`: start the dependency before the *path operation function* that handles the request, end the dependency after the *path operation function* ends, but **before** the response is sent back to the client. So, the dependency function will be executed **around** the *path operation **function***.
* `"request"`: start the dependency before the *path operation function* that handles the request (similar to when using `"function"`), but end **after** the response is sent back to the client. So, the dependency function will be executed **around** the **request** and response cycle.
If not specified and the dependency has `yield`, it will have a `scope` of `"request"` by default.
### `scope` for sub-dependencies { #scope-for-sub-dependencies }
When you declare a dependency with a `scope="request"` (the default), any sub-dependency needs to also have a `scope` of `"request"`.
But a dependency with `scope` of `"function"` can have dependencies with `scope` of `"function"` and `scope` of `"request"`.
This is because any dependency needs to be able to run its exit code before the sub-dependencies, as it might need to still use them during its exit code.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## Dependencies with `yield`, `HTTPException`, `except` and Background Tasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
Dependencies with `yield` have evolved over time to cover different use cases and fix some issues.
If you want to see what has changed in different versions of FastAPI, you can read more about it in the advanced guide, in [Advanced Dependencies - Dependencies with `yield`, `HTTPException`, `except` and Background Tasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Context Managers { #context-managers }
### What are "Context Managers" { #what-are-context-managers }
"Context Managers" are any of those Python objects that you can use in a `with` statement.
For example, [you can use `with` to read a file](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Underneath, the `open("./somefile.txt")` creates an object that is called a "Context Manager".
When the `with` block finishes, it makes sure to close the file, even if there were exceptions.
When you create a dependency with `yield`, **FastAPI** will internally create a context manager for it, and combine it with some other related tools.
### Using context managers in dependencies with `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning
This is, more or less, an "advanced" idea.
If you are just starting with **FastAPI** you might want to skip it for now.
///
In Python, you can create Context Managers by [creating a class with two methods: `__enter__()` and `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
You can also use them inside of **FastAPI** dependencies with `yield` by using
`with` or `async with` statements inside of the dependency function:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip
Another way to create a context manager is with:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) or
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
using them to decorate a function with a single `yield`.
That's what **FastAPI** uses internally for dependencies with `yield`.
But you don't have to use the decorators for FastAPI dependencies (and you shouldn't).
FastAPI will do it for you internally.
///
================================================
FILE: docs/en/docs/tutorial/dependencies/global-dependencies.md
================================================
# Global Dependencies { #global-dependencies }
For some types of applications you might want to add dependencies to the whole application.
Similar to the way you can [add `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md), you can add them to the `FastAPI` application.
In that case, they will be applied to all the *path operations* in the application:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md) still apply, but in this case, to all of the *path operations* in the app.
## Dependencies for groups of *path operations* { #dependencies-for-groups-of-path-operations }
Later, when reading about how to structure bigger applications ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md)), possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.
================================================
FILE: docs/en/docs/tutorial/dependencies/index.md
================================================
# Dependencies { #dependencies }
**FastAPI** has a very powerful but intuitive **Dependency Injection** system.
It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with **FastAPI**.
## What is "Dependency Injection" { #what-is-dependency-injection }
**"Dependency Injection"** means, in programming, that there is a way for your code (in this case, your *path operation functions*) to declare things that it requires to work and use: "dependencies".
And then, that system (in this case **FastAPI**) will take care of doing whatever is needed to provide your code with those needed dependencies ("inject" the dependencies).
This is very useful when you need to:
* Have shared logic (the same code logic again and again).
* Share database connections.
* Enforce security, authentication, role requirements, etc.
* And many other things...
All these, while minimizing code repetition.
## First Steps { #first-steps }
Let's see a very simple example. It will be so simple that it is not very useful, for now.
But this way we can focus on how the **Dependency Injection** system works.
### Create a dependency, or "dependable" { #create-a-dependency-or-dependable }
Let's first focus on the dependency.
It is just a function that can take all the same parameters that a *path operation function* can take:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
That's it.
**2 lines**.
And it has the same shape and structure that all your *path operation functions* have.
You can think of it as a *path operation function* without the "decorator" (without the `@app.get("/some-path")`).
And it can return anything you want.
In this case, this dependency expects:
* An optional query parameter `q` that is a `str`.
* An optional query parameter `skip` that is an `int`, and by default is `0`.
* An optional query parameter `limit` that is an `int`, and by default is `100`.
And then it just returns a `dict` containing those values.
/// info
FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0.
If you have an older version, you would get errors when trying to use `Annotated`.
Make sure you [Upgrade the FastAPI version](../../deployment/versions.md#upgrading-the-fastapi-versions) to at least 0.95.1 before using `Annotated`.
///
### Import `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Declare the dependency, in the "dependant" { #declare-the-dependency-in-the-dependant }
The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Although you use `Depends` in the parameters of your function the same way you use `Body`, `Query`, etc, `Depends` works a bit differently.
You only give `Depends` a single parameter.
This parameter must be something like a function.
You **don't call it** directly (don't add the parenthesis at the end), you just pass it as a parameter to `Depends()`.
And that function takes parameters in the same way that *path operation functions* do.
/// tip
You'll see what other "things", apart from functions, can be used as dependencies in the next chapter.
///
Whenever a new request arrives, **FastAPI** will take care of:
* Calling your dependency ("dependable") function with the correct parameters.
* Get the result from your function.
* Assign that result to the parameter in your *path operation function*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
/// check
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
You just pass it to `Depends` and **FastAPI** knows how to do the rest.
///
## Share `Annotated` dependencies { #share-annotated-dependencies }
In the examples above, you see that there's a tiny bit of **code duplication**.
When you need to use the `common_parameters()` dependency, you have to write the whole parameter with the type annotation and `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
But because we are using `Annotated`, we can store that `Annotated` value in a variable and use it in multiple places:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip
This is just standard Python, it's called a "type alias", it's actually not specific to **FastAPI**.
But because **FastAPI** is based on the Python standards, including `Annotated`, you can use this trick in your code. 😎
///
The dependencies will keep working as expected, and the **best part** is that the **type information will be preserved**, which means that your editor will be able to keep providing you with **autocompletion**, **inline errors**, etc. The same for other tools like `mypy`.
This will be especially useful when you use it in a **large code base** where you use **the same dependencies** over and over again in **many *path operations***.
## To `async` or not to `async` { #to-async-or-not-to-async }
As dependencies will also be called by **FastAPI** (the same as your *path operation functions*), the same rules apply while defining your functions.
You can use `async def` or normal `def`.
And you can declare dependencies with `async def` inside of normal `def` *path operation functions*, or `def` dependencies inside of `async def` *path operation functions*, etc.
It doesn't matter. **FastAPI** will know what to do.
/// note
If you don't know, check the [Async: *"In a hurry?"*](../../async.md#in-a-hurry) section about `async` and `await` in the docs.
///
## Integrated with OpenAPI { #integrated-with-openapi }
All the request declarations, validations and requirements of your dependencies (and sub-dependencies) will be integrated in the same OpenAPI schema.
So, the interactive docs will have all the information from these dependencies too:
## Simple usage { #simple-usage }
If you look at it, *path operation functions* are declared to be used whenever a *path* and *operation* matches, and then **FastAPI** takes care of calling the function with the correct parameters, extracting the data from the request.
Actually, all (or most) of the web frameworks work in this same way.
You never call those functions directly. They are called by your framework (in this case, **FastAPI**).
With the Dependency Injection system, you can also tell **FastAPI** that your *path operation function* also "depends" on something else that should be executed before your *path operation function*, and **FastAPI** will take care of executing it and "injecting" the results.
Other common terms for this same idea of "dependency injection" are:
* resources
* providers
* services
* injectables
* components
## **FastAPI** plug-ins { #fastapi-plug-ins }
Integrations and "plug-ins" can be built using the **Dependency Injection** system. But in fact, there is actually **no need to create "plug-ins"**, as by using dependencies it's possible to declare an infinite number of integrations and interactions that become available to your *path operation functions*.
And dependencies can be created in a very simple and intuitive way that allows you to just import the Python packages you need, and integrate them with your API functions in a couple of lines of code, *literally*.
You will see examples of this in the next chapters, about relational and NoSQL databases, security, etc.
## **FastAPI** compatibility { #fastapi-compatibility }
The simplicity of the dependency injection system makes **FastAPI** compatible with:
* all the relational databases
* NoSQL databases
* external packages
* external APIs
* authentication and authorization systems
* API usage monitoring systems
* response data injection systems
* etc.
## Simple and Powerful { #simple-and-powerful }
Although the hierarchical dependency injection system is very simple to define and use, it's still very powerful.
You can define dependencies that in turn can define dependencies themselves.
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
For example, let's say you have 4 API endpoints (*path operations*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integrated with **OpenAPI** { #integrated-with-openapi_1 }
All these dependencies, while declaring their requirements, also add parameters, validations, etc. to your *path operations*.
**FastAPI** will take care of adding it all to the OpenAPI schema, so that it is shown in the interactive documentation systems.
================================================
FILE: docs/en/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Sub-dependencies { #sub-dependencies }
You can create dependencies that have **sub-dependencies**.
They can be as **deep** as you need them to be.
**FastAPI** will take care of solving them.
## First dependency "dependable" { #first-dependency-dependable }
You could create a first dependency ("dependable") like:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
It declares an optional query parameter `q` as a `str`, and then it just returns it.
This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.
## Second dependency, "dependable" and "dependant" { #second-dependency-dependable-and-dependant }
Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Let's focus on the parameters declared:
* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else).
* It depends on the `query_extractor`, and assigns the value returned by it to the parameter `q`.
* It also declares an optional `last_query` cookie, as a `str`.
* If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before.
## Use the dependency { #use-the-dependency }
Then we can use the dependency with:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info
Notice that we are only declaring one dependency in the *path operation function*, the `query_or_cookie_extractor`.
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Using the same dependency multiple times { #using-the-same-dependency-multiple-times }
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
And it will save the returned value in a "cache" and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request.
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip
Prefer to use the `Annotated` version if possible.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Recap { #recap }
Apart from all the fancy words used here, the **Dependency Injection** system is quite simple.
Just functions that look the same as the *path operation functions*.
But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency "graphs" (trees).
/// tip
All this might not seem as useful with these simple examples.
But you will see how useful it is in the chapters about **security**.
And you will also see the amounts of code it will save you.
///
================================================
FILE: docs/en/docs/tutorial/encoder.md
================================================
# JSON Compatible Encoder { #json-compatible-encoder }
There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON (like a `dict`, `list`, etc).
For example, if you need to store it in a database.
For that, **FastAPI** provides a `jsonable_encoder()` function.
## Using the `jsonable_encoder` { #using-the-jsonable-encoder }
Let's imagine that you have a database `fake_db` that only receives JSON compatible data.
For example, it doesn't receive `datetime` objects, as those are not compatible with JSON.
So, a `datetime` object would have to be converted to a `str` containing the data in [ISO format](https://en.wikipedia.org/wiki/ISO_8601).
The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a `dict`.
You can use `jsonable_encoder` for that.
It receives an object, like a Pydantic model, and returns a JSON compatible version:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
The result of calling it is something that can be encoded with the Python standard [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
It doesn't return a large `str` containing the data in JSON format (as a string). It returns a Python standard data structure (e.g. a `dict`) with values and sub-values that are all compatible with JSON.
/// note
`jsonable_encoder` is actually used by **FastAPI** internally to convert data. But it is useful in many other scenarios.
///
================================================
FILE: docs/en/docs/tutorial/extra-data-types.md
================================================
# Extra Data Types { #extra-data-types }
Up to now, you have been using common data types, like:
* `int`
* `float`
* `str`
* `bool`
But you can also use more complex data types.
And you will still have the same features as seen up to now:
* Great editor support.
* Data conversion from incoming requests.
* Data conversion for response data.
* Data validation.
* Automatic annotation and documentation.
## Other data types { #other-data-types }
Here are some of the additional data types you can use:
* `UUID`:
* A standard "Universally Unique Identifier", common as an ID in many databases and systems.
* In requests and responses will be represented as a `str`.
* `datetime.datetime`:
* A Python `datetime.datetime`.
* In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Python `datetime.date`.
* In requests and responses will be represented as a `str` in ISO 8601 format, like: `2008-09-15`.
* `datetime.time`:
* A Python `datetime.time`.
* In requests and responses will be represented as a `str` in ISO 8601 format, like: `14:23:55.003`.
* `datetime.timedelta`:
* A Python `datetime.timedelta`.
* In requests and responses will be represented as a `float` of total seconds.
* Pydantic also allows representing it as a "ISO 8601 time diff encoding", [see the docs for more info](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* In requests and responses, treated the same as a `set`:
* In requests, a list will be read, eliminating duplicates and converting it to a `set`.
* In responses, the `set` will be converted to a `list`.
* The generated schema will specify that the `set` values are unique (using JSON Schema's `uniqueItems`).
* `bytes`:
* Standard Python `bytes`.
* In requests and responses will be treated as `str`.
* The generated schema will specify that it's a `str` with `binary` "format".
* `Decimal`:
* Standard Python `Decimal`.
* In requests and responses, handled the same as a `float`.
* You can check all the valid Pydantic data types here: [Pydantic data types](https://docs.pydantic.dev/latest/usage/types/types/).
## Example { #example }
Here's an example *path operation* with parameters using some of the above types.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/en/docs/tutorial/extra-models.md
================================================
# Extra Models { #extra-models }
Continuing with the previous example, it will be common to have more than one related model.
This is especially the case for user models, because:
* The **input model** needs to be able to have a password.
* The **output model** should not have a password.
* The **database model** would probably need to have a hashed password.
/// danger
Never store user's plaintext passwords. Always store a "secure hash" that you can then verify.
If you don't know, you will learn what a "password hash" is in the [security chapters](security/simple-oauth2.md#password-hashing).
///
## Multiple models { #multiple-models }
Here's a general idea of how the models could look like with their password fields and the places where they are used:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### About `**user_in.model_dump()` { #about-user-in-model-dump }
#### Pydantic's `.model_dump()` { #pydantics-model-dump }
`user_in` is a Pydantic model of class `UserIn`.
Pydantic models have a `.model_dump()` method that returns a `dict` with the model's data.
So, if we create a Pydantic object `user_in` like:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
and then we call:
```Python
user_dict = user_in.model_dump()
```
we now have a `dict` with the data in the variable `user_dict` (it's a `dict` instead of a Pydantic model object).
And if we call:
```Python
print(user_dict)
```
we would get a Python `dict` with:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Unpacking a `dict` { #unpacking-a-dict }
If we take a `dict` like `user_dict` and pass it to a function (or class) with `**user_dict`, Python will "unpack" it. It will pass the keys and values of the `user_dict` directly as key-value arguments.
So, continuing with the `user_dict` from above, writing:
```Python
UserInDB(**user_dict)
```
would result in something equivalent to:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Or more exactly, using `user_dict` directly, with whatever contents it might have in the future:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### A Pydantic model from the contents of another { #a-pydantic-model-from-the-contents-of-another }
As in the example above we got `user_dict` from `user_in.model_dump()`, this code:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
would be equivalent to:
```Python
UserInDB(**user_in.model_dump())
```
...because `user_in.model_dump()` is a `dict`, and then we make Python "unpack" it by passing it to `UserInDB` prefixed with `**`.
So, we get a Pydantic model from the data in another Pydantic model.
#### Unpacking a `dict` and extra keywords { #unpacking-a-dict-and-extra-keywords }
And then adding the extra keyword argument `hashed_password=hashed_password`, like in:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...ends up being like:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning
The supporting additional functions `fake_password_hasher` and `fake_save_user` are just to demo a possible flow of the data, but they of course are not providing any real security.
///
## Reduce duplication { #reduce-duplication }
Reducing code duplication is one of the core ideas in **FastAPI**.
As code duplication increments the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc.
And these models are all sharing a lot of the data and duplicating attribute names and types.
We could do better.
We can declare a `UserBase` model that serves as a base for our other models. And then we can make subclasses of that model that inherit its attributes (type declarations, validation, etc).
All the data conversion, validation, documentation, etc. will still work as normally.
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` or `anyOf` { #union-or-anyof }
You can declare a response to be the `Union` of two or more types, that means, that the response would be any of them.
It will be defined in OpenAPI with `anyOf`.
To do that, use the standard Python type hint [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note
When defining a [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions), include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` in Python 3.10 { #union-in-python-3-10 }
In this example we pass `Union[PlaneItem, CarItem]` as the value of the argument `response_model`.
Because we are passing it as a **value to an argument** instead of putting it in a **type annotation**, we have to use `Union` even in Python 3.10.
If it was in a type annotation we could have used the vertical bar, as:
```Python
some_variable: PlaneItem | CarItem
```
But if we put that in the assignment `response_model=PlaneItem | CarItem` we would get an error, because Python would try to perform an **invalid operation** between `PlaneItem` and `CarItem` instead of interpreting that as a type annotation.
## List of models { #list-of-models }
The same way, you can declare responses of lists of objects.
For that, use the standard Python `list`:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Response with arbitrary `dict` { #response-with-arbitrary-dict }
You can also declare a response using a plain arbitrary `dict`, declaring just the type of the keys and values, without using a Pydantic model.
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
In this case, you can use `dict`:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Recap { #recap }
Use multiple Pydantic models and inherit freely for each case.
You don't need to have a single data model per entity if that entity must be able to have different "states". As the case with the user "entity" with a state including `password`, `password_hash` and no password.
================================================
FILE: docs/en/docs/tutorial/first-steps.md
================================================
# First Steps { #first-steps }
The simplest FastAPI file could look like this:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copy that to a file `main.py`.
Run the live server:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
In the output, there's a line with something like:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
That line shows the URL where your app is being served on your local machine.
### Check it { #check-it }
Open your browser at [http://127.0.0.1:8000](http://127.0.0.1:8000).
You will see the JSON response as:
```JSON
{"message": "Hello World"}
```
### Interactive API docs { #interactive-api-docs }
Now go to [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see the automatic interactive API documentation (provided by [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Alternative API docs { #alternative-api-docs }
And now, go to [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
You will see the alternative automatic documentation (provided by [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
**FastAPI** generates a "schema" with all your API using the **OpenAPI** standard for defining APIs.
#### "Schema" { #schema }
A "schema" is a definition or description of something. Not the code that implements it, but just an abstract description.
#### API "schema" { #api-schema }
In this case, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) is a specification that dictates how to define a schema of your API.
This schema definition includes your API paths, the possible parameters they take, etc.
#### Data "schema" { #data-schema }
The term "schema" might also refer to the shape of some data, like a JSON content.
In that case, it would mean the JSON attributes, and data types they have, etc.
#### OpenAPI and JSON Schema { #openapi-and-json-schema }
OpenAPI defines an API schema for your API. And that schema includes definitions (or "schemas") of the data sent and received by your API using **JSON Schema**, the standard for JSON data schemas.
#### Check the `openapi.json` { #check-the-openapi-json }
If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON (schema) with the descriptions of all your API.
You can see it directly at: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
It will show a JSON starting with something like:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### What is OpenAPI for { #what-is-openapi-for }
The OpenAPI schema is what powers the two interactive documentation systems included.
And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives to your application built with **FastAPI**.
You could also use it to generate code automatically, for clients that communicate with your API. For example, frontend, mobile or IoT applications.
### Configure the app `entrypoint` in `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
You can configure where your app is located in a `pyproject.toml` file like:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
That `entrypoint` will tell the `fastapi` command that it should import the app like:
```python
from main import app
```
If your code was structured like:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Then you would set the `entrypoint` as:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
which would be equivalent to:
```python
from backend.main import app
```
### `fastapi dev` with path { #fastapi-dev-with-path }
You can also pass the file path to the `fastapi dev` command, and it will guess the FastAPI app object to use:
```console
$ fastapi dev main.py
```
But you would have to remember to pass the correct path every time you call the `fastapi` command.
Additionally, other tools might not be able to find it, for example the [VS Code Extension](../editor-support.md) or [FastAPI Cloud](https://fastapicloud.com), so it is recommended to use the `entrypoint` in `pyproject.toml`.
### Deploy your app (optional) { #deploy-your-app-optional }
You can optionally deploy your FastAPI app to [FastAPI Cloud](https://fastapicloud.com), go and join the waiting list if you haven't. 🚀
If you already have a **FastAPI Cloud** account (we invited you from the waiting list 😉), you can deploy your application with one command.
Before deploying, make sure you are logged in:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Then deploy your app:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
That's it! Now you can access your app at that URL. ✨
## Recap, step by step { #recap-step-by-step }
### Step 1: import `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` is a Python class that provides all the functionality for your API.
/// note | Technical Details
`FastAPI` is a class that inherits directly from `Starlette`.
You can use all the [Starlette](https://www.starlette.dev/) functionality with `FastAPI` too.
///
### Step 2: create a `FastAPI` "instance" { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Here the `app` variable will be an "instance" of the class `FastAPI`.
This will be the main point of interaction to create all your API.
### Step 3: create a *path operation* { #step-3-create-a-path-operation }
#### Path { #path }
"Path" here refers to the last part of the URL starting from the first `/`.
So, in a URL like:
```
https://example.com/items/foo
```
...the path would be:
```
/items/foo
```
/// info
A "path" is also commonly called an "endpoint" or a "route".
///
While building an API, the "path" is the main way to separate "concerns" and "resources".
#### Operation { #operation }
"Operation" here refers to one of the HTTP "methods".
One of:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...and the more exotic ones:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
In the HTTP protocol, you can communicate to each path using one (or more) of these "methods".
---
When building APIs, you normally use these specific HTTP methods to perform a specific action.
Normally you use:
* `POST`: to create data.
* `GET`: to read data.
* `PUT`: to update data.
* `DELETE`: to delete data.
So, in OpenAPI, each of the HTTP methods is called an "operation".
We are going to call them "**operations**" too.
#### Define a *path operation decorator* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
* the path `/`
* using a get operation
/// info | `@decorator` Info
That `@something` syntax in Python is called a "decorator".
You put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from).
A "decorator" takes the function below and does something with it.
In our case, this decorator tells **FastAPI** that the function below corresponds to the **path** `/` with an **operation** `get`.
It is the "**path operation decorator**".
///
You can also use the other operations:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
And the more exotic ones:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip
You are free to use each operation (HTTP method) as you wish.
**FastAPI** doesn't enforce any specific meaning.
The information here is presented as a guideline, not a requirement.
For example, when using GraphQL you normally perform all the actions using only `POST` operations.
///
### Step 4: define the **path operation function** { #step-4-define-the-path-operation-function }
This is our "**path operation function**":
* **path**: is `/`.
* **operation**: is `get`.
* **function**: is the function below the "decorator" (below `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
This is a Python function.
It will be called by **FastAPI** whenever it receives a request to the URL "`/`" using a `GET` operation.
In this case, it is an `async` function.
---
You could also define it as a normal function instead of `async def`:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note
If you don't know the difference, check the [Async: *"In a hurry?"*](../async.md#in-a-hurry).
///
### Step 5: return the content { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
You can return a `dict`, `list`, singular values as `str`, `int`, etc.
You can also return Pydantic models (you'll see more about that later).
There are many other objects and models that will be automatically converted to JSON (including ORMs, etc). Try using your favorite ones, it's highly probable that they are already supported.
### Step 6: Deploy it { #step-6-deploy-it }
Deploy your app to **[FastAPI Cloud](https://fastapicloud.com)** with one command: `fastapi deploy`. 🎉
#### About FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** is built by the same author and team behind **FastAPI**.
It streamlines the process of **building**, **deploying**, and **accessing** an API with minimal effort.
It brings the same **developer experience** of building apps with FastAPI to **deploying** them to the cloud. 🎉
FastAPI Cloud is the primary sponsor and funding provider for the *FastAPI and friends* open source projects. ✨
#### Deploy to other cloud providers { #deploy-to-other-cloud-providers }
FastAPI is open source and based on standards. You can deploy FastAPI apps to any cloud provider you choose.
Follow your cloud provider's guides to deploy FastAPI apps with them. 🤓
## Recap { #recap }
* Import `FastAPI`.
* Create an `app` instance.
* Write a **path operation decorator** using decorators like `@app.get("/")`.
* Define a **path operation function**; for example, `def root(): ...`.
* Run the development server using the command `fastapi dev`.
* Optionally deploy your app with `fastapi deploy`.
================================================
FILE: docs/en/docs/tutorial/handling-errors.md
================================================
# Handling Errors { #handling-errors }
There are many situations in which you need to notify an error to a client that is using your API.
This client could be a browser with a frontend, a code from someone else, an IoT device, etc.
You could need to tell the client that:
* The client doesn't have enough privileges for that operation.
* The client doesn't have access to that resource.
* The item the client was trying to access doesn't exist.
* etc.
In these cases, you would normally return an **HTTP status code** in the range of **400** (from 400 to 499).
This is similar to the 200 HTTP status codes (from 200 to 299). Those "200" status codes mean that somehow there was a "success" in the request.
The status codes in the 400 range mean that there was an error from the client.
Remember all those **"404 Not Found"** errors (and jokes)?
## Use `HTTPException` { #use-httpexception }
To return HTTP responses with errors to the client you use `HTTPException`.
### Import `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Raise an `HTTPException` in your code { #raise-an-httpexception-in-your-code }
`HTTPException` is a normal Python exception with additional data relevant for APIs.
Because it's a Python exception, you don't `return` it, you `raise` it.
This also means that if you are inside a utility function that you are calling inside of your *path operation function*, and you raise the `HTTPException` from inside of that utility function, it won't run the rest of the code in the *path operation function*, it will terminate that request right away and send the HTTP error from the `HTTPException` to the client.
The benefit of raising an exception over returning a value will be more evident in the section about Dependencies and Security.
In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### The resulting response { #the-resulting-response }
If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), that client will receive an HTTP status code of 200, and a JSON response of:
```JSON
{
"item": "The Foo Wrestlers"
}
```
But if the client requests `http://example.com/items/bar` (a non-existent `item_id` `"bar"`), that client will receive an HTTP status code of 404 (the "not found" error), and a JSON response of:
```JSON
{
"detail": "Item not found"
}
```
/// tip
When raising an `HTTPException`, you can pass any value that can be converted to JSON as the parameter `detail`, not only `str`.
You could pass a `dict`, a `list`, etc.
They are handled automatically by **FastAPI** and converted to JSON.
///
## Add custom headers { #add-custom-headers }
There are some situations in where it's useful to be able to add custom headers to the HTTP error. For example, for some types of security.
You probably won't need to use it directly in your code.
But in case you needed it for an advanced scenario, you can add custom headers:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Install custom exception handlers { #install-custom-exception-handlers }
You can add custom exception handlers with [the same exception utilities from Starlette](https://www.starlette.dev/exceptions/).
Let's say you have a custom exception `UnicornException` that you (or a library you use) might `raise`.
And you want to handle this exception globally with FastAPI.
You could add a custom exception handler with `@app.exception_handler()`:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`.
But it will be handled by the `unicorn_exception_handler`.
So, you will receive a clean error, with an HTTP status code of `418` and a JSON content of:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Technical Details
You could also use `from starlette.requests import Request` and `from starlette.responses import JSONResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request`.
///
## Override the default exception handlers { #override-the-default-exception-handlers }
**FastAPI** has some default exception handlers.
These handlers are in charge of returning the default JSON responses when you `raise` an `HTTPException` and when the request has invalid data.
You can override these exception handlers with your own.
### Override request validation exceptions { #override-request-validation-exceptions }
When a request contains invalid data, **FastAPI** internally raises a `RequestValidationError`.
And it also includes a default exception handler for it.
To override it, import the `RequestValidationError` and use it with `@app.exception_handler(RequestValidationError)` to decorate the exception handler.
The exception handler will receive a `Request` and the exception.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Now, if you go to `/items/foo`, instead of getting the default JSON error with:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
you will get a text version, with:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Override the `HTTPException` error handler { #override-the-httpexception-error-handler }
The same way, you can override the `HTTPException` handler.
For example, you could want to return a plain text response instead of JSON for these errors:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Technical Details
You could also use `from starlette.responses import PlainTextResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
///
/// warning
Have in mind that the `RequestValidationError` contains the information of the file name and line where the validation error happens so that you can show it in your logs with the relevant information if you want to.
But that means that if you just convert it to a string and return that information directly, you could be leaking a bit of information about your system, that's why here the code extracts and shows each error independently.
///
### Use the `RequestValidationError` body { #use-the-requestvalidationerror-body }
The `RequestValidationError` contains the `body` it received with invalid data.
You could use it while developing your app to log the body and debug it, return it to the user, etc.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Now try sending an invalid item like:
```JSON
{
"title": "towel",
"size": "XL"
}
```
You will receive a response telling you that the data is invalid containing the received body:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### FastAPI's `HTTPException` vs Starlette's `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** has its own `HTTPException`.
And **FastAPI**'s `HTTPException` error class inherits from Starlette's `HTTPException` error class.
The only difference is that **FastAPI**'s `HTTPException` accepts any JSON-able data for the `detail` field, while Starlette's `HTTPException` only accepts strings for it.
So, you can keep raising **FastAPI**'s `HTTPException` as normally in your code.
But when you register an exception handler, you should register it for Starlette's `HTTPException`.
This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette `HTTPException`, your handler will be able to catch and handle it.
In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Reuse **FastAPI**'s exception handlers { #reuse-fastapis-exception-handlers }
If you want to use the exception along with the same default exception handlers from **FastAPI**, you can import and reuse the default exception handlers from `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
In this example you are just printing the error with a very expressive message, but you get the idea. You can use the exception and then just reuse the default exception handlers.
================================================
FILE: docs/en/docs/tutorial/header-param-models.md
================================================
# Header Parameter Models { #header-parameter-models }
If you have a group of related **header parameters**, you can create a **Pydantic model** to declare them.
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
/// note
This is supported since FastAPI version `0.115.0`. 🤓
///
## Header Parameters with a Pydantic Model { #header-parameters-with-a-pydantic-model }
Declare the **header parameters** that you need in a **Pydantic model**, and then declare the parameter as `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** will **extract** the data for **each field** from the **headers** in the request and give you the Pydantic model you defined.
## Check the Docs { #check-the-docs }
You can see the required headers in the docs UI at `/docs`:
## Forbid Extra Headers { #forbid-extra-headers }
In some special use cases (probably not very common), you might want to **restrict** the headers that you want to receive.
You can use Pydantic's model configuration to `forbid` any `extra` fields:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
If a client tries to send some **extra headers**, they will receive an **error** response.
For example, if the client tries to send a `tool` header with a value of `plumbus`, they will receive an **error** response telling them that the header parameter `tool` is not allowed:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Disable Convert Underscores { #disable-convert-underscores }
The same way as with regular header parameters, when you have underscore characters in the parameter names, they are **automatically converted to hyphens**.
For example, if you have a header parameter `save_data` in the code, the expected HTTP header will be `save-data`, and it will show up like that in the docs.
If for some reason you need to disable this automatic conversion, you can do it as well for Pydantic models for header parameters.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
///
## Summary { #summary }
You can use **Pydantic models** to declare **headers** in **FastAPI**. 😎
================================================
FILE: docs/en/docs/tutorial/header-params.md
================================================
# Header Parameters { #header-parameters }
You can define Header parameters the same way you define `Query`, `Path` and `Cookie` parameters.
## Import `Header` { #import-header }
First import `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Declare `Header` parameters { #declare-header-parameters }
Then declare the header parameters using the same structure as with `Path`, `Query` and `Cookie`.
You can define the default value as well as all the extra validation or annotation parameters:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Technical Details
`Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class.
But remember that when you import `Query`, `Path`, `Header`, and others from `fastapi`, those are actually functions that return special classes.
///
/// info
To declare headers, you need to use `Header`, because otherwise the parameters would be interpreted as query parameters.
///
## Automatic conversion { #automatic-conversion }
`Header` has a little extra functionality on top of what `Path`, `Query` and `Cookie` provide.
Most of the standard headers are separated by a "hyphen" character, also known as the "minus symbol" (`-`).
But a variable like `user-agent` is invalid in Python.
So, by default, `Header` will convert the parameter names characters from underscore (`_`) to hyphen (`-`) to extract and document the headers.
Also, HTTP headers are case-insensitive, so, you can declare them with standard Python style (also known as "snake_case").
So, you can use `user_agent` as you normally would in Python code, instead of needing to capitalize the first letters as `User_Agent` or something similar.
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
///
## Duplicate headers { #duplicate-headers }
It is possible to receive duplicate headers. That means, the same header with multiple values.
You can define those cases using a list in the type declaration.
You will receive all the values from the duplicate header as a Python `list`.
For example, to declare a header of `X-Token` that can appear more than once, you can write:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
If you communicate with that *path operation* sending two HTTP headers like:
```
X-Token: foo
X-Token: bar
```
The response would be like:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Recap { #recap }
Declare headers with `Header`, using the same common pattern as `Query`, `Path` and `Cookie`.
And don't worry about underscores in your variables, **FastAPI** will take care of converting them.
================================================
FILE: docs/en/docs/tutorial/index.md
================================================
# Tutorial - User Guide { #tutorial-user-guide }
This tutorial shows you how to use **FastAPI** with most of its features, step by step.
Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific API needs.
It is also built to work as a future reference so you can come back and see exactly what you need.
## Run the code { #run-the-code }
All the code blocks can be copied and used directly (they are actually tested Python files).
To run any of the examples, copy the code to a file `main.py`, and start `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally.
Using it in your editor is what really shows you the benefits of FastAPI, seeing how little code you have to write, all the type checks, autocompletion, etc.
---
## Install FastAPI { #install-fastapi }
The first step is to install FastAPI.
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then **install FastAPI**:
/// note
When you install with `pip install "fastapi[standard]"` it comes with some default optional standard dependencies, including `fastapi-cloud-cli`, which allows you to deploy to [FastAPI Cloud](https://fastapicloud.com).
If you don't want to have those optional dependencies, you can instead install `pip install fastapi`.
If you want to install the standard dependencies but without the `fastapi-cloud-cli`, you can install with `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip
FastAPI has an [official extension for VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (and Cursor), which provides a lot of features, including a path operation explorer, path operation search, CodeLens navigation in tests (jump to definition from tests), and FastAPI Cloud deployment and logs, all from your editor.
///
## Advanced User Guide { #advanced-user-guide }
There is also an **Advanced User Guide** that you can read later after this **Tutorial - User guide**.
The **Advanced User Guide** builds on this one, uses the same concepts, and teaches you some extra features.
But you should first read the **Tutorial - User Guide** (what you are reading right now).
It's designed so that you can build a complete application with just the **Tutorial - User Guide**, and then extend it in different ways, depending on your needs, using some of the additional ideas from the **Advanced User Guide**.
================================================
FILE: docs/en/docs/tutorial/metadata.md
================================================
# Metadata and Docs URLs { #metadata-and-docs-urls }
You can customize several metadata configurations in your **FastAPI** application.
## Metadata for API { #metadata-for-api }
You can set the following fields that are used in the OpenAPI specification and the automatic API docs UIs:
| Parameter | Type | Description |
|------------|------|-------------|
| `title` | `str` | The title of the API. |
| `summary` | `str` | A short summary of the API. Available since OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | A short description of the API. It can use Markdown. |
| `version` | `string` | The version of the API. This is the version of your own application, not of OpenAPI. For example `2.5.0`. |
| `terms_of_service` | `str` | A URL to the Terms of Service for the API. If provided, this has to be a URL. |
| `contact` | `dict` | The contact information for the exposed API. It can contain several fields. contact fields
Parameter
Type
Description
name
str
The identifying name of the contact person/organization.
url
str
The URL pointing to the contact information. MUST be in the format of a URL.
email
str
The email address of the contact person/organization. MUST be in the format of an email address.
|
| `license_info` | `dict` | The license information for the exposed API. It can contain several fields. license_info fields
Parameter
Type
Description
name
str
REQUIRED (if a license_info is set). The license name used for the API.
identifier
str
An [SPDX](https://spdx.org/licenses/) license expression for the API. The identifier field is mutually exclusive of the url field. Available since OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
A URL to the license used for the API. MUST be in the format of a URL.
|
You can set them as follows:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip
You can write Markdown in the `description` field and it will be rendered in the output.
///
With this configuration, the automatic API docs would look like:
## License identifier { #license-identifier }
Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the `license_info` with an `identifier` instead of a `url`.
For example:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Metadata for tags { #metadata-for-tags }
You can also add additional metadata for the different tags used to group your path operations with the parameter `openapi_tags`.
It takes a list containing one dictionary for each tag.
Each dictionary can contain:
* `name` (**required**): a `str` with the same tag name you use in the `tags` parameter in your *path operations* and `APIRouter`s.
* `description`: a `str` with a short description for the tag. It can have Markdown and will be shown in the docs UI.
* `externalDocs`: a `dict` describing external documentation with:
* `description`: a `str` with a short description for the external docs.
* `url` (**required**): a `str` with the URL for the external documentation.
### Create metadata for tags { #create-metadata-for-tags }
Let's try that in an example with tags for `users` and `items`.
Create metadata for your tags and pass it to the `openapi_tags` parameter:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
/// tip
You don't have to add metadata for all the tags that you use.
///
### Use your tags { #use-your-tags }
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info
Read more about tags in [Path Operation Configuration](path-operation-configuration.md#tags).
///
### Check the docs { #check-the-docs }
Now, if you check the docs, they will show all the additional metadata:
### Order of tags { #order-of-tags }
The order of each tag metadata dictionary also defines the order shown in the docs UI.
For example, even though `users` would go after `items` in alphabetical order, it is shown before them, because we added their metadata as the first dictionary in the list.
## OpenAPI URL { #openapi-url }
By default, the OpenAPI schema is served at `/openapi.json`.
But you can configure it with the parameter `openapi_url`.
For example, to set it to be served at `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it.
## Docs URLs { #docs-urls }
You can configure the two documentation user interfaces included:
* **Swagger UI**: served at `/docs`.
* You can set its URL with the parameter `docs_url`.
* You can disable it by setting `docs_url=None`.
* **ReDoc**: served at `/redoc`.
* You can set its URL with the parameter `redoc_url`.
* You can disable it by setting `redoc_url=None`.
For example, to set Swagger UI to be served at `/documentation` and disable ReDoc:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/en/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
You can add middleware to **FastAPI** applications.
A "middleware" is a function that works with every **request** before it is processed by any specific *path operation*. And also with every **response** before returning it.
* It takes each **request** that comes to your application.
* It can then do something to that **request** or run any needed code.
* Then it passes the **request** to be processed by the rest of the application (by some *path operation*).
* It then takes the **response** generated by the application (by some *path operation*).
* It can do something to that **response** or run any needed code.
* Then it returns the **response**.
/// note | Technical Details
If you have dependencies with `yield`, the exit code will run *after* the middleware.
If there were any background tasks (covered in the [Background Tasks](background-tasks.md) section, you will see it later), they will run *after* all the middleware.
///
## Create a middleware { #create-a-middleware }
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
The middleware function receives:
* The `request`.
* A function `call_next` that will receive the `request` as a parameter.
* This function will pass the `request` to the corresponding *path operation*.
* Then it returns the `response` generated by the corresponding *path operation*.
* You can then further modify the `response` before returning it.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip
Keep in mind that custom proprietary headers can be added [using the `X-` prefix](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md)) using the parameter `expose_headers` documented in [Starlette's CORS docs](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Technical Details
You could also use `from starlette.requests import Request`.
**FastAPI** provides it as a convenience for you, the developer. But it comes directly from Starlette.
///
### Before and after the `response` { #before-and-after-the-response }
You can add code to be run with the `request`, before any *path operation* receives it.
And also after the `response` is generated, before returning it.
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip
Here we use [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) instead of `time.time()` because it can be more precise for these use cases. 🤓
///
## Multiple middleware execution order { #multiple-middleware-execution-order }
When you add multiple middlewares using either `@app.middleware()` decorator or `app.add_middleware()` method, each new middleware wraps the application, forming a stack. The last middleware added is the *outermost*, and the first is the *innermost*.
On the request path, the *outermost* middleware runs first.
On the response path, it runs last.
For example:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
This results in the following execution order:
* **Request**: MiddlewareB → MiddlewareA → route
* **Response**: route → MiddlewareA → MiddlewareB
This stacking behavior ensures that middlewares are executed in a predictable and controllable order.
## Other middlewares { #other-middlewares }
You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md).
You will read about how to handle CORS with a middleware in the next section.
================================================
FILE: docs/en/docs/tutorial/path-operation-configuration.md
================================================
# Path Operation Configuration { #path-operation-configuration }
There are several parameters that you can pass to your *path operation decorator* to configure it.
/// warning
Notice that these parameters are passed directly to the *path operation decorator*, not to your *path operation function*.
///
## Response Status Code { #response-status-code }
You can define the (HTTP) `status_code` to be used in the response of your *path operation*.
You can pass directly the `int` code, like `404`.
But if you don't remember what each number code is for, you can use the shortcut constants in `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
That status code will be used in the response and will be added to the OpenAPI schema.
/// note | Technical Details
You could also use `from starlette import status`.
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
///
## Tags { #tags }
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
### Tags with Enums { #tags-with-enums }
If you have a big application, you might end up accumulating **several tags**, and you would want to make sure you always use the **same tag** for related *path operations*.
In these cases, it could make sense to store the tags in an `Enum`.
**FastAPI** supports that the same way as with plain strings:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Summary and description { #summary-and-description }
You can add a `summary` and `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Description from docstring { #description-from-docstring }
As descriptions tend to be long and cover multiple lines, you can declare the *path operation* description in the function docstring and **FastAPI** will read it from there.
You can write [Markdown](https://en.wikipedia.org/wiki/Markdown) in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
It will be used in the interactive docs:
## Response description { #response-description }
You can specify the response description with the parameter `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info
Notice that `response_description` refers specifically to the response, the `description` refers to the *path operation* in general.
///
/// check
OpenAPI specifies that each *path operation* requires a response description.
So, if you don't provide one, **FastAPI** will automatically generate one of "Successful response".
///
## Deprecate a *path operation* { #deprecate-a-path-operation }
If you need to mark a *path operation* as deprecated, but without removing it, pass the parameter `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
It will be clearly marked as deprecated in the interactive docs:
Check how deprecated and non-deprecated *path operations* look like:
## Recap { #recap }
You can configure and add metadata for your *path operations* easily by passing parameters to the *path operation decorators*.
================================================
FILE: docs/en/docs/tutorial/path-params-numeric-validations.md
================================================
# Path Parameters and Numeric Validations { #path-parameters-and-numeric-validations }
In the same way that you can declare more validations and metadata for query parameters with `Query`, you can declare the same type of validations and metadata for path parameters with `Path`.
## Import `Path` { #import-path }
First, import `Path` from `fastapi`, and import `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info
FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0.
If you have an older version, you would get errors when trying to use `Annotated`.
Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions) to at least 0.95.1 before using `Annotated`.
///
## Declare metadata { #declare-metadata }
You can declare all the same parameters as for `Query`.
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note
A path parameter is always required as it has to be part of the path. Even if you declared it with `None` or set a default value, it would not affect anything, it would still be always required.
///
## Order the parameters as you need { #order-the-parameters-as-you-need }
/// tip
This is probably not as important or necessary if you use `Annotated`.
///
Let's say that you want to declare the query parameter `q` as a required `str`.
And you don't need to declare anything else for that parameter, so you don't really need to use `Query`.
But you still need to use `Path` for the `item_id` path parameter. And you don't want to use `Annotated` for some reason.
Python will complain if you put a value with a "default" before a value that doesn't have a "default".
But you can re-order them, and have the value without a default (the query parameter `q`) first.
It doesn't matter for **FastAPI**. It will detect the parameters by their names, types and default declarations (`Query`, `Path`, etc), it doesn't care about the order.
So, you can declare your function as:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
But keep in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Order the parameters as you need, tricks { #order-the-parameters-as-you-need-tricks }
/// tip
This is probably not as important or necessary if you use `Annotated`.
///
Here's a **small trick** that can be handy, but you won't need it often.
If you want to:
* declare the `q` query parameter without a `Query` nor any default value
* declare the path parameter `item_id` using `Path`
* have them in a different order
* not use `Annotated`
...Python has a little special syntax for that.
Pass `*`, as the first parameter of the function.
Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as kwargs. Even if they don't have a default value.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Better with `Annotated` { #better-with-annotated }
Keep in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and you probably won't need to use `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Number validations: greater than or equal { #number-validations-greater-than-or-equal }
With `Query` and `Path` (and others you'll see later) you can declare number constraints.
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Number validations: greater than and less than or equal { #number-validations-greater-than-and-less-than-or-equal }
The same applies for:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Number validations: floats, greater than and less than { #number-validations-floats-greater-than-and-less-than }
Number validations also work for `float` values.
Here's where it becomes important to be able to declare gt and not just ge. As with it you can require, for example, that a value must be greater than `0`, even if it is less than `1`.
So, `0.5` would be a valid value. But `0.0` or `0` would not.
And the same for lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Recap { #recap }
With `Query`, `Path` (and others you haven't seen yet) you can declare metadata and string validations in the same ways as with [Query Parameters and String Validations](query-params-str-validations.md).
And you can also declare numeric validations:
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info
`Query`, `Path`, and other classes you will see later are subclasses of a common `Param` class.
All of them share the same parameters for additional validation and metadata you have seen.
///
/// note | Technical Details
When you import `Query`, `Path` and others from `fastapi`, they are actually functions.
That when called, return instances of classes of the same name.
So, you import `Query`, which is a function. And when you call it, it returns an instance of a class also named `Query`.
These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types.
That way you can use your normal editor and coding tools without having to add custom configurations to disregard those errors.
///
================================================
FILE: docs/en/docs/tutorial/path-params.md
================================================
# Path Parameters { #path-parameters }
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
So, if you run this example and go to [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), you will see a response of:
```JSON
{"item_id":"foo"}
```
## Path parameters with types { #path-parameters-with-types }
You can declare the type of a path parameter in the function, using standard Python type annotations:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
In this case, `item_id` is declared to be an `int`.
/// check
This will give you editor support inside of your function, with error checks, completion, etc.
///
## Data conversion { #data-conversion }
If you run this example and open your browser at [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), you will see a response of:
```JSON
{"item_id":3}
```
/// check
Notice that the value your function received (and returned) is `3`, as a Python `int`, not a string `"3"`.
So, with that type declaration, **FastAPI** gives you automatic request "parsing".
///
## Data validation { #data-validation }
But if you go to the browser at [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), you will see a nice HTTP error of:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
because the path parameter `item_id` had a value of `"foo"`, which is not an `int`.
The same error would appear if you provided a `float` instead of an `int`, as in: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check
So, with the same Python type declaration, **FastAPI** gives you data validation.
Notice that the error also clearly states exactly the point where the validation didn't pass.
This is incredibly helpful while developing and debugging code that interacts with your API.
///
## Documentation { #documentation }
And when you open your browser at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), you will see an automatic, interactive, API documentation like:
/// check
Again, just with that same Python type declaration, **FastAPI** gives you automatic, interactive documentation (integrating Swagger UI).
Notice that the path parameter is declared to be an integer.
///
## Standards-based benefits, alternative documentation { #standards-based-benefits-alternative-documentation }
And because the generated schema is from the [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) standard, there are many compatible tools.
Because of this, **FastAPI** itself provides an alternative API documentation (using ReDoc), which you can access at [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc):
The same way, there are many compatible tools. Including code generation tools for many languages.
## Pydantic { #pydantic }
All the data validation is performed under the hood by [Pydantic](https://docs.pydantic.dev/), so you get all the benefits from it. And you know you are in good hands.
You can use the same type declarations with `str`, `float`, `bool` and many other complex data types.
Several of these are explored in the next chapters of the tutorial.
## Order matters { #order-matters }
When creating *path operations*, you can find situations where you have a fixed path.
Like `/users/me`, let's say that it's to get data about the current user.
And then you can also have a path `/users/{user_id}` to get data about a specific user by some user ID.
Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`.
Similarly, you cannot redefine a path operation:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
The first one will always be used since the path matches first.
## Predefined values { #predefined-values }
If you have a *path operation* that receives a *path parameter*, but you want the possible valid *path parameter* values to be predefined, you can use a standard Python `Enum`.
### Create an `Enum` class { #create-an-enum-class }
Import `Enum` and create a sub-class that inherits from `str` and from `Enum`.
By inheriting from `str` the API docs will be able to know that the values must be of type `string` and will be able to render correctly.
Then create class attributes with fixed values, which will be the available valid values:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip
If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning models.
///
### Declare a *path parameter* { #declare-a-path-parameter }
Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Check the docs { #check-the-docs }
Because the available values for the *path parameter* are predefined, the interactive docs can show them nicely:
### Working with Python *enumerations* { #working-with-python-enumerations }
The value of the *path parameter* will be an *enumeration member*.
#### Compare *enumeration members* { #compare-enumeration-members }
You can compare it with the *enumeration member* in your created enum `ModelName`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Get the *enumeration value* { #get-the-enumeration-value }
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip
You could also access the value `"lenet"` with `ModelName.lenet.value`.
///
#### Return *enumeration members* { #return-enumeration-members }
You can return *enum members* from your *path operation*, even nested in a JSON body (e.g. a `dict`).
They will be converted to their corresponding values (strings in this case) before returning them to the client:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
In your client you will get a JSON response like:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Path parameters containing paths { #path-parameters-containing-paths }
Let's say you have a *path operation* with a path `/files/{file_path}`.
But you need `file_path` itself to contain a *path*, like `home/johndoe/myfile.txt`.
So, the URL for that file would be something like: `/files/home/johndoe/myfile.txt`.
### OpenAPI support { #openapi-support }
OpenAPI doesn't support a way to declare a *path parameter* to contain a *path* inside, as that could lead to scenarios that are difficult to test and define.
Nevertheless, you can still do it in **FastAPI**, using one of the internal tools from Starlette.
And the docs would still work, although not adding any documentation telling that the parameter should contain a path.
### Path convertor { #path-convertor }
Using an option directly from Starlette you can declare a *path parameter* containing a *path* using a URL like:
```
/files/{file_path:path}
```
In this case, the name of the parameter is `file_path`, and the last part, `:path`, tells it that the parameter should match any *path*.
So, you can use it with:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip
You might need the parameter to contain `/home/johndoe/myfile.txt`, with a leading slash (`/`).
In that case, the URL would be: `/files//home/johndoe/myfile.txt`, with a double slash (`//`) between `files` and `home`.
///
## Recap { #recap }
With **FastAPI**, by using short, intuitive and standard Python type declarations, you get:
* Editor support: error checks, autocompletion, etc.
* Data "parsing"
* Data validation
* API annotation and automatic documentation
And you only have to declare them once.
That's probably the main visible advantage of **FastAPI** compared to alternative frameworks (apart from the raw performance).
================================================
FILE: docs/en/docs/tutorial/query-param-models.md
================================================
# Query Parameter Models { #query-parameter-models }
If you have a group of **query parameters** that are related, you can create a **Pydantic model** to declare them.
This would allow you to **re-use the model** in **multiple places** and also to declare validations and metadata for all the parameters at once. 😎
/// note
This is supported since FastAPI version `0.115.0`. 🤓
///
## Query Parameters with a Pydantic Model { #query-parameters-with-a-pydantic-model }
Declare the **query parameters** that you need in a **Pydantic model**, and then declare the parameter as `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI** will **extract** the data for **each field** from the **query parameters** in the request and give you the Pydantic model you defined.
## Check the Docs { #check-the-docs }
You can see the query parameters in the docs UI at `/docs`:
## Forbid Extra Query Parameters { #forbid-extra-query-parameters }
In some special use cases (probably not very common), you might want to **restrict** the query parameters that you want to receive.
You can use Pydantic's model configuration to `forbid` any `extra` fields:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
If a client tries to send some **extra** data in the **query parameters**, they will receive an **error** response.
For example, if the client tries to send a `tool` query parameter with a value of `plumbus`, like:
```http
https://example.com/items/?limit=10&tool=plumbus
```
They will receive an **error** response telling them that the query parameter `tool` is not allowed:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Summary { #summary }
You can use **Pydantic models** to declare **query parameters** in **FastAPI**. 😎
/// tip
Spoiler alert: you can also use Pydantic models to declare cookies and headers, but you will read about that later in the tutorial. 🤫
///
================================================
FILE: docs/en/docs/tutorial/query-params-str-validations.md
================================================
# Query Parameters and String Validations { #query-parameters-and-string-validations }
**FastAPI** allows you to declare additional information and validation for your parameters.
Let's take this application as example:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
The query parameter `q` is of type `str | None`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
/// note
FastAPI will know that the value of `q` is not required because of the default value `= None`.
Having `str | None` will allow your editor to give you better support and detect errors.
///
## Additional validation { #additional-validation }
We are going to enforce that even though `q` is optional, whenever it is provided, **its length doesn't exceed 50 characters**.
### Import `Query` and `Annotated` { #import-query-and-annotated }
To achieve that, first import:
* `Query` from `fastapi`
* `Annotated` from `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info
FastAPI added support for `Annotated` (and started recommending it) in version 0.95.0.
If you have an older version, you would get errors when trying to use `Annotated`.
Make sure you [Upgrade the FastAPI version](../deployment/versions.md#upgrading-the-fastapi-versions) to at least 0.95.1 before using `Annotated`.
///
## Use `Annotated` in the type for the `q` parameter { #use-annotated-in-the-type-for-the-q-parameter }
Remember I told you before that `Annotated` can be used to add metadata to your parameters in the [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations)?
Now it's the time to use it with FastAPI. 🚀
We had this type annotation:
```Python
q: str | None = None
```
What we will do is wrap that with `Annotated`, so it becomes:
```Python
q: Annotated[str | None] = None
```
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
Now let's jump to the fun stuff. 🎉
## Add `Query` to `Annotated` in the `q` parameter { #add-query-to-annotated-in-the-q-parameter }
Now that we have this `Annotated` where we can put more information (in this case some additional validation), add `Query` inside of `Annotated`, and set the parameter `max_length` to `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Notice that the default value is still `None`, so the parameter is still optional.
But now, having `Query(max_length=50)` inside of `Annotated`, we are telling FastAPI that we want it to have **additional validation** for this value, we want it to have maximum 50 characters. 😎
/// tip
Here we are using `Query()` because this is a **query parameter**. Later we will see others like `Path()`, `Body()`, `Header()`, and `Cookie()`, that also accept the same arguments as `Query()`.
///
FastAPI will now:
* **Validate** the data making sure that the max length is 50 characters
* Show a **clear error** for the client when the data is not valid
* **Document** the parameter in the OpenAPI schema *path operation* (so it will show up in the **automatic docs UI**)
## Alternative (old): `Query` as the default value { #alternative-old-query-as-the-default-value }
Previous versions of FastAPI (before 0.95.0) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you.
/// tip
For new code and whenever possible, use `Annotated` as explained above. There are multiple advantages (explained below) and no disadvantages. 🍰
///
This is how you would use `Query()` as the default value of your function parameter, setting the parameter `max_length` to 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
As in this case (without using `Annotated`) we have to replace the default value `None` in the function with `Query()`, we now need to set the default value with the parameter `Query(default=None)`, it serves the same purpose of defining that default value (at least for FastAPI).
So:
```Python
q: str | None = Query(default=None)
```
...makes the parameter optional, with a default value of `None`, the same as:
```Python
q: str | None = None
```
But the `Query` version declares it explicitly as being a query parameter.
Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
```Python
q: str | None = Query(default=None, max_length=50)
```
This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI schema *path operation*.
### `Query` as the default value or in `Annotated` { #query-as-the-default-value-or-in-annotated }
Keep in mind that when using `Query` inside of `Annotated` you cannot use the `default` parameter for `Query`.
Instead, use the actual default value of the function parameter. Otherwise, it would be inconsistent.
For example, this is not allowed:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...because it's not clear if the default value should be `"rick"` or `"morty"`.
So, you would use (preferably):
```Python
q: Annotated[str, Query()] = "rick"
```
...or in older code bases you will find:
```Python
q: str = Query(default="rick")
```
### Advantages of `Annotated` { #advantages-of-annotated }
**Using `Annotated` is recommended** instead of the default value in function parameters, it is **better** for multiple reasons. 🤓
The **default** value of the **function parameter** is the **actual default** value, that's more intuitive with Python in general. 😌
You could **call** that same function in **other places** without FastAPI, and it would **work as expected**. If there's a **required** parameter (without a default value), your **editor** will let you know with an error, **Python** will also complain if you run it without passing the required parameter.
When you don't use `Annotated` and instead use the **(old) default value style**, if you call that function without FastAPI in **other places**, you have to **remember** to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. `QueryInfo` or something similar instead of `str`). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.
Because `Annotated` can have more than one metadata annotation, you could now even use the same function with other tools, like [Typer](https://typer.tiangolo.com/). 🚀
## Add more validations { #add-more-validations }
You can also add a parameter `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Add regular expressions { #add-regular-expressions }
You can define a regular expression `pattern` that the parameter should match:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
This specific regular expression pattern checks that the received parameter value:
* `^`: starts with the following characters, doesn't have characters before.
* `fixedquery`: has the exact value `fixedquery`.
* `$`: ends there, doesn't have any more characters after `fixedquery`.
If you feel lost with all these **"regular expression"** ideas, don't worry. They are a hard topic for many people. You can still do a lot of stuff without needing regular expressions yet.
Now you know that whenever you need them you can use them in **FastAPI**.
## Default values { #default-values }
You can, of course, use default values other than `None`.
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note
Having a default value of any type, including `None`, makes the parameter optional (not required).
///
## Required parameters { #required-parameters }
When we don't need to declare more validations or metadata, we can make the `q` query parameter required just by not declaring a default value, like:
```Python
q: str
```
instead of:
```Python
q: str | None = None
```
But we are now declaring it with `Query`, for example like:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
So, when you need to declare a value as required while using `Query`, you can simply not declare a default value:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Required, can be `None` { #required-can-be-none }
You can declare that a parameter can accept `None`, but that it's still required. This would force clients to send a value, even if the value is `None`.
To do that, you can declare that `None` is a valid type but simply do not declare a default value:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Query parameter list / multiple values { #query-parameter-list-multiple-values }
When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in another way, to receive multiple values.
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Then, with a URL like:
```
http://localhost:8000/items/?q=foo&q=bar
```
you would receive the multiple `q` *query parameters'* values (`foo` and `bar`) in a Python `list` inside your *path operation function*, in the *function parameter* `q`.
So, the response to that URL would be:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip
To declare a query parameter with a type of `list`, like in the example above, you need to explicitly use `Query`, otherwise it would be interpreted as a request body.
///
The interactive API docs will update accordingly, to allow multiple values:
### Query parameter list / multiple values with defaults { #query-parameter-list-multiple-values-with-defaults }
You can also define a default `list` of values if none are provided:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
If you go to:
```
http://localhost:8000/items/
```
the default of `q` will be: `["foo", "bar"]` and your response will be:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Using just `list` { #using-just-list }
You can also use `list` directly instead of `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note
Keep in mind that in this case, FastAPI won't check the contents of the list.
For example, `list[int]` would check (and document) that the contents of the list are integers. But `list` alone wouldn't.
///
## Declare more metadata { #declare-more-metadata }
You can add more information about the parameter.
That information will be included in the generated OpenAPI and used by the documentation user interfaces and external tools.
/// note
Keep in mind that different tools might have different levels of OpenAPI support.
Some of them might not show all the extra information declared yet, although in most of the cases, the missing feature is already planned for development.
///
You can add a `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
And a `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Alias parameters { #alias-parameters }
Imagine that you want the parameter to be `item-query`.
Like in:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
But `item-query` is not a valid Python variable name.
The closest would be `item_query`.
But you still need it to be exactly `item-query`...
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Deprecating parameters { #deprecating-parameters }
Now let's say you don't like this parameter anymore.
You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as deprecated.
Then pass the parameter `deprecated=True` to `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
The docs will show it like this:
## Exclude parameters from OpenAPI { #exclude-parameters-from-openapi }
To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter `include_in_schema` of `Query` to `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Custom Validation { #custom-validation }
There could be cases where you need to do some **custom validation** that can't be done with the parameters shown above.
In those cases, you can use a **custom validator function** that is applied after the normal validation (e.g. after validating that the value is a `str`).
You can achieve that using [Pydantic's `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) inside of `Annotated`.
/// tip
Pydantic also has [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) and others. 🤓
///
For example, this custom validator checks that the item ID starts with `isbn-` for an ISBN book number or with `imdb-` for an IMDB movie URL ID:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info
This is available with Pydantic version 2 or above. 😎
///
/// tip
If you need to do any type of validation that requires communicating with any **external component**, like a database or another API, you should instead use **FastAPI Dependencies**, you will learn about them later.
These custom validators are for things that can be checked with **only** the **same data** provided in the request.
///
### Understand that Code { #understand-that-code }
The important point is just using **`AfterValidator` with a function inside `Annotated`**. Feel free to skip this part. 🤸
---
But if you're curious about this specific code example and you're still entertained, here are some extra details.
#### String with `value.startswith()` { #string-with-value-startswith }
Did you notice? a string using `value.startswith()` can take a tuple, and it will check each value in the tuple:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### A Random Item { #a-random-item }
With `data.items()` we get an iterable object with tuples containing the key and value for each dictionary item.
We convert this iterable object into a proper `list` with `list(data.items())`.
Then with `random.choice()` we can get a **random value** from the list, so, we get a tuple with `(id, name)`. It will be something like `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Then we **assign those two values** of the tuple to the variables `id` and `name`.
So, if the user didn't provide an item ID, they will still receive a random suggestion.
...we do all this in a **single simple line**. 🤯 Don't you love Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Recap { #recap }
You can declare additional validations and metadata for your parameters.
Generic validations and metadata:
* `alias`
* `title`
* `description`
* `deprecated`
Validations specific for strings:
* `min_length`
* `max_length`
* `pattern`
Custom validations using `AfterValidator`.
In these examples you saw how to declare validations for `str` values.
See the next chapters to learn how to declare validations for other types, like numbers.
================================================
FILE: docs/en/docs/tutorial/query-params.md
================================================
# Query Parameters { #query-parameters }
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
For example, in the URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...the query parameters are:
* `skip`: with a value of `0`
* `limit`: with a value of `10`
As they are part of the URL, they are "naturally" strings.
But when you declare them with Python types (in the example above, as `int`), they are converted to that type and validated against it.
All the same process that applied for path parameters also applies for query parameters:
* Editor support (obviously)
* Data "parsing"
* Data validation
* Automatic documentation
## Defaults { #defaults }
As query parameters are not a fixed part of a path, they can be optional and can have default values.
In the example above they have default values of `skip=0` and `limit=10`.
So, going to the URL:
```
http://127.0.0.1:8000/items/
```
would be the same as going to:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
But if you go to, for example:
```
http://127.0.0.1:8000/items/?skip=20
```
The parameter values in your function will be:
* `skip=20`: because you set it in the URL
* `limit=10`: because that was the default value
## Optional parameters { #optional-parameters }
The same way, you can declare optional query parameters, by setting their default to `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
In this case, the function parameter `q` will be optional, and will be `None` by default.
/// check
Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter.
///
## Query parameter type conversion { #query-parameter-type-conversion }
You can also declare `bool` types, and they will be converted:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
In this case, if you go to:
```
http://127.0.0.1:8000/items/foo?short=1
```
or
```
http://127.0.0.1:8000/items/foo?short=True
```
or
```
http://127.0.0.1:8000/items/foo?short=true
```
or
```
http://127.0.0.1:8000/items/foo?short=on
```
or
```
http://127.0.0.1:8000/items/foo?short=yes
```
or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter `short` with a `bool` value of `True`. Otherwise as `False`.
## Multiple path and query parameters { #multiple-path-and-query-parameters }
You can declare multiple path parameters and query parameters at the same time, **FastAPI** knows which is which.
And you don't have to declare them in any specific order.
They will be detected by name:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Required query parameters { #required-query-parameters }
When you declare a default value for non-path parameters (for now, we have only seen query parameters), then it is not required.
If you don't want to add a specific value but just make it optional, set the default as `None`.
But when you want to make a query parameter required, you can just not declare any default value:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Here the query parameter `needy` is a required query parameter of type `str`.
If you open in your browser a URL like:
```
http://127.0.0.1:8000/items/foo-item
```
...without adding the required parameter `needy`, you will see an error like:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
As `needy` is a required parameter, you would need to set it in the URL:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...this would work:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
In this case, there are 3 query parameters:
* `needy`, a required `str`.
* `skip`, an `int` with a default value of `0`.
* `limit`, an optional `int`.
/// tip
You could also use `Enum`s the same way as with [Path Parameters](path-params.md#predefined-values).
///
================================================
FILE: docs/en/docs/tutorial/request-files.md
================================================
# Request Files { #request-files }
You can define files to be uploaded by the client using `File`.
/// info
To receive uploaded files, first install [`python-multipart`](https://github.com/Kludex/python-multipart).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install python-multipart
```
This is because uploaded files are sent as "form data".
///
## Import `File` { #import-file }
Import `File` and `UploadFile` from `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Define `File` Parameters { #define-file-parameters }
Create file parameters the same way you would for `Body` or `Form`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info
`File` is a class that inherits directly from `Form`.
But remember that when you import `Query`, `Path`, `File` and others from `fastapi`, those are actually functions that return special classes.
///
/// tip
To declare File bodies, you need to use `File`, because otherwise the parameters would be interpreted as query parameters or body (JSON) parameters.
///
The files will be uploaded as "form data".
If you declare the type of your *path operation function* parameter as `bytes`, **FastAPI** will read the file for you and you will receive the contents as `bytes`.
Keep in mind that this means that the whole contents will be stored in memory. This will work well for small files.
But there are several cases in which you might benefit from using `UploadFile`.
## File Parameters with `UploadFile` { #file-parameters-with-uploadfile }
Define a file parameter with a type of `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Using `UploadFile` has several advantages over `bytes`:
* You don't have to use `File()` in the default value of the parameter.
* It uses a "spooled" file:
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
* You can get metadata from the uploaded file.
* It has a [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` interface.
* It exposes an actual Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) object that you can pass directly to other libraries that expect a file-like object.
### `UploadFile` { #uploadfile }
`UploadFile` has the following attributes:
* `filename`: A `str` with the original file name that was uploaded (e.g. `myimage.jpg`).
* `content_type`: A `str` with the content type (MIME type / media type) (e.g. `image/jpeg`).
* `file`: A [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (a [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) object). This is the actual Python file object that you can pass directly to other functions or libraries that expect a "file-like" object.
`UploadFile` has the following `async` methods. They all call the corresponding file methods underneath (using the internal `SpooledTemporaryFile`).
* `write(data)`: Writes `data` (`str` or `bytes`) to the file.
* `read(size)`: Reads `size` (`int`) bytes/characters of the file.
* `seek(offset)`: Goes to the byte position `offset` (`int`) in the file.
* E.g., `await myfile.seek(0)` would go to the start of the file.
* This is especially useful if you run `await myfile.read()` once and then need to read the contents again.
* `close()`: Closes the file.
As all these methods are `async` methods, you need to "await" them.
For example, inside of an `async` *path operation function* you can get the contents with:
```Python
contents = await myfile.read()
```
If you are inside of a normal `def` *path operation function*, you can access the `UploadFile.file` directly, for example:
```Python
contents = myfile.file.read()
```
/// note | `async` Technical Details
When you use the `async` methods, **FastAPI** runs the file methods in a threadpool and awaits for them.
///
/// note | Starlette Technical Details
**FastAPI**'s `UploadFile` inherits directly from **Starlette**'s `UploadFile`, but adds some necessary parts to make it compatible with **Pydantic** and the other parts of FastAPI.
///
## What is "Form Data" { #what-is-form-data }
The way HTML forms (``) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON.
**FastAPI** will make sure to read that data from the right place instead of JSON.
/// note | Technical Details
Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded` when it doesn't include files.
But when the form includes files, it is encoded as `multipart/form-data`. If you use `File`, **FastAPI** will know it has to get the files from the correct part of the body.
If you want to read more about these encodings and form fields, head to the [MDN web docs for `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning
You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`.
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
///
## Optional File Upload { #optional-file-upload }
You can make a file optional by using standard type annotations and setting a default value of `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` with Additional Metadata { #uploadfile-with-additional-metadata }
You can also use `File()` with `UploadFile`, for example, to set additional metadata:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Multiple File Uploads { #multiple-file-uploads }
It's possible to upload several files at the same time.
They would be associated to the same "form field" sent using "form data".
To use that, declare a list of `bytes` or `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
/// note | Technical Details
You could also use `from starlette.responses import HTMLResponse`.
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
///
### Multiple File Uploads with Additional Metadata { #multiple-file-uploads-with-additional-metadata }
And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Recap { #recap }
Use `File`, `bytes`, and `UploadFile` to declare files to be uploaded in the request, sent as form data.
================================================
FILE: docs/en/docs/tutorial/request-form-models.md
================================================
# Form Models { #form-models }
You can use **Pydantic models** to declare **form fields** in FastAPI.
/// info
To use forms, first install [`python-multipart`](https://github.com/Kludex/python-multipart).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install python-multipart
```
///
/// note
This is supported since FastAPI version `0.113.0`. 🤓
///
## Pydantic Models for Forms { #pydantic-models-for-forms }
You just need to declare a **Pydantic model** with the fields you want to receive as **form fields**, and then declare the parameter as `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** will **extract** the data for **each field** from the **form data** in the request and give you the Pydantic model you defined.
## Check the Docs { #check-the-docs }
You can verify it in the docs UI at `/docs`:
## Forbid Extra Form Fields { #forbid-extra-form-fields }
In some special use cases (probably not very common), you might want to **restrict** the form fields to only those declared in the Pydantic model. And **forbid** any **extra** fields.
/// note
This is supported since FastAPI version `0.114.0`. 🤓
///
You can use Pydantic's model configuration to `forbid` any `extra` fields:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
If a client tries to send some extra data, they will receive an **error** response.
For example, if the client tries to send the form fields:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
They will receive an error response telling them that the field `extra` is not allowed:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Summary { #summary }
You can use Pydantic models to declare form fields in FastAPI. 😎
================================================
FILE: docs/en/docs/tutorial/request-forms-and-files.md
================================================
# Request Forms and Files { #request-forms-and-files }
You can define files and form fields at the same time using `File` and `Form`.
/// info
To receive uploaded files and/or form data, first install [`python-multipart`](https://github.com/Kludex/python-multipart).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install python-multipart
```
///
## Import `File` and `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Define `File` and `Form` parameters { #define-file-and-form-parameters }
Create file and form parameters the same way you would for `Body` or `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
The files and form fields will be uploaded as form data and you will receive the files and form fields.
And you can declare some of the files as `bytes` and some as `UploadFile`.
/// warning
You can declare multiple `File` and `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `multipart/form-data` instead of `application/json`.
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
///
## Recap { #recap }
Use `File` and `Form` together when you need to receive data and files in the same request.
================================================
FILE: docs/en/docs/tutorial/request-forms.md
================================================
# Form Data { #form-data }
When you need to receive form fields instead of JSON, you can use `Form`.
/// info
To use forms, first install [`python-multipart`](https://github.com/Kludex/python-multipart).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install python-multipart
```
///
## Import `Form` { #import-form }
Import `Form` from `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Define `Form` parameters { #define-form-parameters }
Create form parameters the same way you would for `Body` or `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.
The spec requires the fields to be exactly named `username` and `password`, and to be sent as form fields, not JSON.
With `Form` you can declare the same configurations as with `Body` (and `Query`, `Path`, `Cookie`), including validation, examples, an alias (e.g. `user-name` instead of `username`), etc.
/// info
`Form` is a class that inherits directly from `Body`.
///
/// tip
To declare form bodies, you need to use `Form` explicitly, because without it the parameters would be interpreted as query parameters or body (JSON) parameters.
///
## About "Form Fields" { #about-form-fields }
The way HTML forms (``) sends the data to the server normally uses a "special" encoding for that data, it's different from JSON.
**FastAPI** will make sure to read that data from the right place instead of JSON.
/// note | Technical Details
Data from forms is normally encoded using the "media type" `application/x-www-form-urlencoded`.
But when the form includes files, it is encoded as `multipart/form-data`. You'll read about handling files in the next chapter.
If you want to read more about these encodings and form fields, head to the [MDN web docs for `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning
You can declare multiple `Form` parameters in a *path operation*, but you can't also declare `Body` fields that you expect to receive as JSON, as the request will have the body encoded using `application/x-www-form-urlencoded` instead of `application/json`.
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
///
## Recap { #recap }
Use `Form` to declare form data input parameters.
================================================
FILE: docs/en/docs/tutorial/response-model.md
================================================
# Response Model - Return Type { #response-model-return-type }
You can declare the type used for the response by annotating the *path operation function* **return type**.
You can use **type annotations** the same way you would for input data in function **parameters**, you can use Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI will use this return type to:
* **Validate** the returned data.
* If the data is invalid (e.g. you are missing a field), it means that *your* app code is broken, not returning what it should, and it will return a server error instead of returning incorrect data. This way you and your clients can be certain that they will receive the data and the data shape expected.
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
* This will be used by the **automatic docs**.
* It will also be used by automatic client code generation tools.
* **Serialize** the returned data to JSON using Pydantic, which is written in **Rust**, so it will be **much faster**.
But most importantly:
* It will **limit and filter** the output data to what is defined in the return type.
* This is particularly important for **security**, we'll see more of that below.
## `response_model` Parameter { #response-model-parameter }
There are some cases where you need or want to return some data that is not exactly what the type declares.
For example, you could want to **return a dictionary** or a database object, but **declare it as a Pydantic model**. This way the Pydantic model would do all the data documentation, validation, etc. for the object that you returned (e.g. a dictionary or database object).
If you added the return type annotation, tools and editors would complain with a (correct) error telling you that your function is returning a type (e.g. a dict) that is different from what you declared (e.g. a Pydantic model).
In those cases, you can use the *path operation decorator* parameter `response_model` instead of the return type.
You can use the `response_model` parameter in any of the *path operations*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body.
///
`response_model` receives the same type you would declare for a Pydantic model field, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`.
FastAPI will use this `response_model` to do all the data documentation, validation, etc. and also to **convert and filter the output data** to its type declaration.
/// tip
If you have strict type checks in your editor, mypy, etc, you can declare the function return type as `Any`.
That way you tell the editor that you are intentionally returning anything. But FastAPI will still do the data documentation, validation, filtering, etc. with the `response_model`.
///
### `response_model` Priority { #response-model-priority }
If you declare both a return type and a `response_model`, the `response_model` will take priority and be used by FastAPI.
This way you can add correct type annotations to your functions even when you are returning a type different than the response model, to be used by the editor and tools like mypy. And still you can have FastAPI do the data validation, documentation, etc. using the `response_model`.
You can also use `response_model=None` to disable creating a response model for that *path operation*, you might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will see an example of that in one of the sections below.
## Return the same input data { #return-the-same-input-data }
Here we are declaring a `UserIn` model, it will contain a plaintext password:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info
To use `EmailStr`, first install [`email-validator`](https://github.com/JoshData/python-email-validator).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install email-validator
```
or with:
```console
$ pip install "pydantic[email]"
```
///
And we are using this model to declare our input and the same model to declare our output:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
In this case, it might not be a problem, because it's the same user sending the password.
But if we use the same model for another *path operation*, we could be sending our user's passwords to every client.
/// danger
Never store the plain password of a user or send it in a response like this, unless you know all the caveats and you know what you are doing.
///
## Add an output model { #add-an-output-model }
We can instead create an input model with the plaintext password and an output model without it:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Here, even though our *path operation function* is returning the same input user that contains the password:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
### `response_model` or Return Type { #response-model-or-return-type }
In this case, because the two models are different, if we annotated the function return type as `UserOut`, the editor and tools would complain that we are returning an invalid type, as those are different classes.
That's why in this example we have to declare it in the `response_model` parameter.
...but continue reading below to see how to overcome that.
## Return Type and Data Filtering { #return-type-and-data-filtering }
Let's continue from the previous example. We wanted to **annotate the function with one type**, but we wanted to be able to return from the function something that actually includes **more data**.
We want FastAPI to keep **filtering** the data using the response model. So that even though the function returns more data, the response will only include the fields declared in the response model.
In the previous example, because the classes were different, we had to use the `response_model` parameter. But that also means that we don't get the support from the editor and tools checking the function return type.
But in most of the cases where we need to do something like this, we want the model just to **filter/remove** some of the data as in this example.
And in those cases, we can use classes and inheritance to take advantage of function **type annotations** to get better support in the editor and tools, and still get the FastAPI **data filtering**.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
With this, we get tooling support, from editors and mypy as this code is correct in terms of types, but we also get the data filtering from FastAPI.
How does this work? Let's check that out. 🤓
### Type Annotations and Tooling { #type-annotations-and-tooling }
First let's see how editors, mypy and other tools would see this.
`BaseUser` has the base fields. Then `UserIn` inherits from `BaseUser` and adds the `password` field, so, it will include all the fields from both models.
We annotate the function return type as `BaseUser`, but we are actually returning a `UserIn` instance.
The editor, mypy, and other tools won't complain about this because, in typing terms, `UserIn` is a subclass of `BaseUser`, which means it's a *valid* type when what is expected is anything that is a `BaseUser`.
### FastAPI Data Filtering { #fastapi-data-filtering }
Now, for FastAPI, it will see the return type and make sure that what you return includes **only** the fields that are declared in the type.
FastAPI does several things internally with Pydantic to make sure that those same rules of class inheritance are not used for the returned data filtering, otherwise you could end up returning much more data than what you expected.
This way, you can get the best of both worlds: type annotations with **tooling support** and **data filtering**.
## See it in the docs { #see-it-in-the-docs }
When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema:
And both models will be used for the interactive API documentation:
## Other Return Type Annotations { #other-return-type-annotations }
There might be cases where you return something that is not a valid Pydantic field and you annotate it in the function, only to get the support provided by tooling (the editor, mypy, etc).
### Return a Response Directly { #return-a-response-directly }
The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass of) `Response`.
And tools will also be happy because both `RedirectResponse` and `JSONResponse` are subclasses of `Response`, so the type annotation is correct.
### Annotate a Response Subclass { #annotate-a-response-subclass }
You can also use a subclass of `Response` in the type annotation:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case.
### Invalid Return Type Annotations { #invalid-return-type-annotations }
But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object) and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that type annotation, and will fail.
The same would happen if you had something like a union between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...this fails because the type annotation is not a Pydantic type and is not just a single `Response` class or subclass, it's a union (any of the two) between a `Response` and a `dict`.
### Disable Response Model { #disable-response-model }
Continuing from the example above, you might not want to have the default data validation, documentation, filtering, etc. that is performed by FastAPI.
But you might want to still keep the return type annotation in the function to get the support from tools like editors and type checkers (e.g. mypy).
In this case, you can disable the response model generation by setting `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓
## Response Model encoding parameters { #response-model-encoding-parameters }
Your response model could have default values, like:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (or `str | None = None` in Python 3.10) has a default of `None`.
* `tax: float = 10.5` has a default of `10.5`.
* `tags: List[str] = []` has a default of an empty list: `[]`.
but you might want to omit them from the result if they were not actually stored.
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
### Use the `response_model_exclude_unset` parameter { #use-the-response-model-exclude-unset-parameter }
You can set the *path operation decorator* parameter `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
and those default values won't be included in the response, only the values actually set.
So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info
You can also use:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
as described in [the Pydantic docs](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) for `exclude_defaults` and `exclude_none`.
///
#### Data with values for fields with defaults { #data-with-values-for-fields-with-defaults }
But if your data has values for the model's fields with default values, like the item with ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
they will be included in the response.
#### Data with the same values as the defaults { #data-with-the-same-values-as-the-defaults }
If the data has the same values as the default ones, like the item with ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though `description`, `tax`, and `tags` have the same values as the defaults, they were set explicitly (instead of taken from the defaults).
So, they will be included in the JSON response.
/// tip
Notice that the default values can be anything, not only `None`.
They can be a list (`[]`), a `float` of `10.5`, etc.
///
### `response_model_include` and `response_model_exclude` { #response-model-include-and-response-model-exclude }
You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`.
They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest).
This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.
/// tip
But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.
This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes.
This also applies to `response_model_by_alias` that works similarly.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip
The syntax `{"name", "description"}` creates a `set` with those two values.
It is equivalent to `set(["name", "description"])`.
///
#### Using `list`s instead of `set`s { #using-lists-instead-of-sets }
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Recap { #recap }
Use the *path operation decorator's* parameter `response_model` to define response models and especially to ensure private data is filtered out.
Use `response_model_exclude_unset` to return only the values explicitly set.
================================================
FILE: docs/en/docs/tutorial/response-status-code.md
================================================
# Response Status Code { #response-status-code }
The same way you can specify a response model, you can also declare the HTTP status code used for the response with the parameter `status_code` in any of the *path operations*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note
Notice that `status_code` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your *path operation function*, like all the parameters and body.
///
The `status_code` parameter receives a number with the HTTP status code.
/// info
`status_code` can alternatively also receive an `IntEnum`, such as Python's [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus).
///
It will:
* Return that status code in the response.
* Document it as such in the OpenAPI schema (and so, in the user interfaces):
/// note
Some response codes (see the next section) indicate that the response does not have a body.
FastAPI knows this, and will produce OpenAPI docs that state there is no response body.
///
## About HTTP status codes { #about-http-status-codes }
/// note
If you already know what HTTP status codes are, skip to the next section.
///
In HTTP, you send a numeric status code of 3 digits as part of the response.
These status codes have a name associated to recognize them, but the important part is the number.
In short:
* `100 - 199` are for "Information". You rarely use them directly. Responses with these status codes cannot have a body.
* **`200 - 299`** are for "Successful" responses. These are the ones you would use the most.
* `200` is the default status code, which means everything was "OK".
* Another example would be `201`, "Created". It is commonly used after creating a new record in the database.
* A special case is `204`, "No Content". This response is used when there is no content to return to the client, and so the response must not have a body.
* **`300 - 399`** are for "Redirection". Responses with these status codes may or may not have a body, except for `304`, "Not Modified", which must not have one.
* **`400 - 499`** are for "Client error" responses. These are the second type you would probably use the most.
* An example is `404`, for a "Not Found" response.
* For generic errors from the client, you can just use `400`.
* `500 - 599` are for server errors. You almost never use them directly. When something goes wrong at some part in your application code, or server, it will automatically return one of these status codes.
/// tip
To know more about each status code and which code is for what, check the [MDN documentation about HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Shortcut to remember the names { #shortcut-to-remember-the-names }
Let's see the previous example again:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` is the status code for "Created".
But you don't have to memorize what each of these codes mean.
You can use the convenience variables from `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them:
/// note | Technical Details
You could also use `from starlette import status`.
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
///
## Changing the default { #changing-the-default }
Later, in the [Advanced User Guide](../advanced/response-change-status-code.md), you will see how to return a different status code than the default you are declaring here.
================================================
FILE: docs/en/docs/tutorial/schema-extra-example.md
================================================
# Declare Request Example Data { #declare-request-example-data }
You can declare examples of the data your app can receive.
Here are several ways to do it.
## Extra JSON Schema data in Pydantic models { #extra-json-schema-data-in-pydantic-models }
You can declare `examples` for a Pydantic model that will be added to the generated JSON Schema.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
That extra info will be added as-is to the output **JSON Schema** for that model, and it will be used in the API docs.
You can use the attribute `model_config` that takes a `dict` as described in [Pydantic's docs: Configuration](https://docs.pydantic.dev/latest/api/config/).
You can set `"json_schema_extra"` with a `dict` containing any additional data you would like to show up in the generated JSON Schema, including `examples`.
/// tip
You could use the same technique to extend the JSON Schema and add your own custom extra info.
For example you could use it to add metadata for a frontend user interface, etc.
///
/// info
OpenAPI 3.1.0 (used since FastAPI 0.99.0) added support for `examples`, which is part of the **JSON Schema** standard.
Before that, it only supported the keyword `example` with a single example. That is still supported by OpenAPI 3.1.0, but is deprecated and is not part of the JSON Schema standard. So you are encouraged to migrate `example` to `examples`. 🤓
You can read more at the end of this page.
///
## `Field` additional arguments { #field-additional-arguments }
When using `Field()` with Pydantic models, you can also declare additional `examples`:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` in JSON Schema - OpenAPI { #examples-in-json-schema-openapi }
When using any of:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
you can also declare a group of `examples` with additional information that will be added to their **JSON Schemas** inside of **OpenAPI**.
### `Body` with `examples` { #body-with-examples }
Here we pass `examples` containing one example of the data expected in `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Example in the docs UI { #example-in-the-docs-ui }
With any of the methods above it would look like this in the `/docs`:
### `Body` with multiple `examples` { #body-with-multiple-examples }
You can of course also pass multiple `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
When you do this, the examples will be part of the internal **JSON Schema** for that body data.
Nevertheless, at the time of writing this, Swagger UI, the tool in charge of showing the docs UI, doesn't support showing multiple examples for the data in **JSON Schema**. But read below for a workaround.
### OpenAPI-specific `examples` { #openapi-specific-examples }
Since before **JSON Schema** supported `examples` OpenAPI had support for a different field also called `examples`.
This **OpenAPI-specific** `examples` goes in another section in the OpenAPI specification. It goes in the **details for each *path operation***, not inside each JSON Schema.
And Swagger UI has supported this particular `examples` field for a while. So, you can use it to **show** different **examples in the docs UI**.
The shape of this OpenAPI-specific field `examples` is a `dict` with **multiple examples** (instead of a `list`), each with extra information that will be added to **OpenAPI** too.
This doesn't go inside of each JSON Schema contained in OpenAPI, this goes outside, in the *path operation* directly.
### Using the `openapi_examples` Parameter { #using-the-openapi-examples-parameter }
You can declare the OpenAPI-specific `examples` in FastAPI with the parameter `openapi_examples` for:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
The keys of the `dict` identify each example, and each value is another `dict`.
Each specific example `dict` in the `examples` can contain:
* `summary`: Short description for the example.
* `description`: A long description that can contain Markdown text.
* `value`: This is the actual example shown, e.g. a `dict`.
* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`.
You can use it like this:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### OpenAPI Examples in the Docs UI { #openapi-examples-in-the-docs-ui }
With `openapi_examples` added to `Body()` the `/docs` would look like:
## Technical Details { #technical-details }
/// tip
If you are already using **FastAPI** version **0.99.0 or above**, you can probably **skip** these details.
They are more relevant for older versions, before OpenAPI 3.1.0 was available.
You can consider this a brief OpenAPI and JSON Schema **history lesson**. 🤓
///
/// warning
These are very technical details about the standards **JSON Schema** and **OpenAPI**.
If the ideas above already work for you, that might be enough, and you probably don't need these details, feel free to skip them.
///
Before OpenAPI 3.1.0, OpenAPI used an older and modified version of **JSON Schema**.
JSON Schema didn't have `examples`, so OpenAPI added its own `example` field to its own modified version.
OpenAPI also added `example` and `examples` fields to other parts of the specification:
* [`Parameter Object` (in the specification)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object) that was used by FastAPI's:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, in the field `content`, on the `Media Type Object` (in the specification)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object) that was used by FastAPI's:
* `Body()`
* `File()`
* `Form()`
/// info
This old OpenAPI-specific `examples` parameter is now `openapi_examples` since FastAPI `0.103.0`.
///
### JSON Schema's `examples` field { #json-schemas-examples-field }
But then JSON Schema added an [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) field to a new version of the specification.
And then the new OpenAPI 3.1.0 was based on the latest version (JSON Schema 2020-12) that included this new field `examples`.
And now this new `examples` field takes precedence over the old single (and custom) `example` field, that is now deprecated.
This new `examples` field in JSON Schema is **just a `list`** of examples, not a dict with extra metadata as in the other places in OpenAPI (described above).
/// info
Even after OpenAPI 3.1.0 was released with this new simpler integration with JSON Schema, for a while, Swagger UI, the tool that provides the automatic docs, didn't support OpenAPI 3.1.0 (it does since version 5.0.0 🎉).
Because of that, versions of FastAPI previous to 0.99.0 still used versions of OpenAPI lower than 3.1.0.
///
### Pydantic and FastAPI `examples` { #pydantic-and-fastapi-examples }
When you add `examples` inside a Pydantic model, using `schema_extra` or `Field(examples=["something"])` that example is added to the **JSON Schema** for that Pydantic model.
And that **JSON Schema** of the Pydantic model is included in the **OpenAPI** of your API, and then it's used in the docs UI.
In versions of FastAPI before 0.99.0 (0.99.0 and above use the newer OpenAPI 3.1.0) when you used `example` or `examples` with any of the other utilities (`Query()`, `Body()`, etc.) those examples were not added to the JSON Schema that describes that data (not even to OpenAPI's own version of JSON Schema), they were added directly to the *path operation* declaration in OpenAPI (outside the parts of OpenAPI that use JSON Schema).
But now that FastAPI 0.99.0 and above uses OpenAPI 3.1.0, that uses JSON Schema 2020-12, and Swagger UI 5.0.0 and above, everything is more consistent and the examples are included in JSON Schema.
### Swagger UI and OpenAPI-specific `examples` { #swagger-ui-and-openapi-specific-examples }
Now, as Swagger UI didn't support multiple JSON Schema examples (as of 2023-08-26), users didn't have a way to show multiple examples in the docs.
To solve that, FastAPI `0.103.0` **added support** for declaring the same old **OpenAPI-specific** `examples` field with the new parameter `openapi_examples`. 🤓
### Summary { #summary }
I used to say I didn't like history that much... and look at me now giving "tech history" lessons. 😅
In short, **upgrade to FastAPI 0.99.0 or above**, and things are much **simpler, consistent, and intuitive**, and you don't have to know all these historic details. 😎
================================================
FILE: docs/en/docs/tutorial/security/first-steps.md
================================================
# Security - First Steps { #security-first-steps }
Let's imagine that you have your **backend** API in some domain.
And you have a **frontend** in another domain or in a different path of the same domain (or in a mobile application).
And you want to have a way for the frontend to authenticate with the backend, using a **username** and **password**.
We can use **OAuth2** to build that with **FastAPI**.
But let's save you the time of reading the full long specification just to find those little pieces of information you need.
Let's use the tools provided by **FastAPI** to handle security.
## How it looks { #how-it-looks }
Let's first just use the code and see how it works, and then we'll come back to understand what's happening.
## Create `main.py` { #create-main-py }
Copy the example in a file `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Run it { #run-it }
/// info
The [`python-multipart`](https://github.com/Kludex/python-multipart) package is automatically installed with **FastAPI** when you run the `pip install "fastapi[standard]"` command.
However, if you use the `pip install fastapi` command, the `python-multipart` package is not included by default.
To install it manually, make sure you create a [virtual environment](../../virtual-environments.md), activate it, and then install it with:
```console
$ pip install python-multipart
```
This is because **OAuth2** uses "form data" for sending the `username` and `password`.
///
Run the example with:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Check it { #check-it }
Go to the interactive docs at: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You will see something like this:
/// check | Authorize button!
You already have a shiny new "Authorize" button.
And your *path operation* has a little lock in the top-right corner that you can click.
///
And if you click it, you have a little authorization form to type a `username` and `password` (and other optional fields):
/// note
It doesn't matter what you type in the form, it won't work yet. But we'll get there.
///
This is of course not the frontend for the final users, but it's a great automatic tool to document interactively all your API.
It can be used by the frontend team (that can also be yourself).
It can be used by third party applications and systems.
And it can also be used by yourself, to debug, check and test the same application.
## The `password` flow { #the-password-flow }
Now let's go back a bit and understand what is all that.
The `password` "flow" is one of the ways ("flows") defined in OAuth2, to handle security and authentication.
OAuth2 was designed so that the backend or API could be independent of the server that authenticates the user.
But in this case, the same **FastAPI** application will handle the API and the authentication.
So, let's review it from that simplified point of view:
* The user types the `username` and `password` in the frontend, and hits `Enter`.
* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API (declared with `tokenUrl="token"`).
* The API checks that `username` and `password`, and responds with a "token" (we haven't implemented any of this yet).
* A "token" is just a string with some content that we can use later to verify this user.
* Normally, a token is set to expire after some time.
* So, the user will have to log in again at some point later.
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases).
* The frontend stores that token temporarily somewhere.
* The user clicks in the frontend to go to another section of the frontend web app.
* The frontend needs to fetch some more data from the API.
* But it needs authentication for that specific endpoint.
* So, to authenticate with our API, it sends a header `Authorization` with a value of `Bearer ` plus the token.
* If the token contains `foobar`, the content of the `Authorization` header would be: `Bearer foobar`.
## **FastAPI**'s `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
**FastAPI** provides several tools, at different levels of abstraction, to implement these security features.
In this example we are going to use **OAuth2**, with the **Password** flow, using a **Bearer** token. We do that using the `OAuth2PasswordBearer` class.
/// info
A "bearer" token is not the only option.
But it's the best one for our use case.
And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that better suits your needs.
In that case, **FastAPI** also provides you with the tools to build it.
///
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip
Here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`.
Because we are using a relative URL, if your API was located at `https://example.com/`, then it would refer to `https://example.com/token`. But if your API was located at `https://example.com/api/v1/`, then it would refer to `https://example.com/api/v1/token`.
Using a relative URL is important to make sure your application keeps working even in an advanced use case like [Behind a Proxy](../../advanced/behind-a-proxy.md).
///
This parameter doesn't create that endpoint / *path operation*, but declares that the URL `/token` will be the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems.
We will soon also create the actual path operation.
/// info
If you are a very strict "Pythonista" you might dislike the style of the parameter name `tokenUrl` instead of `token_url`.
That's because it is using the same name as in the OpenAPI spec. So that if you need to investigate more about any of these security schemes you can just copy and paste it to find more information about it.
///
The `oauth2_scheme` variable is an instance of `OAuth2PasswordBearer`, but it is also a "callable".
It could be called as:
```Python
oauth2_scheme(some, parameters)
```
So, it can be used with `Depends`.
### Use it { #use-it }
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.
**FastAPI** will know that it can use this dependency to define a "security scheme" in the OpenAPI schema (and the automatic API docs).
/// info | Technical Details
**FastAPI** will know that it can use the class `OAuth2PasswordBearer` (declared in a dependency) to define the security scheme in OpenAPI because it inherits from `fastapi.security.oauth2.OAuth2`, which in turn inherits from `fastapi.security.base.SecurityBase`.
All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from `SecurityBase`, that's how **FastAPI** can know how to integrate them in OpenAPI.
///
## What it does { #what-it-does }
It will go and look in the request for that `Authorization` header, check if the value is `Bearer ` plus some token, and will return the token as a `str`.
If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer ` token, it will respond with a 401 status code error (`UNAUTHORIZED`) directly.
You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a `str` in that token.
You can try it already in the interactive docs:
We are not verifying the validity of the token yet, but that's a start already.
## Recap { #recap }
So, in just 3 or 4 extra lines, you already have some primitive form of security.
================================================
FILE: docs/en/docs/tutorial/security/get-current-user.md
================================================
# Get Current User { #get-current-user }
In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
But that is still not that useful.
Let's make it give us the current user.
## Create a user model { #create-a-user-model }
First, let's create a Pydantic user model.
The same way we use Pydantic to declare bodies, we can use it anywhere else:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Create a `get_current_user` dependency { #create-a-get-current-user-dependency }
Let's create a dependency `get_current_user`.
Remember that dependencies can have sub-dependencies?
`get_current_user` will have a dependency with the same `oauth2_scheme` we created before.
The same as we were doing before in the *path operation* directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Get the user { #get-the-user }
`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Inject the current user { #inject-the-current-user }
So now we can use the same `Depends` with our `get_current_user` in the *path operation*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Notice that we declare the type of `current_user` as the Pydantic model `User`.
This will help us inside of the function with all the completion and type checks.
/// tip
You might remember that request bodies are also declared with Pydantic models.
Here **FastAPI** won't get confused because you are using `Depends`.
///
/// check
The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return a `User` model.
We are not restricted to having only one dependency that can return that type of data.
///
## Other models { #other-models }
You can now get the current user directly in the *path operation functions* and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`.
And you can use any model or data for the security requirements (in this case, a Pydantic model `User`).
But you are not restricted to using some specific data model, class or type.
Do you want to have an `id` and `email` and not have any `username` in your model? Sure. You can use these same tools.
Do you want to just have a `str`? Or just a `dict`? Or a database class model instance directly? It all works the same way.
You actually don't have users that log in to your application but robots, bots, or other systems, that have just an access token? Again, it all works the same.
Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system.
## Code size { #code-size }
This example might seem verbose. Keep in mind that we are mixing security, data models, utility functions and *path operations* in the same file.
But here's the key point.
The security and dependency injection stuff is written once.
And you can make it as complex as you want. And still, have it written only once, in a single place. With all the flexibility.
But you can have thousands of endpoints (*path operations*) using the same security system.
And all of them (or any portion of them that you want) can take advantage of re-using these dependencies or any other dependencies you create.
And all these thousands of *path operations* can be as small as 3 lines:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Recap { #recap }
You can now get the current user directly in your *path operation function*.
We are already halfway there.
We just need to add a *path operation* for the user/client to actually send the `username` and `password`.
That comes next.
================================================
FILE: docs/en/docs/tutorial/security/index.md
================================================
# Security { #security }
There are many ways to handle security, authentication and authorization.
And it normally is a complex and "difficult" topic.
In many frameworks and systems just handling security and authentication takes a big amount of effort and code (in many cases it can be 50% or more of all the code written).
**FastAPI** provides several tools to help you deal with **Security** easily, rapidly, in a standard way, without having to study and learn all the security specifications.
But first, let's check some small concepts.
## In a hurry? { #in-a-hurry }
If you don't care about any of these terms and you just need to add security with authentication based on username and password *right now*, skip to the next chapters.
## OAuth2 { #oauth2 }
OAuth2 is a specification that defines several ways to handle authentication and authorization.
It is quite an extensive specification and covers several complex use cases.
It includes ways to authenticate using a "third party".
That's what all the systems with "login with Facebook, Google, X (Twitter), GitHub" use underneath.
### OAuth 1 { #oauth-1 }
There was an OAuth 1, which is very different from OAuth2, and more complex, as it included direct specifications on how to encrypt the communication.
It is not very popular or used nowadays.
OAuth2 doesn't specify how to encrypt the communication, it expects you to have your application served with HTTPS.
/// tip
In the section about **deployment** you will see how to set up HTTPS for free, using Traefik and Let's Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect is another specification, based on **OAuth2**.
It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to make it more interoperable.
For example, Google login uses OpenID Connect (which underneath uses OAuth2).
But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2.
### OpenID (not "OpenID Connect") { #openid-not-openid-connect }
There was also an "OpenID" specification. That tried to solve the same thing as **OpenID Connect**, but was not based on OAuth2.
So, it was a complete additional system.
It is not very popular or used nowadays.
## OpenAPI { #openapi }
OpenAPI (previously known as Swagger) is the open specification for building APIs (now part of the Linux Foundation).
**FastAPI** is based on **OpenAPI**.
That's what makes it possible to have multiple automatic interactive documentation interfaces, code generation, etc.
OpenAPI has a way to define multiple security "schemes".
By using them, you can take advantage of all these standard-based tools, including these interactive documentation systems.
OpenAPI defines the following security schemes:
* `apiKey`: an application specific key that can come from:
* A query parameter.
* A header.
* A cookie.
* `http`: standard HTTP authentication systems, including:
* `bearer`: a header `Authorization` with a value of `Bearer ` plus a token. This is inherited from OAuth2.
* HTTP Basic authentication.
* HTTP Digest, etc.
* `oauth2`: all the OAuth2 ways to handle security (called "flows").
* Several of these flows are appropriate for building an OAuth 2.0 authentication provider (like Google, Facebook, X (Twitter), GitHub, etc):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* But there is one specific "flow" that can be perfectly used for handling authentication in the same application directly:
* `password`: some next chapters will cover examples of this.
* `openIdConnect`: has a way to define how to discover OAuth2 authentication data automatically.
* This automatic discovery is what is defined in the OpenID Connect specification.
/// tip
Integrating other authentication/authorization providers like Google, Facebook, X (Twitter), GitHub, etc. is also possible and relatively easy.
The most complex problem is building an authentication/authorization provider like those, but **FastAPI** gives you the tools to do it easily, while doing the heavy lifting for you.
///
## **FastAPI** utilities { #fastapi-utilities }
FastAPI provides several tools for each of these security schemes in the `fastapi.security` module that simplify using these security mechanisms.
In the next chapters you will see how to add security to your API using those tools provided by **FastAPI**.
And you will also see how it gets automatically integrated into the interactive documentation system.
================================================
FILE: docs/en/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 with Password (and hashing), Bearer with JWT tokens { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing.
This code is something you can actually use in your application, save the password hashes in your database, etc.
We are going to start from where we left in the previous chapter and increment it.
## About JWT { #about-jwt }
JWT means "JSON Web Tokens".
It's a standard to codify a JSON object in a long dense string without spaces. It looks like this:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
It is not encrypted, so, anyone could recover the information from the contents.
But it's signed. So, when you receive a token that you emitted, you can verify that you actually emitted it.
That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know that user is still logged in to your system.
After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match.
If you want to play with JWT tokens and see how they work, check [https://jwt.io](https://jwt.io/).
## Install `PyJWT` { #install-pyjwt }
We need to install `PyJWT` to generate and verify the JWT tokens in Python.
Make sure you create a [virtual environment](../../virtual-environments.md), activate it, and then install `pyjwt`:
```console
$ pip install pyjwt
---> 100%
```
/// info
If you are planning to use digital signature algorithms like RSA or ECDSA, you should install the cryptography library dependency `pyjwt[crypto]`.
You can read more about it in the [PyJWT Installation docs](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Password hashing { #password-hashing }
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.
Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish.
But you cannot convert from the gibberish back to the password.
### Why use password hashing { #why-use-password-hashing }
If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes.
So, the thief won't be able to try to use that password in another system (as many users use the same password everywhere, this would be dangerous).
## Install `pwdlib` { #install-pwdlib }
pwdlib is a great Python package to handle password hashes.
It supports many secure hashing algorithms and utilities to work with them.
The recommended algorithm is "Argon2".
Make sure you create a [virtual environment](../../virtual-environments.md), activate it, and then install pwdlib with Argon2:
/// tip
With `pwdlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others.
So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database.
And your users would be able to login from your Django app or from your **FastAPI** app, at the same time.
///
## Hash and verify the passwords { #hash-and-verify-the-passwords }
Import the tools we need from `pwdlib`.
Create a PasswordHash instance with recommended settings - it will be used for hashing and verifying passwords.
/// tip
pwdlib also supports the bcrypt hashing algorithm but does not include legacy algorithms - for working with outdated hashes, it is recommended to use the passlib library.
For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Argon2 or Bcrypt.
And be compatible with all of them at the same time.
///
Create a utility function to hash a password coming from the user.
And another utility to verify if a received password matches the hash stored.
And another one to authenticate and return a user.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
When `authenticate_user` is called with a username that doesn't exist in the database, we still run `verify_password` against a dummy hash.
This ensures the endpoint takes roughly the same amount of time to respond whether the username is valid or not, preventing **timing attacks** that could be used to enumerate existing usernames.
/// note
If you check the new (fake) database `fake_users_db`, you will see how the hashed password looks like now: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Handle JWT tokens { #handle-jwt-tokens }
Import the modules installed.
Create a random secret key that will be used to sign the JWT tokens.
To generate a secure random secret key use the command:
And copy the output to the variable `SECRET_KEY` (don't use the one in the example).
Create a variable `ALGORITHM` with the algorithm used to sign the JWT token and set it to `"HS256"`.
Create a variable for the expiration of the token.
Define a Pydantic Model that will be used in the token endpoint for the response.
Create a utility function to generate a new access token.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Update the dependencies { #update-the-dependencies }
Update `get_current_user` to receive the same token as before, but this time, using JWT tokens.
Decode the received token, verify it, and return the current user.
If the token is invalid, return an HTTP error right away.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Update the `/token` *path operation* { #update-the-token-path-operation }
Create a `timedelta` with the expiration time of the token.
Create a real JWT access token and return it.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Technical details about the JWT "subject" `sub` { #technical-details-about-the-jwt-subject-sub }
The JWT specification says that there's a key `sub`, with the subject of the token.
It's optional to use it, but that's where you would put the user's identification, so we are using it here.
JWT might be used for other things apart from identifying a user and allowing them to perform operations directly on your API.
For example, you could identify a "car" or a "blog post".
Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog).
And then, you could give that JWT token to a user (or bot), and they could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that.
Using these ideas, JWT can be used for way more sophisticated scenarios.
In those cases, several of those entities could have the same ID, let's say `foo` (a user `foo`, a car `foo`, and a blog post `foo`).
So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the `sub` key, e.g. with `username:`. So, in this example, the value of `sub` could have been: `username:johndoe`.
The important thing to keep in mind is that the `sub` key should have a unique identifier across the entire application, and it should be a string.
## Check it { #check-it }
Run the server and go to the docs: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
You'll see the user interface like:
Authorize the application the same way as before.
Using the credentials:
Username: `johndoe`
Password: `secret`
/// check
Notice that nowhere in the code is the plaintext password "`secret`", we only have the hashed version.
///
Call the endpoint `/users/me/`, you will get the response as:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
If you open the developer tools, you could see how the data sent only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards:
/// note
Notice the header `Authorization`, with a value that starts with `Bearer `.
///
## Advanced usage with `scopes` { #advanced-usage-with-scopes }
OAuth2 has the notion of "scopes".
You can use them to add a specific set of permissions to a JWT token.
Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions.
You can learn how to use them and how they are integrated into **FastAPI** later in the **Advanced User Guide**.
## Recap { #recap }
With what you have seen up to now, you can set up a secure **FastAPI** application using standards like OAuth2 and JWT.
In almost any framework handling the security becomes a rather complex subject quite quickly.
Many packages that simplify it a lot have to make many compromises with the data model, database, and available features. And some of these packages that simplify things too much actually have security flaws underneath.
---
**FastAPI** doesn't make any compromise with any database, data model or tool.
It gives you all the flexibility to choose the ones that fit your project the best.
And you can use directly many well maintained and widely used packages like `pwdlib` and `PyJWT`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security.
And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way.
You can learn more in the **Advanced User Guide** about how to use OAuth2 "scopes", for a more fine-grained permission system, following these same standards. OAuth2 with scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, X (Twitter), etc. to authorize third party applications to interact with their APIs on behalf of their users.
================================================
FILE: docs/en/docs/tutorial/security/simple-oauth2.md
================================================
# Simple OAuth2 with Password and Bearer { #simple-oauth2-with-password-and-bearer }
Now let's build from the previous chapter and add the missing parts to have a complete security flow.
## Get the `username` and `password` { #get-the-username-and-password }
We are going to use **FastAPI** security utilities to get the `username` and `password`.
OAuth2 specifies that when using the "password flow" (that we are using) the client/user must send a `username` and `password` fields as form data.
And the spec says that the fields have to be named like that. So `user-name` or `email` wouldn't work.
But don't worry, you can show it as you wish to your final users in the frontend.
And your database models can use any other names you want.
But for the login *path operation*, we need to use these names to be compatible with the spec (and be able to, for example, use the integrated API documentation system).
The spec also states that the `username` and `password` must be sent as form data (so, no JSON here).
### `scope` { #scope }
The spec also says that the client can send another form field "`scope`".
The form field name is `scope` (in singular), but it is actually a long string with "scopes" separated by spaces.
Each "scope" is just a string (without spaces).
They are normally used to declare specific security permissions, for example:
* `users:read` or `users:write` are common examples.
* `instagram_basic` is used by Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` is used by Google.
/// info
In OAuth2 a "scope" is just a string that declares a specific permission required.
It doesn't matter if it has other characters like `:` or if it is a URL.
Those details are implementation specific.
For OAuth2 they are just strings.
///
## Code to get the `username` and `password` { #code-to-get-the-username-and-password }
Now let's use the utilities provided by **FastAPI** to handle this.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` is a class dependency that declares a form body with:
* The `username`.
* The `password`.
* An optional `scope` field as a big string, composed of strings separated by spaces.
* An optional `grant_type`.
/// tip
The OAuth2 spec actually *requires* a field `grant_type` with a fixed value of `password`, but `OAuth2PasswordRequestForm` doesn't enforce it.
If you need to enforce it, use `OAuth2PasswordRequestFormStrict` instead of `OAuth2PasswordRequestForm`.
///
* An optional `client_id` (we don't need it for our example).
* An optional `client_secret` (we don't need it for our example).
/// info
The `OAuth2PasswordRequestForm` is not a special class for **FastAPI** as is `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` makes **FastAPI** know that it is a security scheme. So it is added that way to OpenAPI.
But `OAuth2PasswordRequestForm` is just a class dependency that you could have written yourself, or you could have declared `Form` parameters directly.
But as it's a common use case, it is provided by **FastAPI** directly, just to make it easier.
///
### Use the form data { #use-the-form-data }
/// tip
The instance of the dependency class `OAuth2PasswordRequestForm` won't have an attribute `scope` with the long string separated by spaces, instead, it will have a `scopes` attribute with the actual list of strings for each scope sent.
We are not using `scopes` in this example, but the functionality is there if you need it.
///
Now, get the user data from the (fake) database, using the `username` from the form field.
If there is no such user, we return an error saying "Incorrect username or password".
For the error, we use the exception `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Check the password { #check-the-password }
At this point we have the user data from our database, but we haven't checked the password.
Let's put that data in the Pydantic `UserInDB` model first.
You should never save plaintext passwords, so, we'll use the (fake) password hashing system.
If the passwords don't match, we return the same error.
#### Password hashing { #password-hashing }
"Hashing" means: converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.
Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish.
But you cannot convert from the gibberish back to the password.
##### Why use password hashing { #why-use-password-hashing }
If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes.
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### About `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` means:
*Pass the keys and values of the `user_dict` directly as key-value arguments, equivalent to:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info
For a more complete explanation of `**user_dict` check back in [the documentation for **Extra Models**](../extra-models.md#about-user-in-dict).
///
## Return the token { #return-the-token }
The response of the `token` endpoint must be a JSON object.
It should have a `token_type`. In our case, as we are using "Bearer" tokens, the token type should be "`bearer`".
And it should have an `access_token`, with a string containing our access token.
For this simple example, we are going to just be completely insecure and return the same `username` as the token.
/// tip
In the next chapter, you will see a real secure implementation, with password hashing and JWT tokens.
But for now, let's focus on the specific details we need.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip
By the spec, you should return a JSON with an `access_token` and a `token_type`, the same as in this example.
This is something that you have to do yourself in your code, and make sure you use those JSON keys.
It's almost the only thing that you have to remember to do correctly yourself, to be compliant with the specifications.
For the rest, **FastAPI** handles it for you.
///
## Update the dependencies { #update-the-dependencies }
Now we are going to update our dependencies.
We want to get the `current_user` *only* if this user is active.
So, we create an additional dependency `get_current_active_user` that in turn uses `get_current_user` as a dependency.
Both of these dependencies will just return an HTTP error if the user doesn't exist, or if is inactive.
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info
The additional header `WWW-Authenticate` with value `Bearer` we are returning here is also part of the spec.
Any HTTP (error) status code 401 "UNAUTHORIZED" is supposed to also return a `WWW-Authenticate` header.
In the case of bearer tokens (our case), the value of that header should be `Bearer`.
You can actually skip that extra header and it would still work.
But it's provided here to be compliant with the specifications.
Also, there might be tools that expect and use it (now or in the future) and that might be useful for you or your users, now or in the future.
That's the benefit of standards...
///
## See it in action { #see-it-in-action }
Open the interactive docs: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Authenticate { #authenticate }
Click the "Authorize" button.
Use the credentials:
User: `johndoe`
Password: `secret`
After authenticating in the system, you will see it like:
### Get your own user data { #get-your-own-user-data }
Now use the operation `GET` with the path `/users/me`.
You will get your user's data, like:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
If you click the lock icon and logout, and then try the same operation again, you will get an HTTP 401 error of:
```JSON
{
"detail": "Not authenticated"
}
```
### Inactive user { #inactive-user }
Now try with an inactive user, authenticate with:
User: `alice`
Password: `secret2`
And try to use the operation `GET` with the path `/users/me`.
You will get an "Inactive user" error, like:
```JSON
{
"detail": "Inactive user"
}
```
## Recap { #recap }
You now have the tools to implement a complete security system based on `username` and `password` for your API.
Using these tools, you can make the security system compatible with any database and with any user or data model.
The only detail missing is that it is not actually "secure" yet.
In the next chapter you'll see how to use a secure password hashing library and JWT tokens.
================================================
FILE: docs/en/docs/tutorial/server-sent-events.md
================================================
# Server-Sent Events (SSE) { #server-sent-events-sse }
You can stream data to the client using **Server-Sent Events** (SSE).
This is similar to [Stream JSON Lines](stream-json-lines.md), but uses the `text/event-stream` format, which is supported natively by browsers with the [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info
Added in FastAPI 0.135.0.
///
## What are Server-Sent Events? { #what-are-server-sent-events }
SSE is a standard for streaming data from the server to the client over HTTP.
Each event is a small text block with "fields" like `data`, `event`, `id`, and `retry`, separated by blank lines.
It looks like this:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE is commonly used for AI chat streaming, live notifications, logs and observability, and other cases where the server pushes updates to the client.
/// tip
If you want to stream binary data, for example video or audio, check the advanced guide: [Stream Data](../advanced/stream-data.md).
///
## Stream SSE with FastAPI { #stream-sse-with-fastapi }
To stream SSE with FastAPI, use `yield` in your *path operation function* and set `response_class=EventSourceResponse`.
Import `EventSourceResponse` from `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Each yielded item is encoded as JSON and sent in the `data:` field of an SSE event.
If you declare the return type as `AsyncIterable[Item]`, FastAPI will use it to **validate**, **document**, and **serialize** the data using Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip
As Pydantic will serialize it in the **Rust** side, you will get much higher **performance** than if you don't declare a return type.
///
### Non-async *path operation functions* { #non-async-path-operation-functions }
You can also use regular `def` functions (without `async`), and use `yield` the same way.
FastAPI will make sure it's run correctly so that it doesn't block the event loop.
As in this case the function is not async, the right return type would be `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### No Return Type { #no-return-type }
You can also omit the return type. FastAPI will use the [`jsonable_encoder`](./encoder.md) to convert the data and send it.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
If you need to set SSE fields like `event`, `id`, `retry`, or `comment`, you can yield `ServerSentEvent` objects instead of plain data.
Import `ServerSentEvent` from `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
The `data` field is always encoded as JSON. You can pass any value that can be serialized as JSON, including Pydantic models.
## Raw Data { #raw-data }
If you need to send data **without** JSON encoding, use `raw_data` instead of `data`.
This is useful for sending pre-formatted text, log lines, or special "sentinel" values like `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note
`data` and `raw_data` are mutually exclusive. You can only set one of them on each `ServerSentEvent`.
///
## Resuming with `Last-Event-ID` { #resuming-with-last-event-id }
When a browser reconnects after a connection drop, it sends the last received `id` in the `Last-Event-ID` header.
You can read it as a header parameter and use it to resume the stream from where the client left off:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE with POST { #sse-with-post }
SSE works with **any HTTP method**, not just `GET`.
This is useful for protocols like [MCP](https://modelcontextprotocol.io) that stream SSE over `POST`:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Technical Details { #technical-details }
FastAPI implements some SSE best practices out of the box.
* Send a **"keep alive" `ping` comment** every 15 seconds when there hasn't been any message, to prevent some proxies from closing the connection, as suggested in the [HTML specification: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
* Set the `Cache-Control: no-cache` header to **prevent caching** of the stream.
* Set a special header `X-Accel-Buffering: no` to **prevent buffering** in some proxies like Nginx.
You don't have to do anything about it, it works out of the box. 🤓
================================================
FILE: docs/en/docs/tutorial/sql-databases.md
================================================
# SQL (Relational) Databases { #sql-relational-databases }
**FastAPI** doesn't require you to use a SQL (relational) database. But you can use **any database** that you want.
Here we'll see an example using [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** is built on top of [SQLAlchemy](https://www.sqlalchemy.org/) and Pydantic. It was made by the same author of **FastAPI** to be the perfect match for FastAPI applications that need to use **SQL databases**.
/// tip
You could use any other SQL or NoSQL database library you want (in some cases called "ORMs"), FastAPI doesn't force you to use anything. 😎
///
As SQLModel is based on SQLAlchemy, you can easily use **any database supported** by SQLAlchemy (which makes them also supported by SQLModel), like:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, etc.
In this example, we'll use **SQLite**, because it uses a single file and Python has integrated support. So, you can copy this example and run it as is.
Later, for your production application, you might want to use a database server like **PostgreSQL**.
/// tip
There is an official project generator with **FastAPI** and **PostgreSQL** including a frontend and more tools: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
This is a very simple and short tutorial, if you want to learn about databases in general, about SQL, or more advanced features, go to the [SQLModel docs](https://sqlmodel.tiangolo.com/).
## Install `SQLModel` { #install-sqlmodel }
First, make sure you create your [virtual environment](../virtual-environments.md), activate it, and then install `sqlmodel`:
```console
$ pip install sqlmodel
---> 100%
```
## Create the App with a Single Model { #create-the-app-with-a-single-model }
We'll create the simplest first version of the app with a single **SQLModel** model first.
Later we'll improve it increasing security and versatility with **multiple models** below. 🤓
### Create Models { #create-models }
Import `SQLModel` and create a database model:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
The `Hero` class is very similar to a Pydantic model (in fact, underneath, it actually *is a Pydantic model*).
There are a few differences:
* `table=True` tells SQLModel that this is a *table model*, it should represent a **table** in the SQL database, it's not just a *data model* (as would be any other regular Pydantic class).
* `Field(primary_key=True)` tells SQLModel that the `id` is the **primary key** in the SQL database (you can learn more about SQL primary keys in the SQLModel docs).
**Note:** We use `int | None` for the primary key field so that in Python code we can *create an object without an `id`* (`id=None`), assuming the database will *generate it when saving*. SQLModel understands that the database will provide the `id` and *defines the column as a non-null `INTEGER`* in the database schema. See [SQLModel docs on primary keys](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) for details.
* `Field(index=True)` tells SQLModel that it should create a **SQL index** for this column, that would allow faster lookups in the database when reading data filtered by this column.
SQLModel will know that something declared as `str` will be a SQL column of type `TEXT` (or `VARCHAR`, depending on the database).
### Create an Engine { #create-an-engine }
A SQLModel `engine` (underneath it's actually a SQLAlchemy `engine`) is what **holds the connections** to the database.
You would have **one single `engine` object** for all your code to connect to the same database.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Using `check_same_thread=False` allows FastAPI to use the same SQLite database in different threads. This is necessary as **one single request** could use **more than one thread** (for example in dependencies).
Don't worry, with the way the code is structured, we'll make sure we use **a single SQLModel *session* per request** later, this is actually what the `check_same_thread` is trying to achieve.
### Create the Tables { #create-the-tables }
We then add a function that uses `SQLModel.metadata.create_all(engine)` to **create the tables** for all the *table models*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Create a Session Dependency { #create-a-session-dependency }
A **`Session`** is what stores the **objects in memory** and keeps track of any changes needed in the data, then it **uses the `engine`** to communicate with the database.
We will create a FastAPI **dependency** with `yield` that will provide a new `Session` for each request. This is what ensures that we use a single session per request. 🤓
Then we create an `Annotated` dependency `SessionDep` to simplify the rest of the code that will use this dependency.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Create Database Tables on Startup { #create-database-tables-on-startup }
We will create the database tables when the application starts.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Here we create the tables on an application startup event.
For production you would probably use a migration script that runs before you start your app. 🤓
/// tip
SQLModel will have migration utilities wrapping Alembic, but for now, you can use [Alembic](https://alembic.sqlalchemy.org/en/latest/) directly.
///
### Create a Hero { #create-a-hero }
Because each SQLModel model is also a Pydantic model, you can use it in the same **type annotations** that you could use Pydantic models.
For example, if you declare a parameter of type `Hero`, it will be read from the **JSON body**.
The same way, you can declare it as the function's **return type**, and then the shape of the data will show up in the automatic API docs UI.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Here we use the `SessionDep` dependency (a `Session`) to add the new `Hero` to the `Session` instance, commit the changes to the database, refresh the data in the `hero`, and then return it.
### Read Heroes { #read-heroes }
We can **read** `Hero`s from the database using a `select()`. We can include a `limit` and `offset` to paginate the results.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Read One Hero { #read-one-hero }
We can **read** a single `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Delete a Hero { #delete-a-hero }
We can also **delete** a `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Run the App { #run-the-app }
You can run the app:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Then go to the `/docs` UI, you will see that **FastAPI** is using these **models** to **document** the API, and it will use them to **serialize** and **validate** the data too.
## Update the App with Multiple Models { #update-the-app-with-multiple-models }
Now let's **refactor** this app a bit to increase **security** and **versatility**.
If you check the previous app, in the UI you can see that, up to now, it lets the client decide the `id` of the `Hero` to create. 😱
We shouldn't let that happen, they could overwrite an `id` we already have assigned in the DB. Deciding the `id` should be done by the **backend** or the **database**, **not by the client**.
Additionally, we create a `secret_name` for the hero, but so far, we are returning it everywhere, that's not very **secret**... 😅
We'll fix these things by adding a few **extra models**. Here's where SQLModel will shine. ✨
### Create Multiple Models { #create-multiple-models }
In **SQLModel**, any model class that has `table=True` is a **table model**.
And any model class that doesn't have `table=True` is a **data model**, these ones are actually just Pydantic models (with a couple of small extra features). 🤓
With SQLModel, we can use **inheritance** to **avoid duplicating** all the fields in all the cases.
#### `HeroBase` - the base class { #herobase-the-base-class }
Let's start with a `HeroBase` model that has all the **fields that are shared** by all the models:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - the *table model* { #hero-the-table-model }
Then let's create `Hero`, the actual *table model*, with the **extra fields** that are not always in the other models:
* `id`
* `secret_name`
Because `Hero` inherits form `HeroBase`, it **also** has the **fields** declared in `HeroBase`, so all the fields for `Hero` are:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - the public *data model* { #heropublic-the-public-data-model }
Next, we create a `HeroPublic` model, this is the one that will be **returned** to the clients of the API.
It has the same fields as `HeroBase`, so it won't include `secret_name`.
Finally, the identity of our heroes is protected! 🥷
It also re-declares `id: int`. By doing this, we are making a **contract** with the API clients, so that they can always expect the `id` to be there and to be an `int` (it will never be `None`).
/// tip
Having the return model ensure that a value is always available and always `int` (not `None`) is very useful for the API clients, they can write much simpler code having this certainty.
Also, **automatically generated clients** will have simpler interfaces, so that the developers communicating with your API can have a much better time working with your API. 😎
///
All the fields in `HeroPublic` are the same as in `HeroBase`, with `id` declared as `int` (not `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - the *data model* to create a hero { #herocreate-the-data-model-to-create-a-hero }
Now we create a `HeroCreate` model, this is the one that will **validate** the data from the clients.
It has the same fields as `HeroBase`, and it also has `secret_name`.
Now, when the clients **create a new hero**, they will send the `secret_name`, it will be stored in the database, but those secret names won't be returned in the API to the clients.
/// tip
This is how you would handle **passwords**. Receive them, but don't return them in the API.
You would also **hash** the values of the passwords before storing them, **never store them in plain text**.
///
The fields of `HeroCreate` are:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - the *data model* to update a hero { #heroupdate-the-data-model-to-update-a-hero }
We didn't have a way to **update a hero** in the previous version of the app, but now with **multiple models**, we can do it. 🎉
The `HeroUpdate` *data model* is somewhat special, it has **all the same fields** that would be needed to create a new hero, but all the fields are **optional** (they all have a default value). This way, when you update a hero, you can send just the fields that you want to update.
Because all the **fields actually change** (the type now includes `None` and they now have a default value of `None`), we need to **re-declare** them.
We don't really need to inherit from `HeroBase` because we are re-declaring all the fields. I'll leave it inheriting just for consistency, but this is not necessary. It's more a matter of personal taste. 🤷
The fields of `HeroUpdate` are:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Create with `HeroCreate` and return a `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Now that we have **multiple models**, we can update the parts of the app that use them.
We receive in the request a `HeroCreate` *data model*, and from it, we create a `Hero` *table model*.
This new *table model* `Hero` will have the fields sent by the client, and will also have an `id` generated by the database.
Then we return the same *table model* `Hero` as is from the function. But as we declare the `response_model` with the `HeroPublic` *data model*, **FastAPI** will use `HeroPublic` to validate and serialize the data.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip
Now we use `response_model=HeroPublic` instead of the **return type annotation** `-> HeroPublic` because the value that we are returning is actually *not* a `HeroPublic`.
If we had declared `-> HeroPublic`, your editor and linter would complain (rightfully so) that you are returning a `Hero` instead of a `HeroPublic`.
By declaring it in `response_model` we are telling **FastAPI** to do its thing, without interfering with the type annotations and the help from your editor and other tools.
///
### Read Heroes with `HeroPublic` { #read-heroes-with-heropublic }
We can do the same as before to **read** `Hero`s, again, we use `response_model=list[HeroPublic]` to ensure that the data is validated and serialized correctly.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Read One Hero with `HeroPublic` { #read-one-hero-with-heropublic }
We can **read** a single hero:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Update a Hero with `HeroUpdate` { #update-a-hero-with-heroupdate }
We can **update a hero**. For this we use an HTTP `PATCH` operation.
And in the code, we get a `dict` with all the data sent by the client, **only the data sent by the client**, excluding any values that would be there just for being the default values. To do it we use `exclude_unset=True`. This is the main trick. 🪄
Then we use `hero_db.sqlmodel_update(hero_data)` to update the `hero_db` with the data from `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Delete a Hero Again { #delete-a-hero-again }
**Deleting** a hero stays pretty much the same.
We won't satisfy the desire to refactor everything in this one. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Run the App Again { #run-the-app-again }
You can run the app again:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
If you go to the `/docs` API UI, you will see that it is now updated, and it won't expect to receive the `id` from the client when creating a hero, etc.
## Recap { #recap }
You can use [**SQLModel**](https://sqlmodel.tiangolo.com/) to interact with a SQL database and simplify the code with *data models* and *table models*.
You can learn a lot more at the **SQLModel** docs, there's a longer mini [tutorial on using SQLModel with **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/en/docs/tutorial/static-files.md
================================================
# Static Files { #static-files }
You can serve static files automatically from a directory using `StaticFiles`.
## Use `StaticFiles` { #use-staticfiles }
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Technical Details
You could also use `from starlette.staticfiles import StaticFiles`.
**FastAPI** provides the same `starlette.staticfiles` as `fastapi.staticfiles` just as a convenience for you, the developer. But it actually comes directly from Starlette.
///
### What is "Mounting" { #what-is-mounting }
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
This is different from using an `APIRouter` as a mounted application is completely independent. The OpenAPI and docs from your main application won't include anything from the mounted application, etc.
You can read more about this in the [Advanced User Guide](../advanced/index.md).
## Details { #details }
The first `"/static"` refers to the sub-path this "sub-application" will be "mounted" on. So, any path that starts with `"/static"` will be handled by it.
The `directory="static"` refers to the name of the directory that contains your static files.
The `name="static"` gives it a name that can be used internally by **FastAPI**.
All these parameters can be different than "`static`", adjust them with the needs and specific details of your own application.
## More info { #more-info }
For more details and options check [Starlette's docs about Static Files](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/en/docs/tutorial/stream-json-lines.md
================================================
# Stream JSON Lines { #stream-json-lines }
You could have a sequence of data that you would like to send in a "**stream**", you could do it with **JSON Lines**.
/// info
Added in FastAPI 0.134.0.
///
## What is a Stream? { #what-is-a-stream }
"**Streaming**" data means that your app will start sending data items to the client without waiting for the entire sequence of items to be ready.
So, it will send the first item, the client will receive and start processing it, and you might still be producing the next item.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
It could even be an infinite stream, where you keep sending data.
## JSON Lines { #json-lines }
In these cases, it's common to send "**JSON Lines**", which is a format where you send one JSON object per line.
A response would have a content type of `application/jsonl` (instead of `application/json`) and the body would be something like:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
It's very similar to a JSON array (equivalent of a Python list), but instead of being wrapped in `[]` and having `,` between the items, it has **one JSON object per line**, they are separated by a new line character.
/// info
The important point is that your app will be able to produce each line in turn, while the client consumes the previous lines.
///
/// note | Technical Details
Because each JSON object will be separated by a new line, they can't contain literal new line characters in their content, but they can contain escaped new lines (`\n`), which is part of the JSON standard.
But normally you won't have to worry about it, it's done automatically, continue reading. 🤓
///
## Use Cases { #use-cases }
You could use this to stream data from an **AI LLM** service, from **logs** or **telemetry**, or from other types of data that can be structured in **JSON** items.
/// tip
If you want to stream binary data, for example video or audio, check the advanced guide: [Stream Data](../advanced/stream-data.md).
///
## Stream JSON Lines with FastAPI { #stream-json-lines-with-fastapi }
To stream JSON Lines with FastAPI you can, instead of using `return` in your *path operation function*, use `yield` to produce each item in turn.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
If each JSON item you want to send back is of type `Item` (a Pydantic model) and it's an async function, you can declare the return type as `AsyncIterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
If you declare the return type, FastAPI will use it to **validate** the data, **document** it in OpenAPI, **filter** it, and **serialize** it using Pydantic.
/// tip
As Pydantic will serialize it in the **Rust** side, you will get much higher **performance** than if you don't declare a return type.
///
### Non-async *path operation functions* { #non-async-path-operation-functions }
You can also use regular `def` functions (without `async`), and use `yield` the same way.
FastAPI will make sure it's run correctly so that it doesn't block the event loop.
As in this case the function is not async, the right return type would be `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### No Return Type { #no-return-type }
You can also omit the return type. FastAPI will then use the [`jsonable_encoder`](./encoder.md) to convert the data to something that can be serialized to JSON and then send it as JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Server-Sent Events (SSE) { #server-sent-events-sse }
FastAPI also has first-class support for Server-Sent Events (SSE), which are quite similar but with a couple of extra details. You can learn about them in the next chapter: [Server-Sent Events (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/en/docs/tutorial/testing.md
================================================
# Testing { #testing }
Thanks to [Starlette](https://www.starlette.dev/testclient/), testing **FastAPI** applications is easy and enjoyable.
It is based on [HTTPX](https://www.python-httpx.org), which in turn is designed based on Requests, so it's very familiar and intuitive.
With it, you can use [pytest](https://docs.pytest.org/) directly with **FastAPI**.
## Using `TestClient` { #using-testclient }
/// info
To use `TestClient`, first install [`httpx`](https://www.python-httpx.org).
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install httpx
```
///
Import `TestClient`.
Create a `TestClient` by passing your **FastAPI** application to it.
Create functions with a name that starts with `test_` (this is standard `pytest` conventions).
Use the `TestClient` object the same way as you do with `httpx`.
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip
Notice that the testing functions are normal `def`, not `async def`.
And the calls to the client are also normal calls, not using `await`.
This allows you to use `pytest` directly without complications.
///
/// note | Technical Details
You could also use `from starlette.testclient import TestClient`.
**FastAPI** provides the same `starlette.testclient` as `fastapi.testclient` just as a convenience for you, the developer. But it comes directly from Starlette.
///
/// tip
If you want to call `async` functions in your tests apart from sending requests to your FastAPI application (e.g. asynchronous database functions), have a look at the [Async Tests](../advanced/async-tests.md) in the advanced tutorial.
///
## Separating tests { #separating-tests }
In a real application, you probably would have your tests in a different file.
And your **FastAPI** application might also be composed of several files/modules, etc.
### **FastAPI** app file { #fastapi-app-file }
Let's say you have a file structure as described in [Bigger Applications](bigger-applications.md):
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
In the file `main.py` you have your **FastAPI** app:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Testing file { #testing-file }
Then you could have a file `test_main.py` with your tests. It could live on the same Python package (the same directory with a `__init__.py` file):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`):
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...and have the code for the tests just like before.
## Testing: extended example { #testing-extended-example }
Now let's extend this example and add more details to see how to test different parts.
### Extended **FastAPI** app file { #extended-fastapi-app-file }
Let's continue with the same file structure as before:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Let's say that now the file `main.py` with your **FastAPI** app has some other **path operations**.
It has a `GET` operation that could return an error.
It has a `POST` operation that could return several errors.
Both *path operations* require an `X-Token` header.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Extended testing file { #extended-testing-file }
You could then update `test_main.py` with the extended tests:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Whenever you need the client to pass information in the request and you don't know how to, you can search (Google) how to do it in `httpx`, or even how to do it with `requests`, as HTTPX's design is based on Requests' design.
Then you just do the same in your tests.
E.g.:
* To pass a *path* or *query* parameter, add it to the URL itself.
* To pass a JSON body, pass a Python object (e.g. a `dict`) to the parameter `json`.
* If you need to send *Form Data* instead of JSON, use the `data` parameter instead.
* To pass *headers*, use a `dict` in the `headers` parameter.
* For *cookies*, a `dict` in the `cookies` parameter.
For more information about how to pass data to the backend (using `httpx` or the `TestClient`) check the [HTTPX documentation](https://www.python-httpx.org).
/// info
Note that the `TestClient` receives data that can be converted to JSON, not Pydantic models.
If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the `jsonable_encoder` described in [JSON Compatible Encoder](encoder.md).
///
## Run it { #run-it }
After that, you just need to install `pytest`.
Make sure you create a [virtual environment](../virtual-environments.md), activate it, and then install it, for example:
```console
$ pip install pytest
---> 100%
```
It will detect the files and tests automatically, execute them, and report the results back to you.
Run the tests with:
================================================
FILE: docs/en/docs/virtual-environments.md
================================================
# Virtual Environments { #virtual-environments }
When you work in Python projects you probably should use a **virtual environment** (or a similar mechanism) to isolate the packages you install for each project.
/// info
If you already know about virtual environments, how to create them and use them, you might want to skip this section. 🤓
///
/// tip
A **virtual environment** is different than an **environment variable**.
An **environment variable** is a variable in the system that can be used by programs.
A **virtual environment** is a directory with some files in it.
///
/// info
This page will teach you how to use **virtual environments** and how they work.
If you are ready to adopt a **tool that manages everything** for you (including installing Python), try [uv](https://github.com/astral-sh/uv).
///
## Create a Project { #create-a-project }
First, create a directory for your project.
What I normally do is that I create a directory named `code` inside my home/user directory.
And inside of that I create one directory per project.
```console
// Go to the home directory
$ cd
// Create a directory for all your code projects
$ mkdir code
// Enter into that code directory
$ cd code
// Create a directory for this project
$ mkdir awesome-project
// Enter into that project directory
$ cd awesome-project
```
## Create a Virtual Environment { #create-a-virtual-environment }
When you start working on a Python project **for the first time**, create a virtual environment **inside your project**.
/// tip
You only need to do this **once per project**, not every time you work.
///
//// tab | `venv`
To create a virtual environment, you can use the `venv` module that comes with Python.
```console
$ python -m venv .venv
```
/// details | What that command means
* `python`: use the program called `python`
* `-m`: call a module as a script, we'll tell it which module next
* `venv`: use the module called `venv` that normally comes installed with Python
* `.venv`: create the virtual environment in the new directory `.venv`
///
////
//// tab | `uv`
If you have [`uv`](https://github.com/astral-sh/uv) installed, you can use it to create a virtual environment.
```console
$ uv venv
```
/// tip
By default, `uv` will create a virtual environment in a directory called `.venv`.
But you could customize it passing an additional argument with the directory name.
///
////
That command creates a new virtual environment in a directory called `.venv`.
/// details | `.venv` or other name
You could create the virtual environment in a different directory, but there's a convention of calling it `.venv`.
///
## Activate the Virtual Environment { #activate-the-virtual-environment }
Activate the new virtual environment so that any Python command you run or package you install uses it.
/// tip
Do this **every time** you start a **new terminal session** to work on the project.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Or if you use Bash for Windows (e.g. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip
Every time you install a **new package** in that environment, **activate** the environment again.
This makes sure that if you use a **terminal (CLI) program** installed by that package, you use the one from your virtual environment and not any other that could be installed globally, probably with a different version than what you need.
///
## Check the Virtual Environment is Active { #check-the-virtual-environment-is-active }
Check that the virtual environment is active (the previous command worked).
/// tip
This is **optional**, but it's a good way to **check** that everything is working as expected and you are using the virtual environment you intended.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
If it shows the `python` binary at `.venv/bin/python`, inside of your project (in this case `awesome-project`), then it worked. 🎉
////
//// tab | Windows PowerShell
If it shows the `python` binary at `.venv\Scripts\python`, inside of your project (in this case `awesome-project`), then it worked. 🎉
////
## Upgrade `pip` { #upgrade-pip }
/// tip
If you use [`uv`](https://github.com/astral-sh/uv) you would use it to install things instead of `pip`, so you don't need to upgrade `pip`. 😎
///
If you are using `pip` to install packages (it comes by default with Python), you should **upgrade** it to the latest version.
Many exotic errors while installing a package are solved by just upgrading `pip` first.
/// tip
You would normally do this **once**, right after you create the virtual environment.
///
Make sure the virtual environment is active (with the command above) and then run:
/// tip
Sometimes, you might get a **`No module named pip`** error when trying to upgrade pip.
If this happens, install and upgrade pip using the command below:
This command will install pip if it is not already installed and also ensures that the installed version of pip is at least as recent as the one available in `ensurepip`.
///
## Add `.gitignore` { #add-gitignore }
If you are using **Git** (you should), add a `.gitignore` file to exclude everything in your `.venv` from Git.
/// tip
If you used [`uv`](https://github.com/astral-sh/uv) to create the virtual environment, it already did this for you, you can skip this step. 😎
///
/// tip
Do this **once**, right after you create the virtual environment.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | What that command means
* `echo "*"`: will "print" the text `*` in the terminal (the next part changes that a bit)
* `>`: anything printed to the terminal by the command to the left of `>` should not be printed but instead written to the file that goes to the right of `>`
* `.gitignore`: the name of the file where the text should be written
And `*` for Git means "everything". So, it will ignore everything in the `.venv` directory.
That command will create a file `.gitignore` with the content:
```gitignore
*
```
///
## Install Packages { #install-packages }
After activating the environment, you can install packages in it.
/// tip
Do this **once** when installing or upgrading the packages your project needs.
If you need to upgrade a version or add a new package you would **do this again**.
///
### Install Packages Directly { #install-packages-directly }
If you're in a hurry and don't want to use a file to declare your project's package requirements, you can install them directly.
/// tip
It's a (very) good idea to put the packages and versions your program needs in a file (for example `requirements.txt` or `pyproject.toml`).
///
//// tab | `pip`
////
### Install from `requirements.txt` { #install-from-requirements-txt }
If you have a `requirements.txt`, you can now use it to install its packages.
//// tab | `pip`
////
/// details | `requirements.txt`
A `requirements.txt` with some packages could look like:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Run Your Program { #run-your-program }
After you activated the virtual environment, you can run your program, and it will use the Python inside of your virtual environment with the packages you installed there.
```console
$ python main.py
Hello World
```
## Configure Your Editor { #configure-your-editor }
You would probably use an editor, make sure you configure it to use the same virtual environment you created (it will probably autodetect it) so that you can get autocompletion and inline errors.
For example:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip
You normally have to do this only **once**, when you create the virtual environment.
///
## Deactivate the Virtual Environment { #deactivate-the-virtual-environment }
Once you are done working on your project you can **deactivate** the virtual environment.
```console
$ deactivate
```
This way, when you run `python` it won't try to run it from that virtual environment with the packages installed there.
## Ready to Work { #ready-to-work }
Now you're ready to start working on your project.
/// tip
Do you want to understand what's all that above?
Continue reading. 👇🤓
///
## Why Virtual Environments { #why-virtual-environments }
To work with FastAPI you need to install [Python](https://www.python.org/).
After that, you would need to **install** FastAPI and any other **packages** you want to use.
To install packages you would normally use the `pip` command that comes with Python (or similar alternatives).
Nevertheless, if you just use `pip` directly, the packages would be installed in your **global Python environment** (the global installation of Python).
### The Problem { #the-problem }
So, what's the problem with installing packages in the global Python environment?
At some point, you will probably end up writing many different programs that depend on **different packages**. And some of these projects you work on will depend on **different versions** of the same package. 😱
For example, you could create a project called `philosophers-stone`, this program depends on another package called **`harry`, using the version `1`**. So, you need to install `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Then, at some point later, you create another project called `prisoner-of-azkaban`, and this project also depends on `harry`, but this project needs **`harry` version `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
But now the problem is, if you install the packages globally (in the global environment) instead of in a local **virtual environment**, you will have to choose which version of `harry` to install.
If you want to run `philosophers-stone` you will need to first install `harry` version `1`, for example with:
```console
$ pip install "harry==1"
```
And then you would end up with `harry` version `1` installed in your global Python environment.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
But then if you want to run `prisoner-of-azkaban`, you will need to uninstall `harry` version `1` and install `harry` version `3` (or just installing version `3` would automatically uninstall version `1`).
```console
$ pip install "harry==3"
```
And then you would end up with `harry` version `3` installed in your global Python environment.
And if you try to run `philosophers-stone` again, there's a chance it would **not work** because it needs `harry` version `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip
It's very common in Python packages to try the best to **avoid breaking changes** in **new versions**, but it's better to be safe, and install newer versions intentionally and when you can run the tests to check everything is working correctly.
///
Now, imagine that with **many** other **packages** that all your **projects depend on**. That's very difficult to manage. And you would probably end up running some projects with some **incompatible versions** of the packages, and not knowing why something isn't working.
Also, depending on your operating system (e.g. Linux, Windows, macOS), it could have come with Python already installed. And in that case it probably had some packages pre-installed with some specific versions **needed by your system**. If you install packages in the global Python environment, you could end up **breaking** some of the programs that came with your operating system.
## Where are Packages Installed { #where-are-packages-installed }
When you install Python, it creates some directories with some files in your computer.
Some of these directories are the ones in charge of having all the packages you install.
When you run:
```console
// Don't run this now, it's just an example 🤓
$ pip install "fastapi[standard]"
---> 100%
```
That will download a compressed file with the FastAPI code, normally from [PyPI](https://pypi.org/project/fastapi/).
It will also **download** files for other packages that FastAPI depends on.
Then it will **extract** all those files and put them in a directory in your computer.
By default, it will put those files downloaded and extracted in the directory that comes with your Python installation, that's the **global environment**.
## What are Virtual Environments { #what-are-virtual-environments }
The solution to the problems of having all the packages in the global environment is to use a **virtual environment for each project** you work on.
A virtual environment is a **directory**, very similar to the global one, where you can install the packages for a project.
This way, each project will have its own virtual environment (`.venv` directory) with its own packages.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## What Does Activating a Virtual Environment Mean { #what-does-activating-a-virtual-environment-mean }
When you activate a virtual environment, for example with:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Or if you use Bash for Windows (e.g. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
That command will create or modify some [environment variables](environment-variables.md) that will be available for the next commands.
One of those variables is the `PATH` variable.
/// tip
You can learn more about the `PATH` environment variable in the [Environment Variables](environment-variables.md#path-environment-variable) section.
///
Activating a virtual environment adds its path `.venv/bin` (on Linux and macOS) or `.venv\Scripts` (on Windows) to the `PATH` environment variable.
Let's say that before activating the environment, the `PATH` variable looked like this:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
That means that the system would look for programs in:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
That means that the system would look for programs in:
* `C:\Windows\System32`
////
After activating the virtual environment, the `PATH` variable would look something like this:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
That means that the system will now start looking first for programs in:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
before looking in the other directories.
So, when you type `python` in the terminal, the system will find the Python program in
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
and use that one.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
That means that the system will now start looking first for programs in:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
before looking in the other directories.
So, when you type `python` in the terminal, the system will find the Python program in
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
and use that one.
////
An important detail is that it will put the virtual environment path at the **beginning** of the `PATH` variable. The system will find it **before** finding any other Python available. This way, when you run `python`, it will use the Python **from the virtual environment** instead of any other `python` (for example, a `python` from a global environment).
Activating a virtual environment also changes a couple of other things, but this is one of the most important things it does.
## Checking a Virtual Environment { #checking-a-virtual-environment }
When you check if a virtual environment is active, for example with:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
That means that the `python` program that will be used is the one **in the virtual environment**.
You use `which` in Linux and macOS and `Get-Command` in Windows PowerShell.
The way that command works is that it will go and check in the `PATH` environment variable, going through **each path in order**, looking for the program called `python`. Once it finds it, it will **show you the path** to that program.
The most important part is that when you call `python`, that is the exact "`python`" that will be executed.
So, you can confirm if you are in the correct virtual environment.
/// tip
It's easy to activate one virtual environment, get one Python, and then **go to another project**.
And the second project **wouldn't work** because you are using the **incorrect Python**, from a virtual environment for another project.
It's useful being able to check what `python` is being used. 🤓
///
## Why Deactivate a Virtual Environment { #why-deactivate-a-virtual-environment }
For example, you could be working on a project `philosophers-stone`, **activate that virtual environment**, install packages and work with that environment.
And then you want to work on **another project** `prisoner-of-azkaban`.
You go to that project:
```console
$ cd ~/code/prisoner-of-azkaban
```
If you don't deactivate the virtual environment for `philosophers-stone`, when you run `python` in the terminal, it will try to use the Python from `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
But if you deactivate the virtual environment and activate the new one for `prisoner-of-azkaban` then when you run `python` it will use the Python from the virtual environment in `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
$ deactivate
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Now when you run python, it will find the package sirius installed in this virtual environment ✨
$ python main.py
I solemnly swear 🐺
```
## Alternatives { #alternatives }
This is a simple guide to get you started and teach you how everything works **underneath**.
There are many **alternatives** to managing virtual environments, package dependencies (requirements), projects.
Once you are ready and want to use a tool to **manage the entire project**, packages dependencies, virtual environments, etc. I would suggest you try [uv](https://github.com/astral-sh/uv).
`uv` can do a lot of things, it can:
* **Install Python** for you, including different versions
* Manage the **virtual environment** for your projects
* Install **packages**
* Manage package **dependencies and versions** for your project
* Make sure you have an **exact** set of packages and versions to install, including their dependencies, so that you can be sure that you can run your project in production exactly the same as in your computer while developing, this is called **locking**
* And many other things
## Conclusion { #conclusion }
If you read and understood all this, now **you know much more** about virtual environments than many developers out there. 🤓
Knowing these details will most probably be useful in a future time when you are debugging something that seems complex, but you will know **how it all works underneath**. 😎
================================================
FILE: docs/en/mkdocs.env.yml
================================================
# Define this here and not in the main mkdocs.yml file because that one is auto
# updated and written, and the script would remove the env var
markdown_extensions:
pymdownx.highlight:
linenums: !ENV [LINENUMS, false]
================================================
FILE: docs/en/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.env.yml
site_name: FastAPI
site_description: FastAPI framework, high performance, easy to learn, fast to code, ready for production
site_url: https://fastapi.tiangolo.com/
theme:
name: material
custom_dir: ../en/overrides
palette:
- media: (prefers-color-scheme)
toggle:
icon: material/lightbulb-auto
name: Switch to light mode
- media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: amber
toggle:
icon: material/lightbulb
name: Switch to dark mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: amber
toggle:
icon: material/lightbulb-outline
name: Switch to system preference
features:
- content.code.annotate
- content.code.copy
- content.footnote.tooltips
- content.tabs.link
- content.tooltips
- navigation.footer
- navigation.indexes
- navigation.instant
- navigation.instant.prefetch
- navigation.instant.progress
- navigation.path
- navigation.tabs
- navigation.tabs.sticky
- navigation.top
- navigation.tracking
- search.highlight
- search.share
- search.suggest
- toc.follow
icon:
repo: fontawesome/brands/github-alt
logo: img/icon-white.svg
favicon: img/favicon.png
language: en
repo_name: fastapi/fastapi
repo_url: https://github.com/fastapi/fastapi
plugins:
social:
cards_layout_options:
logo: ../en/docs/img/icon-white.svg
typeset: null
search: null
macros:
include_yaml:
- github_sponsors: ../en/data/github_sponsors.yml
- people: ../en/data/people.yml
- contributors: ../en/data/contributors.yml
- translators: ../en/data/translators.yml
- translation_reviewers: ../en/data/translation_reviewers.yml
- skip_users: ../en/data/skip_users.yml
- members: ../en/data/members.yml
- sponsors_badge: ../en/data/sponsors_badge.yml
- sponsors: ../en/data/sponsors.yml
- topic_repos: ../en/data/topic_repos.yml
redirects:
redirect_maps:
deployment/deta.md: deployment/cloud.md
advanced/graphql.md: how-to/graphql.md
advanced/custom-request-and-route.md: how-to/custom-request-and-route.md
advanced/conditional-openapi.md: how-to/conditional-openapi.md
advanced/extending-openapi.md: how-to/extending-openapi.md
advanced/testing-database.md: how-to/testing-database.md
mkdocstrings:
handlers:
python:
options:
extensions:
- griffe_typingdoc
show_root_heading: true
show_if_no_docstring: true
preload_modules:
- httpx
- starlette
inherited_members: true
members_order: source
separate_signature: true
unwrap_annotated: true
filters:
- '!^_'
merge_init_into_class: true
docstring_section_style: spacy
signature_crossrefs: true
show_symbol_type_heading: true
show_symbol_type_toc: true
nav:
- FastAPI: index.md
- features.md
- Learn:
- learn/index.md
- python-types.md
- async.md
- environment-variables.md
- virtual-environments.md
- Tutorial - User Guide:
- tutorial/index.md
- tutorial/first-steps.md
- tutorial/path-params.md
- tutorial/query-params.md
- tutorial/body.md
- tutorial/query-params-str-validations.md
- tutorial/path-params-numeric-validations.md
- tutorial/query-param-models.md
- tutorial/body-multiple-params.md
- tutorial/body-fields.md
- tutorial/body-nested-models.md
- tutorial/schema-extra-example.md
- tutorial/extra-data-types.md
- tutorial/cookie-params.md
- tutorial/header-params.md
- tutorial/cookie-param-models.md
- tutorial/header-param-models.md
- tutorial/response-model.md
- tutorial/extra-models.md
- tutorial/response-status-code.md
- tutorial/request-forms.md
- tutorial/request-form-models.md
- tutorial/request-files.md
- tutorial/request-forms-and-files.md
- tutorial/handling-errors.md
- tutorial/path-operation-configuration.md
- tutorial/encoder.md
- tutorial/body-updates.md
- Dependencies:
- tutorial/dependencies/index.md
- tutorial/dependencies/classes-as-dependencies.md
- tutorial/dependencies/sub-dependencies.md
- tutorial/dependencies/dependencies-in-path-operation-decorators.md
- tutorial/dependencies/global-dependencies.md
- tutorial/dependencies/dependencies-with-yield.md
- Security:
- tutorial/security/index.md
- tutorial/security/first-steps.md
- tutorial/security/get-current-user.md
- tutorial/security/simple-oauth2.md
- tutorial/security/oauth2-jwt.md
- tutorial/middleware.md
- tutorial/cors.md
- tutorial/sql-databases.md
- tutorial/bigger-applications.md
- tutorial/stream-json-lines.md
- tutorial/server-sent-events.md
- tutorial/background-tasks.md
- tutorial/metadata.md
- tutorial/static-files.md
- tutorial/testing.md
- tutorial/debugging.md
- Advanced User Guide:
- advanced/index.md
- advanced/stream-data.md
- advanced/path-operation-advanced-configuration.md
- advanced/additional-status-codes.md
- advanced/response-directly.md
- advanced/custom-response.md
- advanced/additional-responses.md
- advanced/response-cookies.md
- advanced/response-headers.md
- advanced/response-change-status-code.md
- advanced/advanced-dependencies.md
- Advanced Security:
- advanced/security/index.md
- advanced/security/oauth2-scopes.md
- advanced/security/http-basic-auth.md
- advanced/using-request-directly.md
- advanced/dataclasses.md
- advanced/middleware.md
- advanced/sub-applications.md
- advanced/behind-a-proxy.md
- advanced/templates.md
- advanced/websockets.md
- advanced/events.md
- advanced/testing-websockets.md
- advanced/testing-events.md
- advanced/testing-dependencies.md
- advanced/async-tests.md
- advanced/settings.md
- advanced/openapi-callbacks.md
- advanced/openapi-webhooks.md
- advanced/wsgi.md
- advanced/generate-clients.md
- advanced/advanced-python-types.md
- advanced/json-base64-bytes.md
- advanced/strict-content-type.md
- fastapi-cli.md
- editor-support.md
- Deployment:
- deployment/index.md
- deployment/versions.md
- deployment/fastapicloud.md
- deployment/https.md
- deployment/manually.md
- deployment/concepts.md
- deployment/cloud.md
- deployment/server-workers.md
- deployment/docker.md
- How To - Recipes:
- how-to/index.md
- how-to/general.md
- how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
- how-to/graphql.md
- how-to/custom-request-and-route.md
- how-to/conditional-openapi.md
- how-to/extending-openapi.md
- how-to/separate-openapi-schemas.md
- how-to/custom-docs-ui-assets.md
- how-to/configure-swagger-ui.md
- how-to/testing-database.md
- how-to/authentication-error-status-code.md
- Reference (Code API):
- reference/index.md
- reference/fastapi.md
- reference/parameters.md
- reference/status.md
- reference/uploadfile.md
- reference/exceptions.md
- reference/dependencies.md
- reference/apirouter.md
- reference/background.md
- reference/request.md
- reference/websockets.md
- reference/httpconnection.md
- reference/response.md
- reference/responses.md
- reference/middleware.md
- OpenAPI:
- reference/openapi/index.md
- reference/openapi/docs.md
- reference/openapi/models.md
- reference/security/index.md
- reference/encoders.md
- reference/staticfiles.md
- reference/templating.md
- reference/testclient.md
- fastapi-people.md
- Resources:
- resources/index.md
- help-fastapi.md
- contributing.md
- project-generation.md
- external-links.md
- newsletter.md
- management-tasks.md
- About:
- about/index.md
- alternatives.md
- history-design-future.md
- benchmarks.md
- management.md
- release-notes.md
markdown_extensions:
material.extensions.preview:
targets:
include:
- '*'
abbr: null
attr_list: null
footnotes: null
md_in_html: null
tables: null
toc:
permalink: true
pymdownx.betterem: null
pymdownx.caret: null
pymdownx.highlight:
line_spans: __span
linenums_style: pymdownx-inline
pymdownx.inlinehilite: null
pymdownx.keys: null
pymdownx.mark: null
pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format ''
pymdownx.tilde: null
pymdownx.blocks.admonition:
types:
- note
- attention
- caution
- danger
- error
- tip
- hint
- warning
- info
- check
pymdownx.blocks.details: null
pymdownx.blocks.tab:
alternate_style: true
mdx_include: null
markdown_include_variants: null
extra:
social:
- icon: fontawesome/brands/github-alt
link: https://github.com/fastapi/fastapi
- icon: fontawesome/brands/discord
link: https://discord.gg/VQjSZaeJmf
- icon: fontawesome/brands/twitter
link: https://x.com/fastapi
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/fastapi
- icon: fontawesome/solid/globe
link: https://tiangolo.com
alternate:
- link: /
name: en - English
- link: /de/
name: de - Deutsch
- link: /es/
name: es - español
- link: /fr/
name: fr - français
- link: /ja/
name: ja - 日本語
- link: /ko/
name: ko - 한국어
- link: /pt/
name: pt - português
- link: /ru/
name: ru - русский язык
- link: /tr/
name: tr - Türkçe
- link: /uk/
name: uk - українська мова
- link: /zh/
name: zh - 简体中文
- link: /zh-hant/
name: zh-hant - 繁體中文
extra_css:
- css/termynal.css
- css/custom.css
extra_javascript:
- js/termynal.js
- js/custom.js
- js/init_kapa_widget.js
hooks:
- ../../scripts/mkdocs_hooks.py
================================================
FILE: docs/en/overrides/main.html
================================================
{% extends "base.html" %}
{% block announce %}
The FastAPI trademark is owned by @tiangolo and is registered in the US and across other regions
{% if not config.extra.generator == false %}
Made with
Material for MkDocs
{% endif %}
================================================
FILE: docs/es/docs/_llm-test.md
================================================
# Archivo de prueba de LLM { #llm-test-file }
Este documento prueba si el LLM, que traduce la documentación, entiende el `general_prompt` en `scripts/translate.py` y el prompt específico del idioma en `docs/{language code}/llm-prompt.md`. El prompt específico del idioma se agrega al final de `general_prompt`.
Las pruebas añadidas aquí serán vistas por todas las personas que diseñan prompts específicos del idioma.
Úsalo de la siguiente manera:
* Ten un prompt específico del idioma - `docs/{language code}/llm-prompt.md`.
* Haz una traducción fresca de este documento a tu idioma destino (mira, por ejemplo, el comando `translate-page` de `translate.py`). Esto creará la traducción en `docs/{language code}/docs/_llm-test.md`.
* Revisa si las cosas están bien en la traducción.
* Si es necesario, mejora tu prompt específico del idioma, el prompt general, o el documento en inglés.
* Luego corrige manualmente los problemas restantes en la traducción para que sea una buena traducción.
* Vuelve a traducir, teniendo la buena traducción en su lugar. El resultado ideal sería que el LLM ya no hiciera cambios a la traducción. Eso significa que el prompt general y tu prompt específico del idioma están tan bien como pueden estar (A veces hará algunos cambios aparentemente aleatorios; la razón es que [los LLMs no son algoritmos deterministas](https://doublespeak.chat/#/handbook#deterministic-output)).
Las pruebas:
## Fragmentos de código { #code-snippets }
//// tab | Prueba
Este es un fragmento de código: `foo`. Y este es otro fragmento de código: `bar`. Y otro más: `baz quux`.
////
//// tab | Info
El contenido de los fragmentos de código debe dejarse tal cual.
Consulta la sección `### Content of code snippets` en el prompt general en `scripts/translate.py`.
////
## Comillas { #quotes }
//// tab | Prueba
Ayer, mi amigo escribió: "Si escribes 'incorrectly' correctamente, lo habrás escrito incorrectamente". A lo que respondí: "Correcto, pero 'incorrectly' está incorrecto, no '"incorrectly"'".
/// note | Nota
El LLM probablemente traducirá esto mal. Lo interesante es si mantiene la traducción corregida al volver a traducir.
///
////
//// tab | Info
La persona que diseña el prompt puede elegir si quiere convertir comillas neutras a comillas tipográficas. También está bien dejarlas como están.
Consulta por ejemplo la sección `### Quotes` en `docs/de/llm-prompt.md`.
////
## Comillas en fragmentos de código { #quotes-in-code-snippets }
//// tab | Prueba
`pip install "foo[bar]"`
Ejemplos de literales de string en fragmentos de código: `"this"`, `'that'`.
Un ejemplo difícil de literales de string en fragmentos de código: `f"I like {'oranges' if orange else "apples"}"`
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Info
... Sin embargo, las comillas dentro de fragmentos de código deben quedarse tal cual.
////
## bloques de código { #code-blocks }
//// tab | Prueba
Un ejemplo de código Bash...
```bash
# Imprime un saludo al universo
echo "Hello universe"
```
...y un ejemplo de código de consola...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...y otro ejemplo de código de consola...
```console
// Crea un directorio "Code"
$ mkdir code
// Cambia a ese directorio
$ cd code
```
...y un ejemplo de código Python...
```Python
wont_work() # Esto no va a funcionar 😱
works(foo="bar") # Esto funciona 🎉
```
...y eso es todo.
////
//// tab | Info
El código en bloques de código no debe modificarse, con la excepción de los comentarios.
Consulta la sección `### Content of code blocks` en el prompt general en `scripts/translate.py`.
////
## Pestañas y cajas coloreadas { #tabs-and-colored-boxes }
//// tab | Prueba
/// info | Información
Algo de texto
///
/// note | Nota
Algo de texto
///
/// note | Detalles técnicos
Algo de texto
///
/// check | Revisa
Algo de texto
///
/// tip | Consejo
Algo de texto
///
/// warning | Advertencia
Algo de texto
///
/// danger | Peligro
Algo de texto
///
////
//// tab | Info
Las pestañas y los bloques `Info`/`Note`/`Warning`/etc. deben tener la traducción de su título añadida después de una barra vertical (`|`).
Consulta las secciones `### Special blocks` y `### Tab blocks` en el prompt general en `scripts/translate.py`.
////
## Enlaces web e internos { #web-and-internal-links }
//// tab | Prueba
El texto del enlace debe traducirse, la dirección del enlace debe permanecer sin cambios:
* [Enlace al encabezado de arriba](#code-snippets)
* [Enlace interno](index.md#installation)
* [Enlace externo](https://sqlmodel.tiangolo.com/)
* [Enlace a un estilo](https://fastapi.tiangolo.com/css/styles.css)
* [Enlace a un script](https://fastapi.tiangolo.com/js/logic.js)
* [Enlace a una imagen](https://fastapi.tiangolo.com/img/foo.jpg)
El texto del enlace debe traducirse, la dirección del enlace debe apuntar a la traducción:
* [Enlace a FastAPI](https://fastapi.tiangolo.com/es/)
////
//// tab | Info
Los enlaces deben traducirse, pero su dirección debe permanecer sin cambios. Una excepción son los enlaces absolutos a páginas de la documentación de FastAPI. En ese caso deben enlazar a la traducción.
Consulta la sección `### Links` en el prompt general en `scripts/translate.py`.
////
## Elementos HTML "abbr" { #html-abbr-elements }
//// tab | Prueba
Aquí algunas cosas envueltas en elementos HTML "abbr" (algunas son inventadas):
### El abbr da una frase completa { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### El abbr da una frase completa y una explicación { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Info
Los atributos "title" de los elementos "abbr" se traducen siguiendo instrucciones específicas.
Las traducciones pueden añadir sus propios elementos "abbr" que el LLM no debe eliminar. P. ej., para explicar palabras en inglés.
Consulta la sección `### HTML abbr elements` en el prompt general en `scripts/translate.py`.
////
## Elementos HTML "dfn" { #html-dfn-elements }
* clúster
* Deep Learning
## Encabezados { #headings }
//// tab | Prueba
### Desarrolla una webapp - un tutorial { #develop-a-webapp-a-tutorial }
Hola.
### Anotaciones de tipos y -anotaciones { #type-hints-and-annotations }
Hola de nuevo.
### Superclases y subclases { #super-and-subclasses }
Hola de nuevo.
////
//// tab | Info
La única regla estricta para los encabezados es que el LLM deje la parte del hash dentro de llaves sin cambios, lo que asegura que los enlaces no se rompan.
Consulta la sección `### Headings` en el prompt general en `scripts/translate.py`.
Para instrucciones específicas del idioma, mira p. ej. la sección `### Headings` en `docs/de/llm-prompt.md`.
////
## Términos usados en la documentación { #terms-used-in-the-docs }
//// tab | Prueba
* tú
* tu
* p. ej.
* etc.
* `foo` como un `int`
* `bar` como un `str`
* `baz` como una `list`
* el Tutorial - Guía de usuario
* la Guía de usuario avanzada
* la documentación de SQLModel
* la documentación de la API
* la documentación automática
* Ciencia de datos
* Deep Learning
* Machine Learning
* Inyección de dependencias
* autenticación HTTP Basic
* HTTP Digest
* formato ISO
* el estándar JSON Schema
* el JSON Schema
* la definición del esquema
* Flujo de contraseña
* Móvil
* obsoleto
* diseñado
* inválido
* sobre la marcha
* estándar
* por defecto
* sensible a mayúsculas/minúsculas
* insensible a mayúsculas/minúsculas
* servir la aplicación
* servir la página
* la app
* la aplicación
* la request
* la response
* la response de error
* la path operation
* el decorador de path operation
* la path operation function
* el body
* el request body
* el response body
* el body JSON
* el body del formulario
* el body de archivo
* el cuerpo de la función
* el parámetro
* el parámetro del body
* el parámetro del path
* el parámetro de query
* el parámetro de cookie
* el parámetro de header
* el parámetro del formulario
* el parámetro de la función
* el evento
* el evento de inicio
* el inicio del servidor
* el evento de apagado
* el evento de lifespan
* el manejador
* el manejador de eventos
* el manejador de excepciones
* manejar
* el modelo
* el modelo de Pydantic
* el modelo de datos
* el modelo de base de datos
* el modelo de formulario
* el objeto del modelo
* la clase
* la clase base
* la clase padre
* la subclase
* la clase hija
* la clase hermana
* el método de clase
* el header
* los headers
* el header de autorización
* el header `Authorization`
* el header forwarded
* el sistema de inyección de dependencias
* la dependencia
* el dependable
* el dependiente
* limitado por I/O
* limitado por CPU
* concurrencia
* paralelismo
* multiprocesamiento
* la env var
* la variable de entorno
* el `PATH`
* la variable `PATH`
* la autenticación
* el proveedor de autenticación
* la autorización
* el formulario de autorización
* el proveedor de autorización
* el usuario se autentica
* el sistema autentica al usuario
* la CLI
* la interfaz de línea de comandos
* el servidor
* el cliente
* el proveedor en la nube
* el servicio en la nube
* el desarrollo
* las etapas de desarrollo
* el dict
* el diccionario
* la enumeración
* el enum
* el miembro del enum
* el codificador
* el decodificador
* codificar
* decodificar
* la excepción
* lanzar
* la expresión
* el statement
* el frontend
* el backend
* la discusión de GitHub
* el issue de GitHub
* el rendimiento
* la optimización de rendimiento
* el tipo de retorno
* el valor de retorno
* la seguridad
* el esquema de seguridad
* la tarea
* la tarea en segundo plano
* la función de tarea
* la plantilla
* el motor de plantillas
* la anotación de tipos
* la anotación de tipos
* el worker del servidor
* el worker de Uvicorn
* el Gunicorn Worker
* el worker process
* la worker class
* la carga de trabajo
* el despliegue
* desplegar
* el SDK
* el kit de desarrollo de software
* el `APIRouter`
* el `requirements.txt`
* el Bearer Token
* el cambio incompatible
* el bug
* el botón
* el invocable
* el código
* el commit
* el context manager
* la corrutina
* la sesión de base de datos
* el disco
* el dominio
* el motor
* el X falso
* el método HTTP GET
* el ítem
* el paquete
* el lifespan
* el lock
* el middleware
* la aplicación móvil
* el módulo
* el montaje
* la red
* el origen
* el override
* el payload
* el procesador
* la propiedad
* el proxy
* el pull request
* la query
* la RAM
* la máquina remota
* el código de estado
* el string
* la etiqueta
* el framework web
* el comodín
* devolver
* validar
////
//// tab | Info
Esta es una lista no completa y no normativa de términos (mayormente) técnicos vistos en la documentación. Puede ayudar a la persona que diseña el prompt a identificar para qué términos el LLM necesita una mano. Por ejemplo cuando sigue revirtiendo una buena traducción a una traducción subóptima. O cuando tiene problemas conjugando/declinando un término en tu idioma.
Mira p. ej. la sección `### List of English terms and their preferred German translations` en `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/es/docs/about/index.md
================================================
# Acerca de { #about }
Acerca de FastAPI, su diseño, inspiración y más. 🤓
================================================
FILE: docs/es/docs/advanced/additional-responses.md
================================================
# Responses Adicionales en OpenAPI { #additional-responses-in-openapi }
/// warning | Advertencia
Este es un tema bastante avanzado.
Si estás comenzando con **FastAPI**, puede que no lo necesites.
///
Puedes declarar responses adicionales, con códigos de estado adicionales, media types, descripciones, etc.
Esos responses adicionales se incluirán en el esquema de OpenAPI, por lo que también aparecerán en la documentación de la API.
Pero para esos responses adicionales tienes que asegurarte de devolver un `Response` como `JSONResponse` directamente, con tu código de estado y contenido.
## Response Adicional con `model` { #additional-response-with-model }
Puedes pasar a tus *decoradores de path operation* un parámetro `responses`.
Recibe un `dict`: las claves son los códigos de estado para cada response (como `200`), y los valores son otros `dict`s con la información para cada uno de ellos.
Cada uno de esos `dict`s de response puede tener una clave `model`, conteniendo un modelo de Pydantic, así como `response_model`.
**FastAPI** tomará ese modelo, generará su JSON Schema y lo incluirá en el lugar correcto en OpenAPI.
Por ejemplo, para declarar otro response con un código de estado `404` y un modelo Pydantic `Message`, puedes escribir:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Nota
Ten en cuenta que debes devolver el `JSONResponse` directamente.
///
/// info | Información
La clave `model` no es parte de OpenAPI.
**FastAPI** tomará el modelo de Pydantic de allí, generará el JSON Schema y lo colocará en el lugar correcto.
El lugar correcto es:
* En la clave `content`, que tiene como valor otro objeto JSON (`dict`) que contiene:
* Una clave con el media type, por ejemplo, `application/json`, que contiene como valor otro objeto JSON, que contiene:
* Una clave `schema`, que tiene como valor el JSON Schema del modelo, aquí es el lugar correcto.
* **FastAPI** agrega una referencia aquí a los JSON Schemas globales en otro lugar de tu OpenAPI en lugar de incluirlo directamente. De este modo, otras aplicaciones y clientes pueden usar esos JSON Schemas directamente, proporcionar mejores herramientas de generación de código, etc.
///
Los responses generadas en el OpenAPI para esta *path operation* serán:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Los esquemas se referencian a otro lugar dentro del esquema de OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Media types adicionales para el response principal { #additional-media-types-for-the-main-response }
Puedes usar este mismo parámetro `responses` para agregar diferentes media type para el mismo response principal.
Por ejemplo, puedes agregar un media type adicional de `image/png`, declarando que tu *path operation* puede devolver un objeto JSON (con media type `application/json`) o una imagen PNG:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Nota
Nota que debes devolver la imagen usando un `FileResponse` directamente.
///
/// info | Información
A menos que especifiques un media type diferente explícitamente en tu parámetro `responses`, FastAPI asumirá que el response tiene el mismo media type que la clase de response principal (por defecto `application/json`).
Pero si has especificado una clase de response personalizada con `None` como su media type, FastAPI usará `application/json` para cualquier response adicional que tenga un modelo asociado.
///
## Combinando información { #combining-information }
También puedes combinar información de response de múltiples lugares, incluyendo los parámetros `response_model`, `status_code`, y `responses`.
Puedes declarar un `response_model`, usando el código de estado por defecto `200` (o uno personalizado si lo necesitas), y luego declarar información adicional para ese mismo response en `responses`, directamente en el esquema de OpenAPI.
**FastAPI** mantendrá la información adicional de `responses` y la combinará con el JSON Schema de tu modelo.
Por ejemplo, puedes declarar un response con un código de estado `404` que usa un modelo Pydantic y tiene una `description` personalizada.
Y un response con un código de estado `200` que usa tu `response_model`, pero incluye un `example` personalizado:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Todo se combinará e incluirá en tu OpenAPI, y se mostrará en la documentación de la API:
## Combina responses predefinidos y personalizados { #combine-predefined-responses-and-custom-ones }
Es posible que desees tener algunos responses predefinidos que se apliquen a muchas *path operations*, pero que quieras combinarlos con responses personalizados necesarios por cada *path operation*.
Para esos casos, puedes usar la técnica de Python de "desempaquetar" un `dict` con `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Aquí, `new_dict` contendrá todos los pares clave-valor de `old_dict` más el nuevo par clave-valor:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Puedes usar esa técnica para reutilizar algunos responses predefinidos en tus *path operations* y combinarlos con otros personalizados adicionales.
Por ejemplo:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Más información sobre responses OpenAPI { #more-information-about-openapi-responses }
Para ver exactamente qué puedes incluir en los responses, puedes revisar estas secciones en la especificación OpenAPI:
* [Objeto de Responses de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), incluye el `Response Object`.
* [Objeto de Response de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), puedes incluir cualquier cosa de esto directamente en cada response dentro de tu parámetro `responses`. Incluyendo `description`, `headers`, `content` (dentro de este es que declaras diferentes media types y JSON Schemas), y `links`.
================================================
FILE: docs/es/docs/advanced/additional-status-codes.md
================================================
# Códigos de Estado Adicionales { #additional-status-codes }
Por defecto, **FastAPI** devolverá los responses usando un `JSONResponse`, colocando el contenido que devuelves desde tu *path operation* dentro de ese `JSONResponse`.
Usará el código de estado por defecto o el que configures en tu *path operation*.
## Códigos de estado adicionales { #additional-status-codes_1 }
Si quieres devolver códigos de estado adicionales aparte del principal, puedes hacerlo devolviendo un `Response` directamente, como un `JSONResponse`, y configurando el código de estado adicional directamente.
Por ejemplo, supongamos que quieres tener una *path operation* que permita actualizar elementos, y devuelva códigos de estado HTTP de 200 "OK" cuando sea exitoso.
Pero también quieres que acepte nuevos elementos. Y cuando los elementos no existían antes, los crea y devuelve un código de estado HTTP de 201 "Created".
Para lograr eso, importa `JSONResponse`, y devuelve tu contenido allí directamente, configurando el `status_code` que deseas:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Advertencia
Cuando devuelves un `Response` directamente, como en el ejemplo anterior, se devuelve directamente.
No se serializará con un modelo, etc.
Asegúrate de que tenga los datos que deseas que tenga y que los valores sean JSON válidos (si estás usando `JSONResponse`).
///
/// note | Detalles Técnicos
También podrías usar `from starlette.responses import JSONResponse`.
**FastAPI** proporciona los mismos `starlette.responses` que `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles provienen directamente de Starlette. Lo mismo con `status`.
///
## OpenAPI y documentación de API { #openapi-and-api-docs }
Si devuelves códigos de estado adicionales y responses directamente, no se incluirán en el esquema de OpenAPI (la documentación de la API), porque FastAPI no tiene una forma de saber de antemano qué vas a devolver.
Pero puedes documentarlo en tu código, usando: [Responses Adicionales](additional-responses.md).
================================================
FILE: docs/es/docs/advanced/advanced-dependencies.md
================================================
# Dependencias Avanzadas { #advanced-dependencies }
## Dependencias con parámetros { #parameterized-dependencies }
Todas las dependencias que hemos visto son una función o clase fija.
Pero podría haber casos en los que quieras poder establecer parámetros en la dependencia, sin tener que declarar muchas funciones o clases diferentes.
Imaginemos que queremos tener una dependencia que revise si el parámetro de query `q` contiene algún contenido fijo.
Pero queremos poder parametrizar ese contenido fijo.
## Una *instance* "callable" { #a-callable-instance }
En Python hay una forma de hacer que una instance de una clase sea un "callable".
No la clase en sí (que ya es un callable), sino una instance de esa clase.
Para hacer eso, declaramos un método `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
En este caso, este `__call__` es lo que **FastAPI** usará para comprobar parámetros adicionales y sub-dependencias, y es lo que llamará para pasar un valor al parámetro en tu *path operation function* más adelante.
## Parametrizar la instance { #parameterize-the-instance }
Y ahora, podemos usar `__init__` para declarar los parámetros de la instance que podemos usar para "parametrizar" la dependencia:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
En este caso, **FastAPI** nunca tocará ni se preocupará por `__init__`, lo usaremos directamente en nuestro código.
## Crear una instance { #create-an-instance }
Podríamos crear una instance de esta clase con:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Y de esa manera podemos "parametrizar" nuestra dependencia, que ahora tiene `"bar"` dentro de ella, como el atributo `checker.fixed_content`.
## Usar la instance como una dependencia { #use-the-instance-as-a-dependency }
Luego, podríamos usar este `checker` en un `Depends(checker)`, en lugar de `Depends(FixedContentQueryChecker)`, porque la dependencia es la instance, `checker`, no la clase en sí.
Y al resolver la dependencia, **FastAPI** llamará a este `checker` así:
```Python
checker(q="somequery")
```
...y pasará lo que eso retorne como el valor de la dependencia en nuestra *path operation function* como el parámetro `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Consejo
Todo esto podría parecer complicado. Y puede que no esté muy claro cómo es útil aún.
Estos ejemplos son intencionalmente simples, pero muestran cómo funciona todo.
En los capítulos sobre seguridad, hay funciones utilitarias que se implementan de esta misma manera.
Si entendiste todo esto, ya sabes cómo funcionan por debajo esas herramientas de utilidad para seguridad.
///
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en segundo plano { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Advertencia
Muy probablemente no necesites estos detalles técnicos.
Estos detalles son útiles principalmente si tenías una aplicación de FastAPI anterior a la 0.121.0 y estás enfrentando problemas con dependencias con `yield`.
///
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y arreglar algunos problemas; aquí tienes un resumen de lo que ha cambiado.
### Dependencias con `yield` y `scope` { #dependencies-with-yield-and-scope }
En la versión 0.121.0, FastAPI agregó soporte para `Depends(scope="function")` para dependencias con `yield`.
Usando `Depends(scope="function")`, el código de salida después de `yield` se ejecuta justo después de que la *path operation function* termina, antes de que la response se envíe de vuelta al cliente.
Y al usar `Depends(scope="request")` (el valor por defecto), el código de salida después de `yield` se ejecuta después de que la response es enviada.
Puedes leer más al respecto en la documentación de [Dependencias con `yield` - Salida temprana y `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Dependencias con `yield` y `StreamingResponse`, detalles técnicos { #dependencies-with-yield-and-streamingresponse-technical-details }
Antes de FastAPI 0.118.0, si usabas una dependencia con `yield`, ejecutaba el código de salida después de que la *path operation function* retornaba pero justo antes de enviar la response.
La intención era evitar retener recursos por más tiempo del necesario, esperando a que la response viajara por la red.
Este cambio también significaba que si retornabas un `StreamingResponse`, el código de salida de la dependencia con `yield` ya se habría ejecutado.
Por ejemplo, si tenías una sesión de base de datos en una dependencia con `yield`, el `StreamingResponse` no podría usar esa sesión mientras hace streaming de datos porque la sesión ya se habría cerrado en el código de salida después de `yield`.
Este comportamiento se revirtió en la 0.118.0, para hacer que el código de salida después de `yield` se ejecute después de que la response sea enviada.
/// info | Información
Como verás abajo, esto es muy similar al comportamiento anterior a la versión 0.106.0, pero con varias mejoras y arreglos de bugs para casos límite.
///
#### Casos de uso con salida temprana del código { #use-cases-with-early-exit-code }
Hay algunos casos de uso con condiciones específicas que podrían beneficiarse del comportamiento antiguo de ejecutar el código de salida de dependencias con `yield` antes de enviar la response.
Por ejemplo, imagina que tienes código que usa una sesión de base de datos en una dependencia con `yield` solo para verificar un usuario, pero la sesión de base de datos no se vuelve a usar en la *path operation function*, solo en la dependencia, y la response tarda mucho en enviarse, como un `StreamingResponse` que envía datos lentamente, pero que por alguna razón no usa la base de datos.
En este caso, la sesión de base de datos se mantendría hasta que la response termine de enviarse, pero si no la usas, entonces no sería necesario mantenerla.
Así es como se vería:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
El código de salida, el cierre automático de la `Session` en:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...se ejecutaría después de que la response termine de enviar los datos lentos:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Pero como `generate_stream()` no usa la sesión de base de datos, no es realmente necesario mantener la sesión abierta mientras se envía la response.
Si tienes este caso de uso específico usando SQLModel (o SQLAlchemy), podrías cerrar explícitamente la sesión después de que ya no la necesites:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
De esa manera la sesión liberaría la conexión a la base de datos, para que otras requests puedan usarla.
Si tienes un caso de uso diferente que necesite salir temprano desde una dependencia con `yield`, por favor crea una [Pregunta de Discusión en GitHub](https://github.com/fastapi/fastapi/discussions/new?category=questions) con tu caso de uso específico y por qué te beneficiaría tener cierre temprano para dependencias con `yield`.
Si hay casos de uso convincentes para el cierre temprano en dependencias con `yield`, consideraría agregar una nueva forma de optar por el cierre temprano.
### Dependencias con `yield` y `except`, detalles técnicos { #dependencies-with-yield-and-except-technical-details }
Antes de FastAPI 0.110.0, si usabas una dependencia con `yield`, y luego capturabas una excepción con `except` en esa dependencia, y no volvías a elevar la excepción, la excepción se elevaría/remitiría automáticamente a cualquier manejador de excepciones o al manejador de error interno del servidor.
Esto cambió en la versión 0.110.0 para arreglar consumo de memoria no manejado por excepciones reenviadas sin un manejador (errores internos del servidor), y para hacerlo consistente con el comportamiento del código Python normal.
### Tareas en segundo plano y dependencias con `yield`, detalles técnicos { #background-tasks-and-dependencies-with-yield-technical-details }
Antes de FastAPI 0.106.0, elevar excepciones después de `yield` no era posible, el código de salida en dependencias con `yield` se ejecutaba después de que la response era enviada, por lo que [Manejadores de Excepciones](../tutorial/handling-errors.md#install-custom-exception-handlers) ya habrían corrido.
Esto se diseñó así principalmente para permitir usar los mismos objetos devueltos con `yield` por las dependencias dentro de tareas en segundo plano, porque el código de salida se ejecutaría después de que las tareas en segundo plano terminaran.
Esto cambió en FastAPI 0.106.0 con la intención de no retener recursos mientras se espera a que la response viaje por la red.
/// tip | Consejo
Adicionalmente, una tarea en segundo plano normalmente es un conjunto independiente de lógica que debería manejarse por separado, con sus propios recursos (por ejemplo, su propia conexión a la base de datos).
Así, probablemente tendrás un código más limpio.
///
Si solías depender de este comportamiento, ahora deberías crear los recursos para las tareas en segundo plano dentro de la propia tarea en segundo plano, y usar internamente solo datos que no dependan de los recursos de dependencias con `yield`.
Por ejemplo, en lugar de usar la misma sesión de base de datos, crearías una nueva sesión de base de datos dentro de la tarea en segundo plano, y obtendrías los objetos de la base de datos usando esta nueva sesión. Y entonces, en lugar de pasar el objeto de la base de datos como parámetro a la función de la tarea en segundo plano, pasarías el ID de ese objeto y luego obtendrías el objeto de nuevo dentro de la función de la tarea en segundo plano.
================================================
FILE: docs/es/docs/advanced/advanced-python-types.md
================================================
# Tipos avanzados de Python { #advanced-python-types }
Aquí tienes algunas ideas adicionales que podrían ser útiles al trabajar con tipos de Python.
## Usar `Union` u `Optional` { #using-union-or-optional }
Si por alguna razón tu código no puede usar `|`, por ejemplo si no está en una anotación de tipos sino en algo como `response_model=`, en lugar de usar la barra vertical (`|`) puedes usar `Union` de `typing`.
Por ejemplo, podrías declarar que algo podría ser un `str` o `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` también tiene un atajo para declarar que algo podría ser `None`, con `Optional`.
Aquí va un Consejo desde mi punto de vista muy subjetivo:
* 🚨 Evita usar `Optional[SomeType]`
* En su lugar ✨ **usa `Union[SomeType, None]`** ✨.
Ambas son equivalentes y por debajo son lo mismo, pero recomendaría `Union` en lugar de `Optional` porque la palabra "**optional**" parecería implicar que el valor es opcional, y en realidad significa "puede ser `None`", incluso si no es opcional y sigue siendo requerido.
Creo que `Union[SomeType, None]` es más explícito respecto a lo que significa.
Se trata solo de palabras y nombres. Pero esas palabras pueden afectar cómo tú y tu equipo piensan sobre el código.
Como ejemplo, tomemos esta función:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
El parámetro `name` está definido como `Optional[str]`, pero **no es opcional**, no puedes llamar a la función sin el parámetro:
```Python
say_hi() # ¡Oh, no, esto lanza un error! 😱
```
El parámetro `name` **sigue siendo requerido** (no es *opcional*) porque no tiene un valor por defecto. Aun así, `name` acepta `None` como valor:
```Python
say_hi(name=None) # Esto funciona, None es válido 🎉
```
La buena noticia es que, en la mayoría de los casos, podrás simplemente usar `|` para definir uniones de tipos:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Así que, normalmente no tienes que preocuparte por nombres como `Optional` y `Union`. 😎
================================================
FILE: docs/es/docs/advanced/async-tests.md
================================================
# Tests Asíncronos { #async-tests }
Ya has visto cómo probar tus aplicaciones de **FastAPI** usando el `TestClient` proporcionado. Hasta ahora, solo has visto cómo escribir tests sincrónicos, sin usar funciones `async`.
Poder usar funciones asíncronas en tus tests puede ser útil, por ejemplo, cuando consultas tu base de datos de forma asíncrona. Imagina que quieres probar el envío de requests a tu aplicación FastAPI y luego verificar que tu backend escribió exitosamente los datos correctos en la base de datos, mientras usas un paquete de base de datos asíncrono.
Veamos cómo podemos hacer que esto funcione.
## pytest.mark.anyio { #pytest-mark-anyio }
Si queremos llamar funciones asíncronas en nuestros tests, nuestras funciones de test tienen que ser asíncronas. AnyIO proporciona un plugin útil para esto, que nos permite especificar que algunas funciones de test deben ser llamadas de manera asíncrona.
## HTTPX { #httpx }
Incluso si tu aplicación de **FastAPI** usa funciones `def` normales en lugar de `async def`, sigue siendo una aplicación `async` por debajo.
El `TestClient` hace algo de magia interna para llamar a la aplicación FastAPI asíncrona en tus funciones de test `def` normales, usando pytest estándar. Pero esa magia ya no funciona cuando lo usamos dentro de funciones asíncronas. Al ejecutar nuestros tests de manera asíncrona, ya no podemos usar el `TestClient` dentro de nuestras funciones de test.
El `TestClient` está basado en [HTTPX](https://www.python-httpx.org), y afortunadamente, podemos usarlo directamente para probar la API.
## Ejemplo { #example }
Para un ejemplo simple, consideremos una estructura de archivos similar a la descrita en [Aplicaciones Más Grandes](../tutorial/bigger-applications.md) y [Testing](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
El archivo `main.py` tendría:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
El archivo `test_main.py` tendría los tests para `main.py`, podría verse así ahora:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Ejecútalo { #run-it }
Puedes ejecutar tus tests como de costumbre vía:
```console
$ pytest
---> 100%
```
## En Detalle { #in-detail }
El marcador `@pytest.mark.anyio` le dice a pytest que esta función de test debe ser llamada asíncronamente:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Consejo
Nota que la función de test ahora es `async def` en lugar de solo `def` como antes al usar el `TestClient`.
///
Luego podemos crear un `AsyncClient` con la app y enviar requests asíncronos a ella, usando `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Esto es equivalente a:
```Python
response = client.get('/')
```
...que usábamos para hacer nuestros requests con el `TestClient`.
/// tip | Consejo
Nota que estamos usando async/await con el nuevo `AsyncClient`: el request es asíncrono.
///
/// warning | Advertencia
Si tu aplicación depende de eventos de lifespan, el `AsyncClient` no activará estos eventos. Para asegurarte de que se activen, usa `LifespanManager` de [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Otras Llamadas a Funciones Asíncronas { #other-asynchronous-function-calls }
Al ser la función de test asíncrona, ahora también puedes llamar (y `await`) otras funciones `async` además de enviar requests a tu aplicación FastAPI en tus tests, exactamente como las llamarías en cualquier otro lugar de tu código.
/// tip | Consejo
Si encuentras un `RuntimeError: Task attached to a different loop` al integrar llamadas a funciones asíncronas en tus tests (por ejemplo, cuando usas [MotorClient de MongoDB](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), recuerda crear instances de objetos que necesiten un loop de eventos solo dentro de funciones async, por ejemplo, en un callback `@app.on_event("startup")`.
///
================================================
FILE: docs/es/docs/advanced/behind-a-proxy.md
================================================
# Detrás de un Proxy { #behind-a-proxy }
En muchas situaciones, usarías un **proxy** como Traefik o Nginx delante de tu app de FastAPI.
Estos proxies podrían manejar certificados HTTPS y otras cosas.
## Headers reenviados por el Proxy { #proxy-forwarded-headers }
Un **proxy** delante de tu aplicación normalmente establecería algunos headers sobre la marcha antes de enviar los requests a tu **server** para que el servidor sepa que el request fue **reenviado** por el proxy, informándole la URL original (pública), incluyendo el dominio, que está usando HTTPS, etc.
El programa **server** (por ejemplo **Uvicorn** a través de **FastAPI CLI**) es capaz de interpretar esos headers, y luego pasar esa información a tu aplicación.
Pero por seguridad, como el server no sabe que está detrás de un proxy confiable, no interpretará esos headers.
/// note | Detalles Técnicos
Los headers del proxy son:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Habilitar headers reenviados por el Proxy { #enable-proxy-forwarded-headers }
Puedes iniciar FastAPI CLI con la *Opción de CLI* `--forwarded-allow-ips` y pasar las direcciones IP que deberían ser confiables para leer esos headers reenviados.
Si lo estableces a `--forwarded-allow-ips="*"`, confiaría en todas las IPs entrantes.
Si tu **server** está detrás de un **proxy** confiable y solo el proxy le habla, esto haría que acepte cualquiera que sea la IP de ese **proxy**.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Redirecciones con HTTPS { #redirects-with-https }
Por ejemplo, digamos que defines una *path operation* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Si el cliente intenta ir a `/items`, por defecto, sería redirigido a `/items/`.
Pero antes de configurar la *Opción de CLI* `--forwarded-allow-ips` podría redirigir a `http://localhost:8000/items/`.
Pero quizá tu aplicación está alojada en `https://mysuperapp.com`, y la redirección debería ser a `https://mysuperapp.com/items/`.
Al configurar `--proxy-headers` ahora FastAPI podrá redirigir a la ubicación correcta. 😎
```
https://mysuperapp.com/items/
```
/// tip | Consejo
Si quieres aprender más sobre HTTPS, revisa la guía [Acerca de HTTPS](../deployment/https.md).
///
### Cómo funcionan los headers reenviados por el Proxy { #how-proxy-forwarded-headers-work }
Aquí tienes una representación visual de cómo el **proxy** añade headers reenviados entre el cliente y el **application server**:
```mermaid
sequenceDiagram
participant Client as Cliente
participant Proxy as Proxy/Load Balancer
participant Server as Servidor de FastAPI
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: El proxy añade headers reenviados
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: El servidor interpreta los headers (si --forwarded-allow-ips está configurado)
Server->>Proxy: HTTP Response con URLs HTTPS correctas
Proxy->>Client: HTTPS Response
```
El **proxy** intercepta el request original del cliente y añade los *headers* especiales de reenvío (`X-Forwarded-*`) antes de pasar el request al **application server**.
Estos headers preservan información sobre el request original que de otro modo se perdería:
* **X-Forwarded-For**: La IP original del cliente
* **X-Forwarded-Proto**: El protocolo original (`https`)
* **X-Forwarded-Host**: El host original (`mysuperapp.com`)
Cuando **FastAPI CLI** está configurado con `--forwarded-allow-ips`, confía en estos headers y los usa, por ejemplo para generar las URLs correctas en redirecciones.
## Proxy con un prefijo de path eliminado { #proxy-with-a-stripped-path-prefix }
Podrías tener un proxy que añada un prefijo de path a tu aplicación.
En estos casos, puedes usar `root_path` para configurar tu aplicación.
El `root_path` es un mecanismo proporcionado por la especificación ASGI (en la que está construido FastAPI, a través de Starlette).
El `root_path` se usa para manejar estos casos específicos.
Y también se usa internamente al montar subaplicaciones.
Tener un proxy con un prefijo de path eliminado, en este caso, significa que podrías declarar un path en `/app` en tu código, pero luego añades una capa encima (el proxy) que situaría tu aplicación **FastAPI** bajo un path como `/api/v1`.
En este caso, el path original `/app` realmente sería servido en `/api/v1/app`.
Aunque todo tu código esté escrito asumiendo que solo existe `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Y el proxy estaría **"eliminando"** el **prefijo del path** sobre la marcha antes de transmitir el request al servidor de aplicaciones (probablemente Uvicorn a través de FastAPI CLI), manteniendo a tu aplicación convencida de que está siendo servida en `/app`, así que no tienes que actualizar todo tu código para incluir el prefijo `/api/v1`.
Hasta aquí, todo funcionaría normalmente.
Pero luego, cuando abres la UI integrada de los docs (el frontend), esperaría obtener el esquema de OpenAPI en `/openapi.json`, en lugar de `/api/v1/openapi.json`.
Entonces, el frontend (que se ejecuta en el navegador) trataría de alcanzar `/openapi.json` y no podría obtener el esquema de OpenAPI.
Porque tenemos un proxy con un prefijo de path de `/api/v1` para nuestra aplicación, el frontend necesita obtener el esquema de OpenAPI en `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Navegador")
proxy["Proxy en http://0.0.0.0:9999/api/v1/app"]
server["Servidor en http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Consejo
La IP `0.0.0.0` se usa comúnmente para indicar que el programa escucha en todas las IPs disponibles en esa máquina/servidor.
///
La UI de los docs también necesitaría el esquema de OpenAPI para declarar que este API `servidor` se encuentra en `/api/v1` (detrás del proxy). Por ejemplo:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Más cosas aquí
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Más cosas aquí
}
}
```
En este ejemplo, el "Proxy" podría ser algo como **Traefik**. Y el servidor sería algo como FastAPI CLI con **Uvicorn**, ejecutando tu aplicación de FastAPI.
### Proporcionando el `root_path` { #providing-the-root-path }
Para lograr esto, puedes usar la opción de línea de comandos `--root-path` como:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Si usas Hypercorn, también tiene la opción `--root-path`.
/// note | Detalles Técnicos
La especificación ASGI define un `root_path` para este caso de uso.
Y la opción de línea de comandos `--root-path` proporciona ese `root_path`.
///
### Revisar el `root_path` actual { #checking-the-current-root-path }
Puedes obtener el `root_path` actual utilizado por tu aplicación para cada request, es parte del diccionario `scope` (que es parte de la especificación ASGI).
Aquí lo estamos incluyendo en el mensaje solo con fines de demostración.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Luego, si inicias Uvicorn con:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
El response sería algo como:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Configurar el `root_path` en la app de FastAPI { #setting-the-root-path-in-the-fastapi-app }
Alternativamente, si no tienes una forma de proporcionar una opción de línea de comandos como `--root-path` o su equivalente, puedes configurar el parámetro `root_path` al crear tu app de FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Pasar el `root_path` a `FastAPI` sería el equivalente a pasar la opción de línea de comandos `--root-path` a Uvicorn o Hypercorn.
### Acerca de `root_path` { #about-root-path }
Ten en cuenta que el servidor (Uvicorn) no usará ese `root_path` para nada, a excepción de pasárselo a la app.
Pero si vas con tu navegador a [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) verás el response normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Así que no se esperará que sea accedido en `http://127.0.0.1:8000/api/v1/app`.
Uvicorn esperará que el proxy acceda a Uvicorn en `http://127.0.0.1:8000/app`, y luego será responsabilidad del proxy añadir el prefijo extra `/api/v1` encima.
## Sobre proxies con un prefijo de path eliminado { #about-proxies-with-a-stripped-path-prefix }
Ten en cuenta que un proxy con prefijo de path eliminado es solo una de las formas de configurarlo.
Probablemente en muchos casos, el valor por defecto será que el proxy no tenga un prefijo de path eliminado.
En un caso así (sin un prefijo de path eliminado), el proxy escucharía algo como `https://myawesomeapp.com`, y luego si el navegador va a `https://myawesomeapp.com/api/v1/app` y tu servidor (por ejemplo, Uvicorn) escucha en `http://127.0.0.1:8000`, el proxy (sin un prefijo de path eliminado) accedería a Uvicorn en el mismo path: `http://127.0.0.1:8000/api/v1/app`.
## Probando localmente con Traefik { #testing-locally-with-traefik }
Puedes ejecutar fácilmente el experimento localmente con un prefijo de path eliminado usando [Traefik](https://docs.traefik.io/).
[Descarga Traefik](https://github.com/containous/traefik/releases), es un archivo binario único, puedes extraer el archivo comprimido y ejecutarlo directamente desde la terminal.
Luego crea un archivo `traefik.toml` con:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Esto le dice a Traefik que escuche en el puerto 9999 y que use otro archivo `routes.toml`.
/// tip | Consejo
Estamos utilizando el puerto 9999 en lugar del puerto HTTP estándar 80 para que no tengas que ejecutarlo con privilegios de administrador (`sudo`).
///
Ahora crea ese otro archivo `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Este archivo configura Traefik para usar el prefijo de path `/api/v1`.
Y luego Traefik redireccionará sus requests a tu Uvicorn ejecutándose en `http://127.0.0.1:8000`.
Ahora inicia Traefik:
Y ahora inicia tu app, utilizando la opción `--root-path`:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Revisa los responses { #check-the-responses }
Ahora, si vas a la URL con el puerto para Uvicorn: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), verás el response normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Consejo
Nota que incluso aunque estés accediendo en `http://127.0.0.1:8000/app`, muestra el `root_path` de `/api/v1`, tomado de la opción `--root-path`.
///
Y ahora abre la URL con el puerto para Traefik, incluyendo el prefijo de path: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Obtenemos el mismo response:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
pero esta vez en la URL con el prefijo de path proporcionado por el proxy: `/api/v1`.
Por supuesto, la idea aquí es que todos accedan a la app a través del proxy, así que la versión con el prefijo de path `/api/v1` es la "correcta".
Y la versión sin el prefijo de path (`http://127.0.0.1:8000/app`), proporcionada directamente por Uvicorn, sería exclusivamente para que el _proxy_ (Traefik) la acceda.
Eso demuestra cómo el Proxy (Traefik) usa el prefijo de path y cómo el servidor (Uvicorn) usa el `root_path` de la opción `--root-path`.
### Revisa la UI de los docs { #check-the-docs-ui }
Pero aquí está la parte divertida. ✨
La forma "oficial" de acceder a la app sería a través del proxy con el prefijo de path que definimos. Así que, como esperaríamos, si intentas usar la UI de los docs servida por Uvicorn directamente, sin el prefijo de path en la URL, no funcionará, porque espera ser accedida a través del proxy.
Puedes verificarlo en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
Pero si accedemos a la UI de los docs en la URL "oficial" usando el proxy con puerto `9999`, en `/api/v1/docs`, ¡funciona correctamente! 🎉
Puedes verificarlo en [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Justo como queríamos. ✔️
Esto es porque FastAPI usa este `root_path` para crear el `server` por defecto en OpenAPI con la URL proporcionada por `root_path`.
## Servidores adicionales { #additional-servers }
/// warning | Advertencia
Este es un caso de uso más avanzado. Siéntete libre de omitirlo.
///
Por defecto, **FastAPI** creará un `server` en el esquema de OpenAPI con la URL para el `root_path`.
Pero también puedes proporcionar otros `servers` alternativos, por ejemplo, si deseas que *la misma* UI de los docs interactúe con un entorno de pruebas y de producción.
Si pasas una lista personalizada de `servers` y hay un `root_path` (porque tu API existe detrás de un proxy), **FastAPI** insertará un "server" con este `root_path` al comienzo de la lista.
Por ejemplo:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Generará un esquema de OpenAPI como:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Más cosas aquí
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Más cosas aquí
}
}
```
/// tip | Consejo
Observa el server auto-generado con un valor `url` de `/api/v1`, tomado del `root_path`.
///
En la UI de los docs en [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) se vería como:
/// tip | Consejo
La UI de los docs interactuará con el server que selecciones.
///
/// note | Detalles Técnicos
La propiedad `servers` en la especificación de OpenAPI es opcional.
Si no especificas el parámetro `servers` y `root_path` es igual a `/`, la propiedad `servers` en el esquema de OpenAPI generado se omitirá por completo por defecto, lo cual es equivalente a un único server con un valor `url` de `/`.
///
### Desactivar el server automático de `root_path` { #disable-automatic-server-from-root-path }
Si no quieres que **FastAPI** incluya un server automático usando el `root_path`, puedes usar el parámetro `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
y entonces no lo incluirá en el esquema de OpenAPI.
## Montando una sub-aplicación { #mounting-a-sub-application }
Si necesitas montar una sub-aplicación (como se describe en [Aplicaciones secundarias - Monturas](sub-applications.md)) mientras usas un proxy con `root_path`, puedes hacerlo normalmente, como esperarías.
FastAPI usará internamente el `root_path` de manera inteligente, así que simplemente funcionará. ✨
================================================
FILE: docs/es/docs/advanced/custom-response.md
================================================
# Response Personalizado - HTML, Stream, Archivo, otros { #custom-response-html-stream-file-others }
Por defecto, **FastAPI** devolverá responses JSON.
Puedes sobrescribirlo devolviendo un `Response` directamente como se ve en [Devolver una Response directamente](response-directly.md).
Pero si devuelves un `Response` directamente (o cualquier subclase, como `JSONResponse`), los datos no se convertirán automáticamente (incluso si declaras un `response_model`), y la documentación no se generará automáticamente (por ejemplo, incluyendo el "media type" específico, en el HTTP header `Content-Type` como parte del OpenAPI generado).
Pero también puedes declarar el `Response` que quieres usar (por ejemplo, cualquier subclase de `Response`), en el *path operation decorator* usando el parámetro `response_class`.
Los contenidos que devuelvas desde tu *path operation function* se colocarán dentro de esa `Response`.
/// note | Nota
Si usas una clase de response sin media type, FastAPI esperará que tu response no tenga contenido, por lo que no documentará el formato del response en su OpenAPI generado.
///
## Responses JSON { #json-responses }
Por defecto FastAPI devuelve responses JSON.
Si declaras un [Response Model](../tutorial/response-model.md) FastAPI lo usará para serializar los datos a JSON, usando Pydantic.
Si no declaras un response model, FastAPI usará el `jsonable_encoder` explicado en [Codificador Compatible con JSON](../tutorial/encoder.md) y lo pondrá en un `JSONResponse`.
Si declaras un `response_class` con un media type JSON (`application/json`), como es el caso con `JSONResponse`, los datos que devuelvas se convertirán automáticamente (y serán filtrados) con cualquier `response_model` de Pydantic que hayas declarado en el *path operation decorator*. Pero los datos no se serializarán a bytes JSON con Pydantic, en su lugar se convertirán con el `jsonable_encoder` y luego se pasarán a la clase `JSONResponse`, que los serializará a bytes usando la librería JSON estándar de Python.
### Rendimiento JSON { #json-performance }
En resumen, si quieres el máximo rendimiento, usa un [Response Model](../tutorial/response-model.md) y no declares un `response_class` en el *path operation decorator*.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## Response HTML { #html-response }
Para devolver un response con HTML directamente desde **FastAPI**, usa `HTMLResponse`.
* Importa `HTMLResponse`.
* Pasa `HTMLResponse` como parámetro `response_class` de tu *path operation decorator*.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Información
El parámetro `response_class` también se utilizará para definir el "media type" del response.
En este caso, el HTTP header `Content-Type` se establecerá en `text/html`.
Y se documentará así en OpenAPI.
///
### Devuelve una `Response` { #return-a-response }
Como se ve en [Devolver una Response directamente](response-directly.md), también puedes sobrescribir el response directamente en tu *path operation*, devolviéndolo.
El mismo ejemplo de arriba, devolviendo una `HTMLResponse`, podría verse así:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Advertencia
Una `Response` devuelta directamente por tu *path operation function* no se documentará en OpenAPI (por ejemplo, el `Content-Type` no se documentará) y no será visible en la documentación interactiva automática.
///
/// info | Información
Por supuesto, el `Content-Type` header real, el código de estado, etc., provendrán del objeto `Response` que devolviste.
///
### Documenta en OpenAPI y sobrescribe `Response` { #document-in-openapi-and-override-response }
Si quieres sobrescribir el response desde dentro de la función pero al mismo tiempo documentar el "media type" en OpenAPI, puedes usar el parámetro `response_class` Y devolver un objeto `Response`.
El `response_class` solo se usará para documentar el OpenAPI *path operation*, pero tu `Response` se usará tal cual.
#### Devuelve un `HTMLResponse` directamente { #return-an-htmlresponse-directly }
Por ejemplo, podría ser algo así:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
En este ejemplo, la función `generate_html_response()` ya genera y devuelve una `Response` en lugar de devolver el HTML en un `str`.
Al devolver el resultado de llamar a `generate_html_response()`, ya estás devolviendo una `Response` que sobrescribirá el comportamiento por defecto de **FastAPI**.
Pero como pasaste `HTMLResponse` en el `response_class` también, **FastAPI** sabrá cómo documentarlo en OpenAPI y la documentación interactiva como HTML con `text/html`:
## Responses disponibles { #available-responses }
Aquí hay algunos de los responses disponibles.
Ten en cuenta que puedes usar `Response` para devolver cualquier otra cosa, o incluso crear una sub-clase personalizada.
/// note | Nota Técnica
También podrías usar `from starlette.responses import HTMLResponse`.
**FastAPI** proporciona los mismos `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette.
///
### `Response` { #response }
La clase principal `Response`, todos los otros responses heredan de ella.
Puedes devolverla directamente.
Acepta los siguientes parámetros:
* `content` - Un `str` o `bytes`.
* `status_code` - Un código de estado HTTP `int`.
* `headers` - Un `dict` de strings.
* `media_type` - Un `str` que da el media type. Por ejemplo, `"text/html"`.
FastAPI (de hecho Starlette) incluirá automáticamente un header Content-Length. También incluirá un header Content-Type, basado en el `media_type` y añadiendo un conjunto de caracteres para tipos de texto.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Toma algún texto o bytes y devuelve un response HTML, como leíste arriba.
### `PlainTextResponse` { #plaintextresponse }
Toma algún texto o bytes y devuelve un response de texto plano.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Toma algunos datos y devuelve un response codificado como `application/json`.
Este es el response usado por defecto en **FastAPI**, como leíste arriba.
/// note | Nota Técnica
Pero si declaras un response model o un tipo de retorno, eso se usará directamente para serializar los datos a JSON, y se devolverá directamente un response con el media type correcto para JSON, sin usar la clase `JSONResponse`.
Esta es la forma ideal de obtener el mejor rendimiento.
///
### `RedirectResponse` { #redirectresponse }
Devuelve una redirección HTTP. Usa un código de estado 307 (Redirección Temporal) por defecto.
Puedes devolver un `RedirectResponse` directamente:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
O puedes usarlo en el parámetro `response_class`:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation* function.
En este caso, el `status_code` utilizado será el por defecto para `RedirectResponse`, que es `307`.
---
También puedes usar el parámetro `status_code` combinado con el parámetro `response_class`:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Toma un generador `async` o un generador/iterador normal (una función con `yield`) y transmite el cuerpo del response.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Nota Técnica
Una tarea `async` solo puede cancelarse cuando llega a un `await`. Si no hay `await`, el generador (función con `yield`) no se puede cancelar correctamente y puede seguir ejecutándose incluso después de solicitar la cancelación.
Como este pequeño ejemplo no necesita ninguna sentencia `await`, añadimos un `await anyio.sleep(0)` para darle al loop de eventos la oportunidad de manejar la cancelación.
Esto sería aún más importante con streams grandes o infinitos.
///
/// tip | Consejo
En lugar de devolver un `StreamingResponse` directamente, probablemente deberías seguir el estilo en [Stream Data](./stream-data.md), es mucho más conveniente y maneja la cancelación por detrás de escena por ti.
Si estás transmitiendo JSON Lines, sigue el tutorial [Stream JSON Lines](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Transmite un archivo asincrónicamente como response.
Toma un conjunto diferente de argumentos para crear un instance que los otros tipos de response:
* `path` - La path del archivo para el archivo a transmitir.
* `headers` - Cualquier header personalizado para incluir, como un diccionario.
* `media_type` - Un string que da el media type. Si no se establece, se usará el nombre de archivo o la path para inferir un media type.
* `filename` - Si se establece, se incluirá en el response `Content-Disposition`.
Los responses de archivos incluirán los headers apropiados `Content-Length`, `Last-Modified` y `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
También puedes usar el parámetro `response_class`:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
En este caso, puedes devolver la path del archivo directamente desde tu *path operation* function.
## Clase de response personalizada { #custom-response-class }
Puedes crear tu propia clase de response personalizada, heredando de `Response` y usándola.
Por ejemplo, digamos que quieres usar [`orjson`](https://github.com/ijl/orjson) con algunas configuraciones.
Digamos que quieres que devuelva JSON con sangría y formato, por lo que quieres usar la opción de orjson `orjson.OPT_INDENT_2`.
Podrías crear un `CustomORJSONResponse`. Lo principal que tienes que hacer es crear un método `Response.render(content)` que devuelva el contenido como `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Ahora en lugar de devolver:
```json
{"message": "Hello World"}
```
...este response devolverá:
```json
{
"message": "Hello World"
}
```
Por supuesto, probablemente encontrarás formas mucho mejores de aprovechar esto que formatear JSON. 😉
### `orjson` o Response Model { #orjson-or-response-model }
Si lo que buscas es rendimiento, probablemente te convenga más usar un [Response Model](../tutorial/response-model.md) que un response con `orjson`.
Con un response model, FastAPI usará Pydantic para serializar los datos a JSON, sin pasos intermedios, como convertirlos con `jsonable_encoder`, que ocurriría en cualquier otro caso.
Y por debajo, Pydantic usa los mismos mecanismos en Rust que `orjson` para serializar a JSON, así que ya obtendrás el mejor rendimiento con un response model.
## Clase de response por defecto { #default-response-class }
Al crear una instance de la clase **FastAPI** o un `APIRouter`, puedes especificar qué clase de response usar por defecto.
El parámetro que define esto es `default_response_class`.
En el ejemplo a continuación, **FastAPI** usará `HTMLResponse` por defecto, en todas las *path operations*, en lugar de JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Consejo
Todavía puedes sobrescribir `response_class` en *path operations* como antes.
///
## Documentación adicional { #additional-documentation }
También puedes declarar el media type y muchos otros detalles en OpenAPI usando `responses`: [Responses Adicionales en OpenAPI](additional-responses.md).
================================================
FILE: docs/es/docs/advanced/dataclasses.md
================================================
# Usando Dataclasses { #using-dataclasses }
FastAPI está construido sobre **Pydantic**, y te he estado mostrando cómo usar modelos de Pydantic para declarar requests y responses.
Pero FastAPI también soporta el uso de [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) de la misma manera:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene [soporte interno para `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Así que, incluso con el código anterior que no usa Pydantic explícitamente, FastAPI está usando Pydantic para convertir esos dataclasses estándar en su propia versión de dataclasses de Pydantic.
Y por supuesto, soporta lo mismo:
* validación de datos
* serialización de datos
* documentación de datos, etc.
Esto funciona de la misma manera que con los modelos de Pydantic. Y en realidad se logra de la misma manera internamente, utilizando Pydantic.
/// info | Información
Ten en cuenta que los dataclasses no pueden hacer todo lo que los modelos de Pydantic pueden hacer.
Así que, podrías necesitar seguir usando modelos de Pydantic.
Pero si tienes un montón de dataclasses por ahí, este es un buen truco para usarlos para potenciar una API web usando FastAPI. 🤓
///
## Dataclasses en `response_model` { #dataclasses-in-response-model }
También puedes usar `dataclasses` en el parámetro `response_model`:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
El dataclass será automáticamente convertido a un dataclass de Pydantic.
De esta manera, su esquema aparecerá en la interfaz de usuario de la documentación de la API:
## Dataclasses en Estructuras de Datos Anidadas { #dataclasses-in-nested-data-structures }
También puedes combinar `dataclasses` con otras anotaciones de tipos para crear estructuras de datos anidadas.
En algunos casos, todavía podrías tener que usar la versión de `dataclasses` de Pydantic. Por ejemplo, si tienes errores con la documentación de la API generada automáticamente.
En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Todavía importamos `field` de los `dataclasses` estándar.
2. `pydantic.dataclasses` es un reemplazo directo para `dataclasses`.
3. El dataclass `Author` incluye una lista de dataclasses `Item`.
4. El dataclass `Author` se usa como el parámetro `response_model`.
5. Puedes usar otras anotaciones de tipos estándar con dataclasses como el request body.
En este caso, es una lista de dataclasses `Item`.
6. Aquí estamos regresando un diccionario que contiene `items`, que es una lista de dataclasses.
FastAPI todavía es capaz de serializar los datos a JSON.
7. Aquí el `response_model` está usando una anotación de tipo de una lista de dataclasses `Author`.
Nuevamente, puedes combinar `dataclasses` con anotaciones de tipos estándar.
8. Nota que esta *path operation function* usa `def` regular en lugar de `async def`.
Como siempre, en FastAPI puedes combinar `def` y `async def` según sea necesario.
Si necesitas un repaso sobre cuándo usar cuál, revisa la sección _"¿Con prisa?"_ en la documentación sobre [`async` y `await`](../async.md#in-a-hurry).
9. Esta *path operation function* no está devolviendo dataclasses (aunque podría), sino una lista de diccionarios con datos internos.
FastAPI usará el parámetro `response_model` (que incluye dataclasses) para convertir el response.
Puedes combinar `dataclasses` con otras anotaciones de tipos en muchas combinaciones diferentes para formar estructuras de datos complejas.
Revisa las anotaciones en el código arriba para ver más detalles específicos.
## Aprende Más { #learn-more }
También puedes combinar `dataclasses` con otros modelos de Pydantic, heredar de ellos, incluirlos en tus propios modelos, etc.
Para saber más, revisa la [documentación de Pydantic sobre dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Versión { #version }
Esto está disponible desde la versión `0.67.0` de FastAPI. 🔖
================================================
FILE: docs/es/docs/advanced/events.md
================================================
# Eventos de Lifespan { #lifespan-events }
Puedes definir lógica (código) que debería ser ejecutada antes de que la aplicación **inicie**. Esto significa que este código será ejecutado **una vez**, **antes** de que la aplicación **comience a recibir requests**.
De la misma manera, puedes definir lógica (código) que debería ser ejecutada cuando la aplicación esté **cerrándose**. En este caso, este código será ejecutado **una vez**, **después** de haber manejado posiblemente **muchos requests**.
Debido a que este código se ejecuta antes de que la aplicación **comience** a tomar requests, y justo después de que **termine** de manejarlos, cubre todo el **lifespan** de la aplicación (la palabra "lifespan" será importante en un momento 😉).
Esto puede ser muy útil para configurar **recursos** que necesitas usar para toda la app, y que son **compartidos** entre requests, y/o que necesitas **limpiar** después. Por ejemplo, un pool de conexiones a una base de datos, o cargando un modelo de machine learning compartido.
## Caso de Uso { #use-case }
Empecemos con un ejemplo de **caso de uso** y luego veamos cómo resolverlo con esto.
Imaginemos que tienes algunos **modelos de machine learning** que quieres usar para manejar requests. 🤖
Los mismos modelos son compartidos entre requests, por lo que no es un modelo por request, o uno por usuario o algo similar.
Imaginemos que cargar el modelo puede **tomar bastante tiempo**, porque tiene que leer muchos **datos del disco**. Entonces no quieres hacerlo para cada request.
Podrías cargarlo en el nivel superior del módulo/archivo, pero eso también significaría que **cargaría el modelo** incluso si solo estás ejecutando una simple prueba automatizada, entonces esa prueba sería **lenta** porque tendría que esperar a que el modelo se cargue antes de poder ejecutar una parte independiente del código.
Eso es lo que resolveremos, vamos a cargar el modelo antes de que los requests sean manejados, pero solo justo antes de que la aplicación comience a recibir requests, no mientras el código se está cargando.
## Lifespan { #lifespan }
Puedes definir esta lógica de *startup* y *shutdown* usando el parámetro `lifespan` de la app de `FastAPI`, y un "context manager" (te mostraré lo que es en un momento).
Comencemos con un ejemplo y luego veámoslo en detalle.
Creamos una función asíncrona `lifespan()` con `yield` así:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Aquí estamos simulando la operación costosa de *startup* de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes del `yield`. Este código será ejecutado **antes** de que la aplicación **comience a tomar requests**, durante el *startup*.
Y luego, justo después del `yield`, quitaremos el modelo de memoria. Este código será ejecutado **después** de que la aplicación **termine de manejar requests**, justo antes del *shutdown*. Esto podría, por ejemplo, liberar recursos como la memoria o una GPU.
/// tip | Consejo
El `shutdown` ocurriría cuando estás **deteniendo** la aplicación.
Quizás necesites iniciar una nueva versión, o simplemente te cansaste de ejecutarla. 🤷
///
### Función de Lifespan { #lifespan-function }
Lo primero que hay que notar es que estamos definiendo una función asíncrona con `yield`. Esto es muy similar a las Dependencias con `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
La primera parte de la función, antes del `yield`, será ejecutada **antes** de que la aplicación comience.
Y la parte después del `yield` será ejecutada **después** de que la aplicación haya terminado.
### Async Context Manager { #async-context-manager }
Si revisas, la función está decorada con un `@asynccontextmanager`.
Eso convierte a la función en algo llamado un "**async context manager**".
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Un **context manager** en Python es algo que puedes usar en un statement `with`, por ejemplo, `open()` puede ser usado como un context manager:
```Python
with open("file.txt") as file:
file.read()
```
En versiones recientes de Python, también hay un **async context manager**. Lo usarías con `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Cuando creas un context manager o un async context manager como arriba, lo que hace es que, antes de entrar al bloque `with`, ejecutará el código antes del `yield`, y al salir del bloque `with`, ejecutará el código después del `yield`.
En nuestro ejemplo de código arriba, no lo usamos directamente, pero se lo pasamos a FastAPI para que lo use.
El parámetro `lifespan` de la app de `FastAPI` toma un **async context manager**, por lo que podemos pasar nuestro nuevo `lifespan` async context manager a él.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Eventos Alternativos (obsoleto) { #alternative-events-deprecated }
/// warning | Advertencia
La forma recomendada de manejar el *startup* y el *shutdown* es usando el parámetro `lifespan` de la app de `FastAPI` como se describió arriba. Si proporcionas un parámetro `lifespan`, los manejadores de eventos `startup` y `shutdown` ya no serán llamados. Es solo `lifespan` o solo los eventos, no ambos.
Probablemente puedas saltarte esta parte.
///
Hay una forma alternativa de definir esta lógica para ser ejecutada durante el *startup* y durante el *shutdown*.
Puedes definir manejadores de eventos (funciones) que necesitan ser ejecutadas antes de que la aplicación se inicie, o cuando la aplicación se está cerrando.
Estas funciones pueden ser declaradas con `async def` o `def` normal.
### Evento `startup` { #startup-event }
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento `"startup"`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
En este caso, la función manejadora del evento `startup` inicializará los ítems de la "base de datos" (solo un `dict`) con algunos valores.
Puedes añadir más de un manejador de eventos.
Y tu aplicación no comenzará a recibir requests hasta que todos los manejadores de eventos `startup` hayan completado.
### Evento `shutdown` { #shutdown-event }
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento `"shutdown"`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Aquí, la función manejadora del evento `shutdown` escribirá una línea de texto `"Application shutdown"` a un archivo `log.txt`.
/// info | Información
En la función `open()`, el `mode="a"` significa "añadir", por lo tanto, la línea será añadida después de lo que sea que esté en ese archivo, sin sobrescribir el contenido anterior.
///
/// tip | Consejo
Nota que en este caso estamos usando una función estándar de Python `open()` que interactúa con un archivo.
Entonces, involucra I/O (entrada/salida), que requiere "esperar" para que las cosas se escriban en el disco.
Pero `open()` no usa `async` y `await`.
Por eso, declaramos la función manejadora del evento con `def` estándar en vez de `async def`.
///
### `startup` y `shutdown` juntos { #startup-and-shutdown-together }
Hay una gran posibilidad de que la lógica para tu *startup* y *shutdown* esté conectada, podrías querer iniciar algo y luego finalizarlo, adquirir un recurso y luego liberarlo, etc.
Hacer eso en funciones separadas que no comparten lógica o variables juntas es más difícil ya que necesitarías almacenar valores en variables globales o trucos similares.
Debido a eso, ahora se recomienda en su lugar usar el `lifespan` como se explicó arriba.
## Detalles Técnicos { #technical-details }
Solo un detalle técnico para los nerds curiosos. 🤓
Por debajo, en la especificación técnica ASGI, esto es parte del [Protocolo de Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), y define eventos llamados `startup` y `shutdown`.
/// info | Información
Puedes leer más sobre los manejadores `lifespan` de Starlette en [la documentación de `Lifespan` de Starlette](https://www.starlette.dev/lifespan/).
Incluyendo cómo manejar el estado de lifespan que puede ser usado en otras áreas de tu código.
///
## Sub Aplicaciones { #sub-applications }
🚨 Ten en cuenta que estos eventos de lifespan (startup y shutdown) solo serán ejecutados para la aplicación principal, no para [Sub Aplicaciones - Mounts](sub-applications.md).
================================================
FILE: docs/es/docs/advanced/generate-clients.md
================================================
# Generando SDKs { #generating-sdks }
Como **FastAPI** está basado en la especificación **OpenAPI**, sus APIs se pueden describir en un formato estándar que muchas herramientas entienden.
Esto facilita generar **documentación** actualizada, paquetes de cliente (**SDKs**) en múltiples lenguajes y **escribir pruebas** o **flujos de automatización** que se mantengan sincronizados con tu código.
En esta guía, aprenderás a generar un **SDK de TypeScript** para tu backend con FastAPI.
## Generadores de SDKs de código abierto { #open-source-sdk-generators }
Una opción versátil es el [OpenAPI Generator](https://openapi-generator.tech/), que soporta **muchos lenguajes de programación** y puede generar SDKs a partir de tu especificación OpenAPI.
Para **clientes de TypeScript**, [Hey API](https://heyapi.dev/) es una solución diseñada específicamente, que ofrece una experiencia optimizada para el ecosistema de TypeScript.
Puedes descubrir más generadores de SDK en [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Consejo
FastAPI genera automáticamente especificaciones **OpenAPI 3.1**, así que cualquier herramienta que uses debe soportar esta versión.
///
## Generadores de SDKs de sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
Esta sección destaca soluciones **respaldadas por empresas** y **venture-backed** de compañías que sponsorean FastAPI. Estos productos ofrecen **funcionalidades adicionales** e **integraciones** además de SDKs generados de alta calidad.
Al ✨ [**sponsorear FastAPI**](../help-fastapi.md#sponsor-the-author) ✨, estas compañías ayudan a asegurar que el framework y su **ecosistema** se mantengan saludables y **sustentables**.
Su sponsorship también demuestra un fuerte compromiso con la **comunidad** de FastAPI (tú), mostrando que no solo les importa ofrecer un **gran servicio**, sino también apoyar un **framework robusto y próspero**, FastAPI. 🙇
Por ejemplo, podrías querer probar:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Algunas de estas soluciones también pueden ser open source u ofrecer niveles gratuitos, así que puedes probarlas sin un compromiso financiero. Hay otros generadores de SDK comerciales disponibles y se pueden encontrar en línea. 🤓
## Crea un SDK de TypeScript { #create-a-typescript-sdk }
Empecemos con una aplicación simple de FastAPI:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Nota que las *path operations* definen los modelos que usan para el payload del request y el payload del response, usando los modelos `Item` y `ResponseMessage`.
### Documentación de la API { #api-docs }
Si vas a `/docs`, verás que tiene los **esquemas** para los datos a enviar en requests y recibir en responses:
Puedes ver esos esquemas porque fueron declarados con los modelos en la app.
Esa información está disponible en el **OpenAPI schema** de la app, y luego se muestra en la documentación de la API.
Y esa misma información de los modelos que está incluida en OpenAPI es lo que puede usarse para **generar el código del cliente**.
### Hey API { #hey-api }
Una vez que tenemos una app de FastAPI con los modelos, podemos usar Hey API para generar un cliente de TypeScript. La forma más rápida de hacerlo es con npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Esto generará un SDK de TypeScript en `./src/client`.
Puedes aprender cómo [instalar `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started) y leer sobre el [output generado](https://heyapi.dev/openapi-ts/output) en su sitio web.
### Usar el SDK { #using-the-sdk }
Ahora puedes importar y usar el código del cliente. Podría verse así, nota que tienes autocompletado para los métodos:
También obtendrás autocompletado para el payload a enviar:
/// tip | Consejo
Nota el autocompletado para `name` y `price`, que fue definido en la aplicación de FastAPI, en el modelo `Item`.
///
Tendrás errores en línea para los datos que envíes:
El objeto de response también tendrá autocompletado:
## App de FastAPI con tags { #fastapi-app-with-tags }
En muchos casos tu app de FastAPI será más grande, y probablemente usarás tags para separar diferentes grupos de *path operations*.
Por ejemplo, podrías tener una sección para **items** y otra sección para **users**, y podrían estar separadas por tags:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Genera un Cliente TypeScript con tags { #generate-a-typescript-client-with-tags }
Si generas un cliente para una app de FastAPI usando tags, normalmente también separará el código del cliente basándose en los tags.
De esta manera podrás tener las cosas ordenadas y agrupadas correctamente para el código del cliente:
En este caso tienes:
* `ItemsService`
* `UsersService`
### Nombres de los métodos del cliente { #client-method-names }
Ahora mismo los nombres de los métodos generados como `createItemItemsPost` no se ven muy limpios:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...eso es porque el generador del cliente usa el **operation ID** interno de OpenAPI para cada *path operation*.
OpenAPI requiere que cada operation ID sea único a través de todas las *path operations*, por lo que FastAPI usa el **nombre de la función**, el **path**, y el **método/operación HTTP** para generar ese operation ID, porque de esa manera puede asegurarse de que los operation IDs sean únicos.
Pero te mostraré cómo mejorar eso a continuación. 🤓
## Operation IDs personalizados y mejores nombres de métodos { #custom-operation-ids-and-better-method-names }
Puedes **modificar** la forma en que estos operation IDs son **generados** para hacerlos más simples y tener **nombres de métodos más simples** en los clientes.
En este caso tendrás que asegurarte de que cada operation ID sea **único** de alguna otra manera.
Por ejemplo, podrías asegurarte de que cada *path operation* tenga un tag, y luego generar el operation ID basado en el **tag** y el **name** de la *path operation* (el nombre de la función).
### Función personalizada para generar ID único { #custom-generate-unique-id-function }
FastAPI usa un **ID único** para cada *path operation*, se usa para el **operation ID** y también para los nombres de cualquier modelo personalizado necesario, para requests o responses.
Puedes personalizar esa función. Toma un `APIRoute` y retorna un string.
Por ejemplo, aquí está usando el primer tag (probablemente tendrás solo un tag) y el nombre de la *path operation* (el nombre de la función).
Puedes entonces pasar esa función personalizada a **FastAPI** como el parámetro `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Genera un Cliente TypeScript con operation IDs personalizados { #generate-a-typescript-client-with-custom-operation-ids }
Ahora, si generas el cliente de nuevo, verás que tiene los nombres de métodos mejorados:
Como ves, los nombres de métodos ahora tienen el tag y luego el nombre de la función, ahora no incluyen información del path de la URL y la operación HTTP.
### Preprocesa la especificación OpenAPI para el generador de clientes { #preprocess-the-openapi-specification-for-the-client-generator }
El código generado aún tiene algo de **información duplicada**.
Ya sabemos que este método está relacionado con los **items** porque esa palabra está en el `ItemsService` (tomado del tag), pero aún tenemos el nombre del tag prefijado en el nombre del método también. 😕
Probablemente aún querremos mantenerlo para OpenAPI en general, ya que eso asegurará que los operation IDs sean **únicos**.
Pero para el cliente generado podríamos **modificar** los operation IDs de OpenAPI justo antes de generar los clientes, solo para hacer esos nombres de métodos más bonitos y **limpios**.
Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podríamos **remover ese tag prefijado** con un script como este:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Con eso, los operation IDs serían renombrados de cosas como `items-get_items` a solo `get_items`, de esa manera el generador del cliente puede generar nombres de métodos más simples.
### Genera un Cliente TypeScript con el OpenAPI preprocesado { #generate-a-typescript-client-with-the-preprocessed-openapi }
Como el resultado final ahora está en un archivo `openapi.json`, necesitas actualizar la ubicación de la entrada:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Después de generar el nuevo cliente, ahora tendrías nombres de métodos **limpios**, con todo el **autocompletado**, **errores en línea**, etc:
## Beneficios { #benefits }
Cuando uses los clientes generados automáticamente obtendrás **autocompletado** para:
* Métodos.
* Payloads de request en el body, parámetros de query, etc.
* Payloads de response.
También tendrás **errores en línea** para todo.
Y cada vez que actualices el código del backend, y **regeneres** el frontend, tendrás las nuevas *path operations* disponibles como métodos, las antiguas eliminadas, y cualquier otro cambio se reflejará en el código generado. 🤓
Esto también significa que si algo cambió será **reflejado** automáticamente en el código del cliente. Y si haces **build** del cliente, dará error si tienes algún **desajuste** en los datos utilizados.
Así que, **detectarás muchos errores** muy temprano en el ciclo de desarrollo en lugar de tener que esperar a que los errores se muestren a tus usuarios finales en producción para luego intentar depurar dónde está el problema. ✨
================================================
FILE: docs/es/docs/advanced/index.md
================================================
# Guía avanzada del usuario { #advanced-user-guide }
## Funcionalidades adicionales { #additional-features }
El [Tutorial - Guía del usuario](../tutorial/index.md) principal debería ser suficiente para darte un recorrido por todas las funcionalidades principales de **FastAPI**.
En las siguientes secciones verás otras opciones, configuraciones y funcionalidades adicionales.
/// tip | Consejo
Las siguientes secciones **no son necesariamente "avanzadas"**.
Y es posible que para tu caso de uso, la solución esté en una de ellas.
///
## Lee primero el Tutorial { #read-the-tutorial-first }
Aún podrías usar la mayoría de las funcionalidades en **FastAPI** con el conocimiento del [Tutorial - Guía del usuario](../tutorial/index.md) principal.
Y las siguientes secciones asumen que ya lo leíste y que conoces esas ideas principales.
================================================
FILE: docs/es/docs/advanced/json-base64-bytes.md
================================================
# JSON con Bytes como Base64 { #json-with-bytes-as-base64 }
Si tu app necesita recibir y enviar datos JSON, pero necesitas incluir datos binarios en él, puedes codificarlos como base64.
## Base64 vs Archivos { #base64-vs-files }
Considera primero si puedes usar [Archivos en request](../tutorial/request-files.md) para subir datos binarios y [Response personalizada - FileResponse](./custom-response.md#fileresponse--fileresponse-) para enviar datos binarios, en lugar de codificarlos en JSON.
JSON solo puede contener strings codificados en UTF-8, así que no puede contener bytes crudos.
Base64 puede codificar datos binarios en strings, pero para hacerlo necesita usar más caracteres que los datos binarios originales, así que normalmente sería menos eficiente que los archivos normales.
Usa base64 solo si definitivamente necesitas incluir datos binarios en JSON y no puedes usar archivos para eso.
## Pydantic `bytes` { #pydantic-bytes }
Puedes declarar un modelo de Pydantic con campos `bytes`, y luego usar `val_json_bytes` en la configuración del modelo para indicarle que use base64 para validar datos JSON de entrada; como parte de esa validación decodificará el string base64 en bytes.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Si revisas `/docs`, verás que el campo `data` espera bytes codificados en base64:
Podrías enviar un request como:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Consejo
`aGVsbG8=` es la codificación base64 de `hello`.
///
Y luego Pydantic decodificará el string base64 y te dará los bytes originales en el campo `data` del modelo.
Recibirás una response como:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` para datos de salida { #pydantic-bytes-for-output-data }
También puedes usar campos `bytes` con `ser_json_bytes` en la configuración del modelo para datos de salida, y Pydantic serializará los bytes como base64 al generar la response JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` para datos de entrada y salida { #pydantic-bytes-for-input-and-output-data }
Y por supuesto, puedes usar el mismo modelo configurado para usar base64 para manejar tanto la entrada (*validate*) con `val_json_bytes` como la salida (*serialize*) con `ser_json_bytes` al recibir y enviar datos JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/es/docs/advanced/middleware.md
================================================
# Middleware Avanzado { #advanced-middleware }
En el tutorial principal leíste cómo agregar [Middleware Personalizado](../tutorial/middleware.md) a tu aplicación.
Y luego también leíste cómo manejar [CORS con el `CORSMiddleware`](../tutorial/cors.md).
En esta sección veremos cómo usar otros middlewares.
## Agregando middlewares ASGI { #adding-asgi-middlewares }
Como **FastAPI** está basado en Starlette e implementa la especificación ASGI, puedes usar cualquier middleware ASGI.
Un middleware no tiene que estar hecho para FastAPI o Starlette para funcionar, siempre que siga la especificación ASGI.
En general, los middlewares ASGI son clases que esperan recibir una aplicación ASGI como primer argumento.
Entonces, en la documentación de middlewares ASGI de terceros probablemente te indicarán que hagas algo como:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Pero FastAPI (en realidad Starlette) proporciona una forma más simple de hacerlo que asegura que los middlewares internos manejen errores del servidor y los controladores de excepciones personalizadas funcionen correctamente.
Para eso, usas `app.add_middleware()` (como en el ejemplo para CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` recibe una clase de middleware como primer argumento y cualquier argumento adicional que se le quiera pasar al middleware.
## Middlewares integrados { #integrated-middlewares }
**FastAPI** incluye varios middlewares para casos de uso común, veremos a continuación cómo usarlos.
/// note | Detalles Técnicos
Para los próximos ejemplos, también podrías usar `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** proporciona varios middlewares en `fastapi.middleware` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los middlewares disponibles provienen directamente de Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Impone que todas las requests entrantes deben ser `https` o `wss`.
Cualquier request entrante a `http` o `ws` será redirigida al esquema seguro.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Impone que todas las requests entrantes tengan correctamente configurado el header `Host`, para proteger contra ataques de HTTP Host Header.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Se soportan los siguientes argumentos:
* `allowed_hosts` - Una list de nombres de dominio que deberían ser permitidos como nombres de host. Se soportan dominios comodín como `*.example.com` para hacer coincidir subdominios. Para permitir cualquier nombre de host, usa `allowed_hosts=["*"]` u omite el middleware.
* `www_redirect` - Si se establece en True, las requests a versiones sin www de los hosts permitidos serán redirigidas a sus equivalentes con www. Por defecto es `True`.
Si una request entrante no se valida correctamente, se enviará un response `400`.
## `GZipMiddleware` { #gzipmiddleware }
Maneja responses GZip para cualquier request que incluya `"gzip"` en el header `Accept-Encoding`.
El middleware manejará tanto responses estándar como en streaming.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Se soportan los siguientes argumentos:
* `minimum_size` - No comprimir con GZip responses que sean más pequeñas que este tamaño mínimo en bytes. Por defecto es `500`.
* `compresslevel` - Usado durante la compresión GZip. Es un entero que varía de 1 a 9. Por defecto es `9`. Un valor más bajo resulta en una compresión más rápida pero archivos más grandes, mientras que un valor más alto resulta en una compresión más lenta pero archivos más pequeños.
## Otros middlewares { #other-middlewares }
Hay muchos otros middlewares ASGI.
Por ejemplo:
* [`ProxyHeadersMiddleware` de Uvicorn](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Para ver otros middlewares disponibles, revisa [la documentación de Middleware de Starlette](https://www.starlette.dev/middleware/) y la [Lista ASGI Awesome](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/es/docs/advanced/openapi-callbacks.md
================================================
# Callbacks de OpenAPI { #openapi-callbacks }
Podrías crear una API con una *path operation* que podría desencadenar un request a una *API externa* creada por alguien más (probablemente el mismo desarrollador que estaría *usando* tu API).
El proceso que ocurre cuando tu aplicación API llama a la *API externa* se llama un "callback". Porque el software que escribió el desarrollador externo envía un request a tu API y luego tu API hace un *callback*, enviando un request a una *API externa* (que probablemente fue creada por el mismo desarrollador).
En este caso, podrías querer documentar cómo esa API externa *debería* verse. Qué *path operation* debería tener, qué cuerpo debería esperar, qué response debería devolver, etc.
## Una aplicación con callbacks { #an-app-with-callbacks }
Veamos todo esto con un ejemplo.
Imagina que desarrollas una aplicación que permite crear facturas.
Estas facturas tendrán un `id`, `title` (opcional), `customer` y `total`.
El usuario de tu API (un desarrollador externo) creará una factura en tu API con un request POST.
Luego tu API (imaginemos):
* Enviará la factura a algún cliente del desarrollador externo.
* Recogerá el dinero.
* Enviará una notificación de vuelta al usuario de la API (el desarrollador externo).
* Esto se hará enviando un request POST (desde *tu API*) a alguna *API externa* proporcionada por ese desarrollador externo (este es el "callback").
## La aplicación normal de **FastAPI** { #the-normal-fastapi-app }
Primero veamos cómo se vería la aplicación API normal antes de agregar el callback.
Tendrá una *path operation* que recibirá un cuerpo `Invoice`, y un parámetro de query `callback_url` que contendrá la URL para el callback.
Esta parte es bastante normal, probablemente ya estés familiarizado con la mayor parte del código:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Consejo
El parámetro de query `callback_url` utiliza un tipo [Url](https://docs.pydantic.dev/latest/api/networks/) de Pydantic.
///
Lo único nuevo es `callbacks=invoices_callback_router.routes` como un argumento para el *decorador de path operation*. Veremos qué es eso a continuación.
## Documentar el callback { #documenting-the-callback }
El código real del callback dependerá mucho de tu propia aplicación API.
Y probablemente variará mucho de una aplicación a otra.
Podría ser solo una o dos líneas de código, como:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Pero posiblemente la parte más importante del callback es asegurarse de que el usuario de tu API (el desarrollador externo) implemente la *API externa* correctamente, de acuerdo con los datos que *tu API* va a enviar en el request body del callback, etc.
Así que, lo que haremos a continuación es agregar el código para documentar cómo debería verse esa *API externa* para recibir el callback de *tu API*.
Esa documentación aparecerá en la Swagger UI en `/docs` en tu API, y permitirá a los desarrolladores externos saber cómo construir la *API externa*.
Este ejemplo no implementa el callback en sí (eso podría ser solo una línea de código), solo la parte de documentación.
/// tip | Consejo
El callback real es solo un request HTTP.
Cuando implementes el callback tú mismo, podrías usar algo como [HTTPX](https://www.python-httpx.org) o [Requests](https://requests.readthedocs.io/).
///
## Escribe el código de documentación del callback { #write-the-callback-documentation-code }
Este código no se ejecutará en tu aplicación, solo lo necesitamos para *documentar* cómo debería verse esa *API externa*.
Pero ya sabes cómo crear fácilmente documentación automática para una API con **FastAPI**.
Así que vamos a usar ese mismo conocimiento para documentar cómo debería verse la *API externa*... creando la(s) *path operation(s)* que la API externa debería implementar (las que tu API va a llamar).
/// tip | Consejo
Cuando escribas el código para documentar un callback, podría ser útil imaginar que eres ese *desarrollador externo*. Y que actualmente estás implementando la *API externa*, no *tu API*.
Adoptar temporalmente este punto de vista (del *desarrollador externo*) puede ayudarte a sentir que es más obvio dónde poner los parámetros, el modelo de Pydantic para el body, para el response, etc. para esa *API externa*.
///
### Crea un `APIRouter` de callback { #create-a-callback-apirouter }
Primero crea un nuevo `APIRouter` que contendrá uno o más callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Crea la *path operation* del callback { #create-the-callback-path-operation }
Para crear la *path operation* del callback usa el mismo `APIRouter` que creaste arriba.
Debería verse como una *path operation* normal de FastAPI:
* Probablemente debería tener una declaración del body que debería recibir, por ejemplo `body: InvoiceEvent`.
* Y también podría tener una declaración del response que debería devolver, por ejemplo `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Hay 2 diferencias principales respecto a una *path operation* normal:
* No necesita tener ningún código real, porque tu aplicación nunca llamará a este código. Solo se usa para documentar la *API externa*. Así que, la función podría simplemente tener `pass`.
* El *path* puede contener una [expresión OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (ver más abajo) donde puede usar variables con parámetros y partes del request original enviado a *tu API*.
### La expresión del path del callback { #the-callback-path-expression }
El *path* del callback puede tener una [expresión OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) que puede contener partes del request original enviado a *tu API*.
En este caso, es el `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Entonces, si el usuario de tu API (el desarrollador externo) envía un request a *tu API* a:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
con un JSON body de:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
luego *tu API* procesará la factura y, en algún momento después, enviará un request de callback al `callback_url` (la *API externa*):
```
https://www.external.org/events/invoices/2expen51ve
```
con un JSON body que contiene algo como:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
y esperaría un response de esa *API externa* con un JSON body como:
```JSON
{
"ok": true
}
```
/// tip | Consejo
Observa cómo la URL del callback utilizada contiene la URL recibida como parámetro de query en `callback_url` (`https://www.external.org/events`) y también el `id` de la factura desde dentro del JSON body (`2expen51ve`).
///
### Agrega el router de callback { #add-the-callback-router }
En este punto tienes las *path operation(s)* del callback necesarias (las que el *desarrollador externo* debería implementar en la *API externa*) en el router de callback que creaste arriba.
Ahora usa el parámetro `callbacks` en el *decorador de path operation de tu API* para pasar el atributo `.routes` (que en realidad es solo un `list` de rutas/*path operations*) de ese router de callback:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Consejo
Observa que no estás pasando el router en sí (`invoices_callback_router`) a `callback=`, sino el atributo `.routes`, como en `invoices_callback_router.routes`.
///
### Revisa la documentación { #check-the-docs }
Ahora puedes iniciar tu aplicación e ir a [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás tu documentación incluyendo una sección de "Callbacks" para tu *path operation* que muestra cómo debería verse la *API externa*:
================================================
FILE: docs/es/docs/advanced/openapi-webhooks.md
================================================
# Webhooks de OpenAPI { #openapi-webhooks }
Hay casos donde quieres decirle a los **usuarios** de tu API que tu aplicación podría llamar a *su* aplicación (enviando una request) con algunos datos, normalmente para **notificar** de algún tipo de **evento**.
Esto significa que en lugar del proceso normal de tus usuarios enviando requests a tu API, es **tu API** (o tu aplicación) la que podría **enviar requests a su sistema** (a su API, su aplicación).
Esto normalmente se llama un **webhook**.
## Pasos de los webhooks { #webhooks-steps }
El proceso normalmente es que **tú defines** en tu código cuál es el mensaje que enviarás, el **body de la request**.
También defines de alguna manera en qué **momentos** tu aplicación enviará esas requests o eventos.
Y **tus usuarios** definen de alguna manera (por ejemplo en un panel web en algún lugar) el **URL** donde tu aplicación debería enviar esas requests.
Toda la **lógica** sobre cómo registrar los URLs para webhooks y el código para realmente enviar esas requests depende de ti. Lo escribes como quieras en **tu propio código**.
## Documentando webhooks con **FastAPI** y OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
Con **FastAPI**, usando OpenAPI, puedes definir los nombres de estos webhooks, los tipos de operaciones HTTP que tu aplicación puede enviar (por ejemplo, `POST`, `PUT`, etc.) y los **bodies** de las requests que tu aplicación enviaría.
Esto puede hacer mucho más fácil para tus usuarios **implementar sus APIs** para recibir tus requests de **webhook**, incluso podrían ser capaces de autogenerar algo de su propio código de API.
/// info | Información
Los webhooks están disponibles en OpenAPI 3.1.0 y superiores, soportados por FastAPI `0.99.0` y superiores.
///
## Una aplicación con webhooks { #an-app-with-webhooks }
Cuando creas una aplicación de **FastAPI**, hay un atributo `webhooks` que puedes usar para definir *webhooks*, de la misma manera que definirías *path operations*, por ejemplo con `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Los webhooks que defines terminarán en el esquema de **OpenAPI** y en la interfaz automática de **documentación**.
/// info | Información
El objeto `app.webhooks` es en realidad solo un `APIRouter`, el mismo tipo que usarías al estructurar tu aplicación con múltiples archivos.
///
Nota que con los webhooks en realidad no estás declarando un *path* (como `/items/`), el texto que pasas allí es solo un **identificador** del webhook (el nombre del evento), por ejemplo en `@app.webhooks.post("new-subscription")`, el nombre del webhook es `new-subscription`.
Esto es porque se espera que **tus usuarios** definan el actual **URL path** donde quieren recibir la request del webhook de alguna otra manera (por ejemplo, un panel web).
### Revisa la documentación { #check-the-docs }
Ahora puedes iniciar tu app e ir a [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás que tu documentación tiene las *path operations* normales y ahora también algunos **webhooks**:
================================================
FILE: docs/es/docs/advanced/path-operation-advanced-configuration.md
================================================
# Configuración Avanzada de Path Operation { #path-operation-advanced-configuration }
## operationId de OpenAPI { #openapi-operationid }
/// warning | Advertencia
Si no eres un "experto" en OpenAPI, probablemente no necesites esto.
///
Puedes establecer el `operationId` de OpenAPI para ser usado en tu *path operation* con el parámetro `operation_id`.
Tendrías que asegurarte de que sea único para cada operación.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Usar el nombre de la *path operation function* como el operationId { #using-the-path-operation-function-name-as-the-operationid }
Si quieres usar los nombres de las funciones de tus APIs como `operationId`s, puedes iterar sobre todas ellas y sobrescribir el `operation_id` de cada *path operation* usando su `APIRoute.name`.
Deberías hacerlo después de agregar todas tus *path operations*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Consejo
Si llamas manualmente a `app.openapi()`, deberías actualizar los `operationId`s antes de eso.
///
/// warning | Advertencia
Si haces esto, tienes que asegurarte de que cada una de tus *path operation functions* tenga un nombre único.
Incluso si están en diferentes módulos (archivos de Python).
///
## Excluir de OpenAPI { #exclude-from-openapi }
Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), utiliza el parámetro `include_in_schema` y configúralo en `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Descripción avanzada desde el docstring { #advanced-description-from-docstring }
Puedes limitar las líneas usadas del docstring de una *path operation function* para OpenAPI.
Añadir un `\f` (un carácter "form feed" escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
No aparecerá en la documentación, pero otras herramientas (como Sphinx) podrán usar el resto.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Responses Adicionales { #additional-responses }
Probablemente has visto cómo declarar el `response_model` y el `status_code` para una *path operation*.
Eso define los metadatos sobre el response principal de una *path operation*.
También puedes declarar responses adicionales con sus modelos, códigos de estado, etc.
Hay un capítulo entero en la documentación sobre ello, puedes leerlo en [Responses Adicionales en OpenAPI](additional-responses.md).
## OpenAPI Extra { #openapi-extra }
Cuando declaras una *path operation* en tu aplicación, **FastAPI** genera automáticamente los metadatos relevantes sobre esa *path operation* para incluirlos en el esquema de OpenAPI.
/// note | Detalles técnicos
En la especificación de OpenAPI se llama el [Objeto de Operación](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
Tiene toda la información sobre la *path operation* y se usa para generar la documentación automática.
Incluye los `tags`, `parameters`, `requestBody`, `responses`, etc.
Este esquema de OpenAPI específico de *path operation* normalmente se genera automáticamente por **FastAPI**, pero también puedes extenderlo.
/// tip | Consejo
Este es un punto de extensión de bajo nivel.
Si solo necesitas declarar responses adicionales, una forma más conveniente de hacerlo es con [Responses Adicionales en OpenAPI](additional-responses.md).
///
Puedes extender el esquema de OpenAPI para una *path operation* usando el parámetro `openapi_extra`.
### Extensiones de OpenAPI { #openapi-extensions }
Este `openapi_extra` puede ser útil, por ejemplo, para declarar [Extensiones de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Si abres la documentación automática de la API, tu extensión aparecerá en la parte inferior de la *path operation* específica.
Y si ves el OpenAPI resultante (en `/openapi.json` en tu API), verás tu extensión como parte de la *path operation* específica también:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Esquema de *path operation* personalizada de OpenAPI { #custom-openapi-path-operation-schema }
El diccionario en `openapi_extra` se combinará profundamente con el esquema de OpenAPI generado automáticamente para la *path operation*.
Por lo tanto, podrías añadir datos adicionales al esquema generado automáticamente.
Por ejemplo, podrías decidir leer y validar el request con tu propio código, sin usar las funcionalidades automáticas de FastAPI con Pydantic, pero aún podrías querer definir el request en el esquema de OpenAPI.
Podrías hacer eso con `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera es parseado como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de hacer parse de él de alguna manera.
Sin embargo, podemos declarar el esquema esperado para el request body.
### Tipo de contenido personalizado de OpenAPI { #custom-openapi-content-type }
Usando este mismo truco, podrías usar un modelo Pydantic para definir el JSON Schema que luego se incluye en la sección personalizada del esquema OpenAPI para la *path operation*.
Y podrías hacer esto incluso si el tipo de datos en el request no es JSON.
Por ejemplo, en esta aplicación no usamos la funcionalidad integrada de FastAPI para extraer el JSON Schema de los modelos Pydantic ni la validación automática para JSON. De hecho, estamos declarando el tipo de contenido del request como YAML, no JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el JSON Schema para los datos que queremos recibir en YAML.
Luego usamos el request directamente, y extraemos el cuerpo como `bytes`. Esto significa que FastAPI ni siquiera intentará hacer parse de la carga útil del request como JSON.
Y luego en nuestro código, hacemos parse de ese contenido YAML directamente, y nuevamente estamos usando el mismo modelo Pydantic para validar el contenido YAML:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Consejo
Aquí reutilizamos el mismo modelo Pydantic.
Pero de la misma manera, podríamos haberlo validado de alguna otra forma.
///
================================================
FILE: docs/es/docs/advanced/response-change-status-code.md
================================================
# Response - Cambiar Código de Estado { #response-change-status-code }
Probablemente leíste antes que puedes establecer un [Código de Estado de Response](../tutorial/response-status-code.md) por defecto.
Pero en algunos casos necesitas devolver un código de estado diferente al predeterminado.
## Caso de uso { #use-case }
Por ejemplo, imagina que quieres devolver un código de estado HTTP de "OK" `200` por defecto.
Pero si los datos no existieran, quieres crearlos y devolver un código de estado HTTP de "CREATED" `201`.
Pero todavía quieres poder filtrar y convertir los datos que devuelves con un `response_model`.
Para esos casos, puedes usar un parámetro `Response`.
## Usa un parámetro `Response` { #use-a-response-parameter }
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies y headers).
Y luego puedes establecer el `status_code` en ese objeto de response *temporal*.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc).
Y si declaraste un `response_model`, todavía se utilizará para filtrar y convertir el objeto que devolviste.
**FastAPI** usará ese response *temporal* para extraer el código de estado (también cookies y headers), y los pondrá en el response final que contiene el valor que devolviste, filtrado por cualquier `response_model`.
También puedes declarar el parámetro `Response` en dependencias y establecer el código de estado en ellas. Pero ten en cuenta que el último establecido prevalecerá.
================================================
FILE: docs/es/docs/advanced/response-cookies.md
================================================
# Cookies de Response { #response-cookies }
## Usar un parámetro `Response` { #use-a-response-parameter }
Puedes declarar un parámetro de tipo `Response` en tu *path operation function*.
Y luego puedes establecer cookies en ese objeto de response *temporal*.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Y entonces puedes devolver cualquier objeto que necesites, como normalmente lo harías (un `dict`, un modelo de base de datos, etc).
Y si declaraste un `response_model`, todavía se utilizará para filtrar y convertir el objeto que devolviste.
**FastAPI** utilizará ese response *temporal* para extraer las cookies (también los headers y el código de estado), y las pondrá en el response final que contiene el valor que devolviste, filtrado por cualquier `response_model`.
También puedes declarar el parámetro `Response` en las dependencias, y establecer cookies (y headers) en ellas.
## Devolver una `Response` directamente { #return-a-response-directly }
También puedes crear cookies al devolver una `Response` directamente en tu código.
Para hacer eso, puedes crear un response como se describe en [Devolver un Response Directamente](response-directly.md).
Luego establece Cookies en ella, y luego devuélvela:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Consejo
Ten en cuenta que si devuelves un response directamente en lugar de usar el parámetro `Response`, FastAPI lo devolverá directamente.
Así que tendrás que asegurarte de que tus datos son del tipo correcto. Por ejemplo, que sea compatible con JSON, si estás devolviendo un `JSONResponse`.
Y también que no estés enviando ningún dato que debería haber sido filtrado por un `response_model`.
///
### Más información { #more-info }
/// note | Detalles Técnicos
También podrías usar `from starlette.responses import Response` o `from starlette.responses import JSONResponse`.
**FastAPI** proporciona los mismos `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette.
Y como el `Response` se puede usar frecuentemente para establecer headers y cookies, **FastAPI** también lo proporciona en `fastapi.Response`.
///
Para ver todos los parámetros y opciones disponibles, revisa la [documentación en Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/es/docs/advanced/response-directly.md
================================================
# Devolver una Response Directamente { #return-a-response-directly }
Cuando creas una *path operation* en **FastAPI**, normalmente puedes devolver cualquier dato desde ella: un `dict`, una `list`, un modelo de Pydantic, un modelo de base de datos, etc.
Si declaras un [Response Model](../tutorial/response-model.md) FastAPI lo usará para serializar los datos a JSON, usando Pydantic.
Si no declaras un response model, FastAPI usará el `jsonable_encoder` explicado en [JSON Compatible Encoder](../tutorial/encoder.md) y lo pondrá en un `JSONResponse`.
También podrías crear un `JSONResponse` directamente y devolverlo.
/// tip | Consejo
Normalmente tendrás mucho mejor rendimiento usando un [Response Model](../tutorial/response-model.md) que devolviendo un `JSONResponse` directamente, ya que de esa forma serializa los datos usando Pydantic, en Rust.
///
## Devolver una `Response` { #return-a-response }
De hecho, puedes devolver cualquier `Response` o cualquier subclase de ella.
/// info | Información
`JSONResponse` en sí misma es una subclase de `Response`.
///
Y cuando devuelves una `Response`, **FastAPI** la pasará directamente.
No hará ninguna conversión de datos con los modelos de Pydantic, no convertirá los contenidos a ningún tipo, etc.
Esto te da mucha flexibilidad. Puedes devolver cualquier tipo de datos, sobrescribir cualquier declaración o validación de datos, etc.
También te da mucha responsabilidad. Tienes que asegurarte de que los datos que devuelves sean correctos, en el formato correcto, que se puedan serializar, etc.
## Usar el `jsonable_encoder` en una `Response` { #using-the-jsonable-encoder-in-a-response }
Como **FastAPI** no realiza cambios en una `Response` que devuelves, tienes que asegurarte de que sus contenidos estén listos para ello.
Por ejemplo, no puedes poner un modelo de Pydantic en un `JSONResponse` sin primero convertirlo a un `dict` con todos los tipos de datos (como `datetime`, `UUID`, etc.) convertidos a tipos compatibles con JSON.
Para esos casos, puedes usar el `jsonable_encoder` para convertir tus datos antes de pasarlos a un response:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Detalles técnicos
También podrías usar `from starlette.responses import JSONResponse`.
**FastAPI** proporciona los mismos `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette.
///
## Devolver una `Response` personalizada { #returning-a-custom-response }
El ejemplo anterior muestra todas las partes que necesitas, pero aún no es muy útil, ya que podrías haber devuelto el `item` directamente, y **FastAPI** lo colocaría en un `JSONResponse` por ti, convirtiéndolo a un `dict`, etc. Todo eso por defecto.
Ahora, veamos cómo podrías usar eso para devolver un response personalizado.
Digamos que quieres devolver un response en [XML](https://en.wikipedia.org/wiki/XML).
Podrías poner tu contenido XML en un string, poner eso en un `Response`, y devolverlo:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Cómo funciona un Response Model { #how-a-response-model-works }
Cuando declaras un [Response Model - Return Type](../tutorial/response-model.md) en una *path operation*, **FastAPI** lo usará para serializar los datos a JSON, usando Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Como eso sucederá del lado de Rust, el rendimiento será mucho mejor que si se hiciera con Python normal y la clase `JSONResponse`.
Al usar un `response_model` o tipo de retorno, FastAPI no usará el `jsonable_encoder` para convertir los datos (lo cual sería más lento) ni la clase `JSONResponse`.
En su lugar, toma los bytes JSON generados con Pydantic usando el response model (o tipo de retorno) y devuelve una `Response` con el media type correcto para JSON directamente (`application/json`).
## Notas { #notes }
Cuando devuelves una `Response` directamente, sus datos no son validados, convertidos (serializados), ni documentados automáticamente.
Pero aún puedes documentarlo como se describe en [Additional Responses in OpenAPI](additional-responses.md).
Puedes ver en secciones posteriores cómo usar/declarar estas `Response`s personalizadas mientras todavía tienes conversión automática de datos, documentación, etc.
================================================
FILE: docs/es/docs/advanced/response-headers.md
================================================
# Headers de Response { #response-headers }
## Usa un parámetro `Response` { #use-a-response-parameter }
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies).
Y luego puedes establecer headers en ese objeto de response *temporal*.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Y luego puedes devolver cualquier objeto que necesites, como harías normalmente (un `dict`, un modelo de base de datos, etc).
Y si declaraste un `response_model`, aún se usará para filtrar y convertir el objeto que devolviste.
**FastAPI** usará ese response *temporal* para extraer los headers (también cookies y el código de estado), y los pondrá en el response final que contiene el valor que devolviste, filtrado por cualquier `response_model`.
También puedes declarar el parámetro `Response` en dependencias y establecer headers (y cookies) en ellas.
## Retorna una `Response` directamente { #return-a-response-directly }
También puedes agregar headers cuando devuelves un `Response` directamente.
Crea un response como se describe en [Retorna un Response Directamente](response-directly.md) y pasa los headers como un parámetro adicional:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Detalles Técnicos
También podrías usar `from starlette.responses import Response` o `from starlette.responses import JSONResponse`.
**FastAPI** proporciona las mismas `starlette.responses` como `fastapi.responses` solo por conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles provienen directamente de Starlette.
Y como el `Response` se puede usar frecuentemente para establecer headers y cookies, **FastAPI** también lo proporciona en `fastapi.Response`.
///
## Headers Personalizados { #custom-headers }
Ten en cuenta que los headers propietarios personalizados se pueden agregar [usando el prefijo `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Pero si tienes headers personalizados que quieres que un cliente en un navegador pueda ver, necesitas agregarlos a tus configuraciones de CORS (leer más en [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), usando el parámetro `expose_headers` documentado en [la documentación CORS de Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/es/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
Para los casos más simples, puedes usar HTTP Basic Auth.
En HTTP Basic Auth, la aplicación espera un header que contiene un nombre de usuario y una contraseña.
Si no lo recibe, devuelve un error HTTP 401 "Unauthorized".
Y devuelve un header `WWW-Authenticate` con un valor de `Basic`, y un parámetro `realm` opcional.
Eso le dice al navegador que muestre el prompt integrado para un nombre de usuario y contraseña.
Luego, cuando escribes ese nombre de usuario y contraseña, el navegador los envía automáticamente en el header.
## Simple HTTP Basic Auth { #simple-http-basic-auth }
* Importa `HTTPBasic` y `HTTPBasicCredentials`.
* Crea un "esquema de `security`" usando `HTTPBasic`.
* Usa ese `security` con una dependencia en tu *path operation*.
* Devuelve un objeto de tipo `HTTPBasicCredentials`:
* Contiene el `username` y `password` enviados.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Cuando intentas abrir la URL por primera vez (o haces clic en el botón "Execute" en la documentación) el navegador te pedirá tu nombre de usuario y contraseña:
## Revisa el nombre de usuario { #check-the-username }
Aquí hay un ejemplo más completo.
Usa una dependencia para comprobar si el nombre de usuario y la contraseña son correctos.
Para esto, usa el módulo estándar de Python [`secrets`](https://docs.python.org/3/library/secrets.html) para verificar el nombre de usuario y la contraseña.
`secrets.compare_digest()` necesita tomar `bytes` o un `str` que solo contenga caracteres ASCII (los carácteres en inglés), esto significa que no funcionaría con caracteres como `á`, como en `Sebastián`.
Para manejar eso, primero convertimos el `username` y `password` a `bytes` codificándolos con UTF-8.
Luego podemos usar `secrets.compare_digest()` para asegurar que `credentials.username` es `"stanleyjobson"`, y que `credentials.password` es `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Esto sería similar a:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Devuelve algún error
...
```
Pero al usar `secrets.compare_digest()` será seguro contra un tipo de ataques llamados "timing attacks".
### Timing attacks { #timing-attacks }
¿Pero qué es un "timing attack"?
Imaginemos que algunos atacantes están tratando de adivinar el nombre de usuario y la contraseña.
Y envían un request con un nombre de usuario `johndoe` y una contraseña `love123`.
Entonces el código de Python en tu aplicación equivaldría a algo como:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Pero justo en el momento en que Python compara la primera `j` en `johndoe` con la primera `s` en `stanleyjobson`, devolverá `False`, porque ya sabe que esas dos strings no son iguales, pensando que "no hay necesidad de gastar más computación comparando el resto de las letras". Y tu aplicación dirá "Nombre de usuario o contraseña incorrectos".
Pero luego los atacantes prueban con el nombre de usuario `stanleyjobsox` y contraseña `love123`.
Y el código de tu aplicación hace algo así como:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python tendrá que comparar todo `stanleyjobso` en ambos `stanleyjobsox` y `stanleyjobson` antes de darse cuenta de que ambas strings no son las mismas. Así que tomará algunos microsegundos extra para responder "Nombre de usuario o contraseña incorrectos".
#### El tiempo de respuesta ayuda a los atacantes { #the-time-to-answer-helps-the-attackers }
En ese punto, al notar que el servidor tardó algunos microsegundos más en enviar el response "Nombre de usuario o contraseña incorrectos", los atacantes sabrán que acertaron en _algo_, algunas de las letras iniciales eran correctas.
Y luego pueden intentar de nuevo sabiendo que probablemente es algo más similar a `stanleyjobsox` que a `johndoe`.
#### Un ataque "profesional" { #a-professional-attack }
Por supuesto, los atacantes no intentarían todo esto a mano, escribirían un programa para hacerlo, posiblemente con miles o millones de pruebas por segundo. Y obtendrían solo una letra correcta adicional a la vez.
Pero haciendo eso, en algunos minutos u horas, los atacantes habrían adivinado el nombre de usuario y la contraseña correctos, con la "ayuda" de nuestra aplicación, solo usando el tiempo tomado para responder.
#### Arréglalo con `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Pero en nuestro código estamos usando realmente `secrets.compare_digest()`.
En resumen, tomará el mismo tiempo comparar `stanleyjobsox` con `stanleyjobson` que comparar `johndoe` con `stanleyjobson`. Y lo mismo para la contraseña.
De esa manera, usando `secrets.compare_digest()` en el código de tu aplicación, será seguro contra todo este rango de ataques de seguridad.
### Devuelve el error { #return-the-error }
Después de detectar que las credenciales son incorrectas, regresa un `HTTPException` con un código de estado 401 (el mismo que se devuelve cuando no se proporcionan credenciales) y agrega el header `WWW-Authenticate` para que el navegador muestre el prompt de inicio de sesión nuevamente:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/es/docs/advanced/security/index.md
================================================
# Seguridad Avanzada { #advanced-security }
## Funcionalidades Adicionales { #additional-features }
Hay algunas funcionalidades extra para manejar la seguridad aparte de las cubiertas en el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md).
/// tip | Consejo
Las siguientes secciones no son necesariamente "avanzadas".
Y es posible que para tu caso de uso, la solución esté en una de ellas.
///
## Lee primero el Tutorial { #read-the-tutorial-first }
Las siguientes secciones asumen que ya leíste el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md) principal.
Todas están basadas en los mismos conceptos, pero permiten algunas funcionalidades adicionales.
================================================
FILE: docs/es/docs/advanced/security/oauth2-scopes.md
================================================
# Scopes de OAuth2 { #oauth2-scopes }
Puedes usar scopes de OAuth2 directamente con **FastAPI**, están integrados para funcionar de manera fluida.
Esto te permitiría tener un sistema de permisos más detallado, siguiendo el estándar de OAuth2, integrado en tu aplicación OpenAPI (y la documentación de la API).
OAuth2 con scopes es el mecanismo usado por muchos grandes proveedores de autenticación, como Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Lo usan para proporcionar permisos específicos a usuarios y aplicaciones.
Cada vez que te "logueas con" Facebook, Google, GitHub, Microsoft, X (Twitter), esa aplicación está usando OAuth2 con scopes.
En esta sección verás cómo manejar autenticación y autorización con el mismo OAuth2 con scopes en tu aplicación de **FastAPI**.
/// warning | Advertencia
Esta es una sección más o menos avanzada. Si estás comenzando, puedes saltarla.
No necesariamente necesitas scopes de OAuth2, y puedes manejar autenticación y autorización como quieras.
Pero OAuth2 con scopes se puede integrar muy bien en tu API (con OpenAPI) y en la documentación de tu API.
No obstante, tú aún impones esos scopes, o cualquier otro requisito de seguridad/autorización, como necesites, en tu código.
En muchos casos, OAuth2 con scopes puede ser un exceso.
Pero si sabes que lo necesitas, o tienes curiosidad, sigue leyendo.
///
## Scopes de OAuth2 y OpenAPI { #oauth2-scopes-and-openapi }
La especificación de OAuth2 define "scopes" como una lista de strings separados por espacios.
El contenido de cada uno de estos strings puede tener cualquier formato, pero no debe contener espacios.
Estos scopes representan "permisos".
En OpenAPI (por ejemplo, en la documentación de la API), puedes definir "esquemas de seguridad".
Cuando uno de estos esquemas de seguridad usa OAuth2, también puedes declarar y usar scopes.
Cada "scope" es solo un string (sin espacios).
Normalmente se utilizan para declarar permisos de seguridad específicos, por ejemplo:
* `users:read` o `users:write` son ejemplos comunes.
* `instagram_basic` es usado por Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` es usado por Google.
/// info | Información
En OAuth2 un "scope" es solo un string que declara un permiso específico requerido.
No importa si tiene otros caracteres como `:` o si es una URL.
Esos detalles son específicos de la implementación.
Para OAuth2 son solo strings.
///
## Vista global { #global-view }
Primero, echemos un vistazo rápido a las partes que cambian desde los ejemplos en el **Tutorial - User Guide** principal para [OAuth2 con Password (y hashing), Bearer con tokens JWT](../../tutorial/security/oauth2-jwt.md). Ahora usando scopes de OAuth2:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Ahora revisemos esos cambios paso a paso.
## Esquema de seguridad OAuth2 { #oauth2-security-scheme }
El primer cambio es que ahora estamos declarando el esquema de seguridad OAuth2 con dos scopes disponibles, `me` y `items`.
El parámetro `scopes` recibe un `dict` con cada scope como clave y la descripción como valor:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Como ahora estamos declarando esos scopes, aparecerán en la documentación de la API cuando inicies sesión/autorices.
Y podrás seleccionar cuáles scopes quieres dar de acceso: `me` y `items`.
Este es el mismo mecanismo utilizado cuando das permisos al iniciar sesión con Facebook, Google, GitHub, etc:
## Token JWT con scopes { #jwt-token-with-scopes }
Ahora, modifica la *path operation* del token para devolver los scopes solicitados.
Todavía estamos usando el mismo `OAuth2PasswordRequestForm`. Incluye una propiedad `scopes` con una `list` de `str`, con cada scope que recibió en el request.
Y devolvemos los scopes como parte del token JWT.
/// danger | Peligro
Para simplificar, aquí solo estamos añadiendo los scopes recibidos directamente al token.
Pero en tu aplicación, por seguridad, deberías asegurarte de añadir solo los scopes que el usuario realmente puede tener, o los que has predefinido.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Declarar scopes en *path operations* y dependencias { #declare-scopes-in-path-operations-and-dependencies }
Ahora declaramos que la *path operation* para `/users/me/items/` requiere el scope `items`.
Para esto, importamos y usamos `Security` de `fastapi`.
Puedes usar `Security` para declarar dependencias (igual que `Depends`), pero `Security` también recibe un parámetro `scopes` con una lista de scopes (strings).
En este caso, pasamos una función de dependencia `get_current_active_user` a `Security` (de la misma manera que haríamos con `Depends`).
Pero también pasamos una `list` de scopes, en este caso con solo un scope: `items` (podría tener más).
Y la función de dependencia `get_current_active_user` también puede declarar sub-dependencias, no solo con `Depends` sino también con `Security`. Declarando su propia función de sub-dependencia (`get_current_user`), y más requisitos de scope.
En este caso, requiere el scope `me` (podría requerir más de un scope).
/// note | Nota
No necesariamente necesitas añadir diferentes scopes en diferentes lugares.
Lo estamos haciendo aquí para demostrar cómo **FastAPI** maneja scopes declarados en diferentes niveles.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Información Técnica
`Security` es en realidad una subclase de `Depends`, y tiene solo un parámetro extra que veremos más adelante.
Pero al usar `Security` en lugar de `Depends`, **FastAPI** sabrá que puede declarar scopes de seguridad, usarlos internamente y documentar la API con OpenAPI.
Pero cuando importas `Query`, `Path`, `Depends`, `Security` y otros de `fastapi`, en realidad son funciones que devuelven clases especiales.
///
## Usar `SecurityScopes` { #use-securityscopes }
Ahora actualiza la dependencia `get_current_user`.
Esta es la que usan las dependencias anteriores.
Aquí es donde estamos usando el mismo esquema de OAuth2 que creamos antes, declarándolo como una dependencia: `oauth2_scheme`.
Porque esta función de dependencia no tiene ningún requisito de scope en sí, podemos usar `Depends` con `oauth2_scheme`, no tenemos que usar `Security` cuando no necesitamos especificar scopes de seguridad.
También declaramos un parámetro especial de tipo `SecurityScopes`, importado de `fastapi.security`.
Esta clase `SecurityScopes` es similar a `Request` (`Request` se usó para obtener el objeto request directamente).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Usar los `scopes` { #use-the-scopes }
El parámetro `security_scopes` será del tipo `SecurityScopes`.
Tendrá una propiedad `scopes` con una lista que contiene todos los scopes requeridos por sí mismo y por todas las dependencias que lo usan como sub-dependencia. Eso significa, todos los "dependientes"... esto podría sonar confuso, se explica de nuevo más abajo.
El objeto `security_scopes` (de la clase `SecurityScopes`) también proporciona un atributo `scope_str` con un único string, que contiene esos scopes separados por espacios (lo vamos a usar).
Creamos una `HTTPException` que podemos reutilizar (`raise`) más tarde en varios puntos.
En esta excepción, incluimos los scopes requeridos (si los hay) como un string separado por espacios (usando `scope_str`). Ponemos ese string que contiene los scopes en el header `WWW-Authenticate` (esto es parte de la especificación).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Verificar el `username` y la forma de los datos { #verify-the-username-and-data-shape }
Verificamos que obtenemos un `username`, y extraemos los scopes.
Y luego validamos esos datos con el modelo de Pydantic (capturando la excepción `ValidationError`), y si obtenemos un error leyendo el token JWT o validando los datos con Pydantic, lanzamos la `HTTPException` que creamos antes.
Para eso, actualizamos el modelo de Pydantic `TokenData` con una nueva propiedad `scopes`.
Al validar los datos con Pydantic podemos asegurarnos de que tenemos, por ejemplo, exactamente una `list` de `str` con los scopes y un `str` con el `username`.
En lugar de, por ejemplo, un `dict`, o algo más, ya que podría romper la aplicación en algún punto posterior, haciéndolo un riesgo de seguridad.
También verificamos que tenemos un usuario con ese username, y si no, lanzamos esa misma excepción que creamos antes.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Verificar los `scopes` { #verify-the-scopes }
Ahora verificamos que todos los scopes requeridos, por esta dependencia y todos los dependientes (incluyendo *path operations*), estén incluidos en los scopes proporcionados en el token recibido, de lo contrario, lanzamos una `HTTPException`.
Para esto, usamos `security_scopes.scopes`, que contiene una `list` con todos estos scopes como `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Árbol de dependencias y scopes { #dependency-tree-and-scopes }
Revisemos de nuevo este árbol de dependencias y los scopes.
Como la dependencia `get_current_active_user` tiene como sub-dependencia a `get_current_user`, el scope `"me"` declarado en `get_current_active_user` se incluirá en la lista de scopes requeridos en el `security_scopes.scopes` pasado a `get_current_user`.
La *path operation* en sí también declara un scope, `"items"`, por lo que esto también estará en la lista de `security_scopes.scopes` pasado a `get_current_user`.
Así es como se ve la jerarquía de dependencias y scopes:
* La *path operation* `read_own_items` tiene:
* Scopes requeridos `["items"]` con la dependencia:
* `get_current_active_user`:
* La función de dependencia `get_current_active_user` tiene:
* Scopes requeridos `["me"]` con la dependencia:
* `get_current_user`:
* La función de dependencia `get_current_user` tiene:
* No requiere scopes por sí misma.
* Una dependencia usando `oauth2_scheme`.
* Un parámetro `security_scopes` de tipo `SecurityScopes`:
* Este parámetro `security_scopes` tiene una propiedad `scopes` con una `list` que contiene todos estos scopes declarados arriba, por lo que:
* `security_scopes.scopes` contendrá `["me", "items"]` para la *path operation* `read_own_items`.
* `security_scopes.scopes` contendrá `["me"]` para la *path operation* `read_users_me`, porque está declarado en la dependencia `get_current_active_user`.
* `security_scopes.scopes` contendrá `[]` (nada) para la *path operation* `read_system_status`, porque no declaró ningún `Security` con `scopes`, y su dependencia, `get_current_user`, tampoco declara ningún `scopes`.
/// tip | Consejo
Lo importante y "mágico" aquí es que `get_current_user` tendrá una lista diferente de `scopes` para verificar para cada *path operation*.
Todo depende de los `scopes` declarados en cada *path operation* y cada dependencia en el árbol de dependencias para esa *path operation* específica.
///
## Más detalles sobre `SecurityScopes` { #more-details-about-securityscopes }
Puedes usar `SecurityScopes` en cualquier punto, y en múltiples lugares, no tiene que ser en la dependencia "raíz".
Siempre tendrá los scopes de seguridad declarados en las dependencias `Security` actuales y todos los dependientes para **esa específica** *path operation* y **ese específico** árbol de dependencias.
Debido a que `SecurityScopes` tendrá todos los scopes declarados por dependientes, puedes usarlo para verificar que un token tiene los scopes requeridos en una función de dependencia central, y luego declarar diferentes requisitos de scope en diferentes *path operations*.
Serán verificados independientemente para cada *path operation*.
## Revisa { #check-it }
Si abres la documentación de la API, puedes autenticarte y especificar qué scopes deseas autorizar.
Si no seleccionas ningún scope, estarás "autenticado", pero cuando intentes acceder a `/users/me/` o `/users/me/items/` obtendrás un error diciendo que no tienes suficientes permisos. Aún podrás acceder a `/status/`.
Y si seleccionas el scope `me` pero no el scope `items`, podrás acceder a `/users/me/` pero no a `/users/me/items/`.
Eso es lo que pasaría a una aplicación de terceros que intentara acceder a una de estas *path operations* con un token proporcionado por un usuario, dependiendo de cuántos permisos el usuario otorgó a la aplicación.
## Acerca de las integraciones de terceros { #about-third-party-integrations }
En este ejemplo estamos usando el flujo de OAuth2 "password".
Esto es apropiado cuando estamos iniciando sesión en nuestra propia aplicación, probablemente con nuestro propio frontend.
Porque podemos confiar en ella para recibir el `username` y `password`, ya que la controlamos.
Pero si estás construyendo una aplicación OAuth2 a la que otros se conectarían (es decir, si estás construyendo un proveedor de autenticación equivalente a Facebook, Google, GitHub, etc.) deberías usar uno de los otros flujos.
El más común es el flujo implícito.
El más seguro es el flujo de código, pero es más complejo de implementar ya que requiere más pasos. Como es más complejo, muchos proveedores terminan sugiriendo el flujo implícito.
/// note | Nota
Es común que cada proveedor de autenticación nombre sus flujos de una manera diferente, para hacerlos parte de su marca.
Pero al final, están implementando el mismo estándar OAuth2.
///
**FastAPI** incluye utilidades para todos estos flujos de autenticación OAuth2 en `fastapi.security.oauth2`.
## `Security` en `dependencies` del decorador { #security-in-decorator-dependencies }
De la misma manera que puedes definir una `list` de `Depends` en el parámetro `dependencies` del decorador (como se explica en [Dependencias en decoradores de path operation](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), también podrías usar `Security` con `scopes` allí.
================================================
FILE: docs/es/docs/advanced/settings.md
================================================
# Configuraciones y Variables de Entorno { #settings-and-environment-variables }
En muchos casos, tu aplicación podría necesitar algunas configuraciones o ajustes externos, por ejemplo, claves secretas, credenciales de base de datos, credenciales para servicios de correo electrónico, etc.
La mayoría de estas configuraciones son variables (pueden cambiar), como las URLs de bases de datos. Y muchas podrían ser sensibles, como los secretos.
Por esta razón, es común proporcionarlas en variables de entorno que son leídas por la aplicación.
/// tip | Consejo
Para entender las variables de entorno, puedes leer [Variables de Entorno](../environment-variables.md).
///
## Tipos y validación { #types-and-validation }
Estas variables de entorno solo pueden manejar strings de texto, ya que son externas a Python y tienen que ser compatibles con otros programas y el resto del sistema (e incluso con diferentes sistemas operativos, como Linux, Windows, macOS).
Eso significa que cualquier valor leído en Python desde una variable de entorno será un `str`, y cualquier conversión a un tipo diferente o cualquier validación tiene que hacerse en código.
## Pydantic `Settings` { #pydantic-settings }
Afortunadamente, Pydantic proporciona una gran utilidad para manejar estas configuraciones provenientes de variables de entorno con [Pydantic: Settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Instalar `pydantic-settings` { #install-pydantic-settings }
Primero, asegúrate de crear tu [entorno virtual](../virtual-environments.md), actívalo y luego instala el paquete `pydantic-settings`:
### Crear el objeto `Settings` { #create-the-settings-object }
Importa `BaseSettings` de Pydantic y crea una sub-clase, muy similar a un modelo de Pydantic.
De la misma forma que con los modelos de Pydantic, declaras atributos de clase con anotaciones de tipos, y posiblemente, valores por defecto.
Puedes usar todas las mismas funcionalidades de validación y herramientas que usas para los modelos de Pydantic, como diferentes tipos de datos y validaciones adicionales con `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Consejo
Si quieres algo rápido para copiar y pegar, no uses este ejemplo, usa el último más abajo.
///
Luego, cuando creas un instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
Luego convertirá y validará los datos. Así que, cuando uses ese objeto `settings`, tendrás datos de los tipos que declaraste (por ejemplo, `items_per_user` será un `int`).
### Usar el `settings` { #use-the-settings }
Luego puedes usar el nuevo objeto `settings` en tu aplicación:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Ejecutar el servidor { #run-the-server }
Luego, ejecutarías el servidor pasando las configuraciones como variables de entorno, por ejemplo, podrías establecer un `ADMIN_EMAIL` y `APP_NAME` con:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Consejo
Para establecer múltiples env vars para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
///
Y luego la configuración `admin_email` se establecería en `"deadpool@example.com"`.
El `app_name` sería `"ChimichangApp"`.
Y el `items_per_user` mantendría su valor por defecto de `50`.
## Configuraciones en otro módulo { #settings-in-another-module }
Podrías poner esas configuraciones en otro archivo de módulo como viste en [Aplicaciones Más Grandes - Múltiples Archivos](../tutorial/bigger-applications.md).
Por ejemplo, podrías tener un archivo `config.py` con:
{* ../../docs_src/settings/app01_py310/config.py *}
Y luego usarlo en un archivo `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Consejo
También necesitarías un archivo `__init__.py` como viste en [Aplicaciones Más Grandes - Múltiples Archivos](../tutorial/bigger-applications.md).
///
## Configuraciones en una dependencia { #settings-in-a-dependency }
En algunas ocasiones podría ser útil proporcionar las configuraciones desde una dependencia, en lugar de tener un objeto global con `settings` que se use en todas partes.
Esto podría ser especialmente útil durante las pruebas, ya que es muy fácil sobrescribir una dependencia con tus propias configuraciones personalizadas.
### El archivo de configuración { #the-config-file }
Proveniente del ejemplo anterior, tu archivo `config.py` podría verse como:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Nota que ahora no creamos un instance por defecto `settings = Settings()`.
### El archivo principal de la app { #the-main-app-file }
Ahora creamos una dependencia que devuelve un nuevo `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Consejo
Hablaremos del `@lru_cache` en un momento.
Por ahora puedes asumir que `get_settings()` es una función normal.
///
Y luego podemos requerirlo desde la *path operation function* como una dependencia y usarlo donde lo necesitemos.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Configuraciones y pruebas { #settings-and-testing }
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al crear una sobrescritura de dependencia para `get_settings`:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
En la sobrescritura de dependencia establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
Luego podemos probar que se está usando.
## Leer un archivo `.env` { #reading-a-env-file }
Si tienes muchas configuraciones que posiblemente cambien mucho, tal vez en diferentes entornos, podría ser útil ponerlos en un archivo y luego leerlos desde allí como si fueran variables de entorno.
Esta práctica es lo suficientemente común que tiene un nombre, estas variables de entorno generalmente se colocan en un archivo `.env`, y el archivo se llama un "dotenv".
/// tip | Consejo
Un archivo que comienza con un punto (`.`) es un archivo oculto en sistemas tipo Unix, como Linux y macOS.
Pero un archivo dotenv realmente no tiene que tener ese nombre exacto.
///
Pydantic tiene soporte para leer desde estos tipos de archivos usando un paquete externo. Puedes leer más en [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Consejo
Para que esto funcione, necesitas `pip install python-dotenv`.
///
### El archivo `.env` { #the-env-file }
Podrías tener un archivo `.env` con:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Leer configuraciones desde `.env` { #read-settings-from-env }
Y luego actualizar tu `config.py` con:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Consejo
El atributo `model_config` se usa solo para configuración de Pydantic. Puedes leer más en [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Aquí definimos la configuración `env_file` dentro de tu clase Pydantic `Settings`, y establecemos el valor en el nombre del archivo con el archivo dotenv que queremos usar.
### Creando el `Settings` solo una vez con `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Leer un archivo desde el disco es normalmente una operación costosa (lenta), por lo que probablemente quieras hacerlo solo una vez y luego reutilizar el mismo objeto de configuraciones, en lugar de leerlo para cada request.
Pero cada vez que hacemos:
```Python
Settings()
```
se crearía un nuevo objeto `Settings`, y al crearse leería el archivo `.env` nuevamente.
Si la función de dependencia fuera simplemente así:
```Python
def get_settings():
return Settings()
```
crearíamos ese objeto para cada request, y estaríamos leyendo el archivo `.env` para cada request. ⚠️
Pero como estamos usando el decorador `@lru_cache` encima, el objeto `Settings` se creará solo una vez, la primera vez que se llame. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Entonces, para cualquier llamada subsiguiente de `get_settings()` en las dependencias de los próximos requests, en lugar de ejecutar el código interno de `get_settings()` y crear un nuevo objeto `Settings`, devolverá el mismo objeto que fue devuelto en la primera llamada, una y otra vez.
#### Detalles Técnicos de `lru_cache` { #lru-cache-technical-details }
`@lru_cache` modifica la función que decora para devolver el mismo valor que se devolvió la primera vez, en lugar de calcularlo nuevamente, ejecutando el código de la función cada vez.
Así que la función debajo se ejecutará una vez por cada combinación de argumentos. Y luego, los valores devueltos por cada una de esas combinaciones de argumentos se utilizarán una y otra vez cada vez que la función sea llamada con exactamente la misma combinación de argumentos.
Por ejemplo, si tienes una función:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
tu programa podría ejecutarse así:
```mermaid
sequenceDiagram
participant code as Código
participant function as say_hi()
participant execute as Ejecutar función
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: ejecutar código de la función
execute ->> code: devolver el resultado
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: devolver resultado almacenado
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: ejecutar código de la función
execute ->> code: devolver el resultado
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: ejecutar código de la función
execute ->> code: devolver el resultado
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: devolver resultado almacenado
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: devolver resultado almacenado
end
```
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devuelve el mismo valor.
De esa manera, se comporta casi como si fuera solo una variable global. Pero como usa una función de dependencia, entonces podemos sobrescribirla fácilmente para las pruebas.
`@lru_cache` es parte de `functools`, que es parte del paquete estándar de Python, puedes leer más sobre él en las [docs de Python para `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Resumen { #recap }
Puedes usar Pydantic Settings para manejar las configuraciones o ajustes de tu aplicación, con todo el poder de los modelos de Pydantic.
* Al usar una dependencia, puedes simplificar las pruebas.
* Puedes usar archivos `.env` con él.
* Usar `@lru_cache` te permite evitar leer el archivo dotenv una y otra vez para cada request, mientras te permite sobrescribirlo durante las pruebas.
================================================
FILE: docs/es/docs/advanced/stream-data.md
================================================
# Transmitir datos { #stream-data }
Si quieres transmitir datos que se puedan estructurar como JSON, deberías [Transmitir JSON Lines](../tutorial/stream-json-lines.md).
Pero si quieres transmitir datos binarios puros o strings, aquí tienes cómo hacerlo.
/// info | Información
Añadido en FastAPI 0.134.0.
///
## Casos de uso { #use-cases }
Podrías usar esto si quieres transmitir strings puros, por ejemplo directamente de la salida de un servicio de AI LLM.
También podrías usarlo para transmitir archivos binarios grandes, donde transmites cada bloque de datos a medida que lo lees, sin tener que leerlo todo en memoria de una sola vez.
También podrías transmitir video o audio de esta manera; incluso podría generarse mientras lo procesas y lo envías.
## Un `StreamingResponse` con `yield` { #a-streamingresponse-with-yield }
Si declaras un `response_class=StreamingResponse` en tu *path operation function*, puedes usar `yield` para enviar cada bloque de datos a su vez.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI entregará cada bloque de datos a `StreamingResponse` tal cual, no intentará convertirlo a JSON ni nada parecido.
### *path operation functions* no async { #non-async-path-operation-functions }
También puedes usar funciones `def` normales (sin `async`) y usar `yield` de la misma manera.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Sin anotación { #no-annotation }
Realmente no necesitas declarar la anotación de tipo de retorno para transmitir datos binarios.
Como FastAPI no intentará convertir los datos a JSON con Pydantic ni serializarlos de ninguna manera, en este caso la anotación de tipos es solo para que la use tu editor y tus herramientas; FastAPI no la usará.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Esto también significa que con `StreamingResponse` tienes la libertad y la responsabilidad de producir y codificar los bytes de datos exactamente como necesites enviarlos, independientemente de las anotaciones de tipos. 🤓
### Transmitir bytes { #stream-bytes }
Uno de los casos de uso principales sería transmitir `bytes` en lugar de strings; por supuesto puedes hacerlo.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Un `PNGStreamingResponse` personalizado { #a-custom-pngstreamingresponse }
En los ejemplos anteriores, se transmitieron los bytes de datos, pero la response no tenía un header `Content-Type`, así que el cliente no sabía qué tipo de datos estaba recibiendo.
Puedes crear una subclase personalizada de `StreamingResponse` que establezca el header `Content-Type` al tipo de datos que estás transmitiendo.
Por ejemplo, puedes crear un `PNGStreamingResponse` que establezca el header `Content-Type` a `image/png` usando el atributo `media_type`:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Luego puedes usar esta nueva clase en `response_class=PNGStreamingResponse` en tu *path operation function*:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Simular un archivo { #simulate-a-file }
En este ejemplo estamos simulando un archivo con `io.BytesIO`, que es un objeto tipo archivo que vive solo en memoria, pero nos permite usar la misma interfaz.
Por ejemplo, podemos iterarlo para consumir su contenido, como podríamos con un archivo.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Detalles técnicos
Las otras dos variables, `image_base64` y `binary_image`, son una imagen codificada en Base64 y luego convertida a bytes, para después pasarla a `io.BytesIO`.
Solo para que pueda vivir en el mismo archivo para este ejemplo y puedas copiarlo y ejecutarlo tal cual. 🥚
///
Al usar un bloque `with`, nos aseguramos de que el objeto tipo archivo se cierre cuando termine la función generadora (la función con `yield`). Es decir, después de que termine de enviar la response.
No sería tan importante en este ejemplo específico porque es un archivo falso en memoria (con `io.BytesIO`), pero con un archivo real sí sería importante asegurarse de que el archivo se cierre al terminar de trabajar con él.
### Archivos y async { #files-and-async }
En la mayoría de los casos, los objetos tipo archivo no son compatibles con `async` y `await` por defecto.
Por ejemplo, no tienen un `await file.read()`, ni un `async for chunk in file`.
Y en muchos casos leerlos sería una operación bloqueante (que podría bloquear el event loop), porque se leen desde disco o desde la red.
/// info | Información
El ejemplo anterior es en realidad una excepción, porque el objeto `io.BytesIO` ya está en memoria, así que leerlo no bloqueará nada.
Pero en muchos casos leer un archivo u objeto tipo archivo sí bloquearía.
///
Para evitar bloquear el event loop, puedes simplemente declarar la *path operation function* con un `def` normal en lugar de `async def`; de esa forma FastAPI la ejecutará en un worker de threadpool para evitar bloquear el loop principal.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Consejo
Si necesitas llamar código bloqueante desde dentro de una función async, o una función async desde dentro de una función bloqueante, podrías usar [Asyncer](https://asyncer.tiangolo.com), un paquete hermano de FastAPI.
///
### `yield from` { #yield-from }
Cuando estés iterando sobre algo, como un objeto tipo archivo, y estés haciendo `yield` para cada elemento, también podrías usar `yield from` para hacer `yield` de cada elemento directamente y saltarte el `for`.
Esto no es particular de FastAPI, es simplemente Python, pero es un truco útil que conviene conocer. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/es/docs/advanced/strict-content-type.md
================================================
# Chequeo estricto de Content-Type { #strict-content-type-checking }
Por defecto, **FastAPI** usa un chequeo estricto del header `Content-Type` para request bodies JSON, esto significa que las requests JSON deben incluir un header `Content-Type` válido (p. ej. `application/json`) para que el request body se parse como JSON.
## Riesgo de CSRF { #csrf-risk }
Este comportamiento por defecto provee protección contra una clase de ataques de **Cross-Site Request Forgery (CSRF)** en un escenario muy específico.
Estos ataques aprovechan que los navegadores permiten que los scripts envíen requests sin hacer un preflight de CORS cuando:
* no tienen un header `Content-Type` (p. ej. usando `fetch()` con un body `Blob`)
* y no envían credenciales de autenticación.
Este tipo de ataque es relevante principalmente cuando:
* la aplicación corre localmente (p. ej. en `localhost`) o en una red interna
* y la aplicación no tiene ninguna autenticación, espera que cualquier request de la misma red sea confiable.
## Ejemplo de ataque { #example-attack }
Imagina que construyes una forma de ejecutar un agente de IA local.
Provee un API en
```
http://localhost:8000/v1/agents/multivac
```
También hay un frontend en
```
http://localhost:8000
```
/// tip | Consejo
Ten en cuenta que ambos tienen el mismo host.
///
Luego, usando el frontend, puedes hacer que el agente de IA haga cosas en tu nombre.
Como está corriendo localmente y no en Internet abierta, decides no tener ninguna autenticación configurada, confiando simplemente en el acceso a la red local.
Entonces, uno de tus usuarios podría instalarlo y ejecutarlo localmente.
Después podría abrir un sitio web malicioso, por ejemplo algo como
```
https://evilhackers.example.com
```
Y ese sitio malicioso envía requests usando `fetch()` con un body `Blob` al API local en
```
http://localhost:8000/v1/agents/multivac
```
Aunque el host del sitio malicioso y el de la app local sea diferente, el navegador no disparará un preflight de CORS porque:
* Está corriendo sin ninguna autenticación, no tiene que enviar credenciales.
* El navegador cree que no está enviando JSON (por la falta del header `Content-Type`).
Entonces el sitio malicioso podría hacer que el agente de IA local envíe mensajes agresivos al exjefe del usuario... o peor. 😅
## Internet abierta { #open-internet }
Si tu app está en Internet abierta, no “confiarías en la red” ni permitirías que cualquiera envíe requests privilegiadas sin autenticación.
Los atacantes podrían simplemente ejecutar un script para enviar requests a tu API, sin necesidad de interacción del navegador, así que probablemente ya estás asegurando cualquier endpoint privilegiado.
En ese caso, este ataque/riesgo no aplica a ti.
Este riesgo y ataque es relevante principalmente cuando la app corre en la red local y esa es la única protección asumida.
## Permitir requests sin Content-Type { #allowing-requests-without-content-type }
Si necesitas soportar clientes que no envían un header `Content-Type`, puedes desactivar el chequeo estricto configurando `strict_content_type=False`:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
Con esta configuración, las requests sin un header `Content-Type` tendrán su body parseado como JSON, que es el mismo comportamiento de versiones anteriores de FastAPI.
/// info | Información
Este comportamiento y configuración se añadieron en FastAPI 0.132.0.
///
================================================
FILE: docs/es/docs/advanced/sub-applications.md
================================================
# Sub Aplicaciones - Mounts { #sub-applications-mounts }
Si necesitas tener dos aplicaciones de **FastAPI** independientes, cada una con su propio OpenAPI independiente y su propia interfaz de docs, puedes tener una aplicación principal y "montar" una (o más) sub-aplicación(es).
## Montar una aplicación **FastAPI** { #mounting-a-fastapi-application }
"Montar" significa añadir una aplicación completamente "independiente" en un path específico, que luego se encarga de manejar todo bajo ese path, con las _path operations_ declaradas en esa sub-aplicación.
### Aplicación de nivel superior { #top-level-application }
Primero, crea la aplicación principal de nivel superior de **FastAPI**, y sus *path operations*:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sub-aplicación { #sub-application }
Luego, crea tu sub-aplicación, y sus *path operations*.
Esta sub-aplicación es solo otra aplicación estándar de FastAPI, pero es la que se "montará":
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Montar la sub-aplicación { #mount-the-sub-application }
En tu aplicación de nivel superior, `app`, monta la sub-aplicación, `subapi`.
En este caso, se montará en el path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Revisa la documentación automática de la API { #check-the-automatic-api-docs }
Ahora, ejecuta el comando `fastapi`:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Y abre la documentación en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás la documentación automática de la API para la aplicación principal, incluyendo solo sus propias _path operations_:
Y luego, abre la documentación para la sub-aplicación, en [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Verás la documentación automática de la API para la sub-aplicación, incluyendo solo sus propias _path operations_, todas bajo el prefijo correcto del sub-path `/subapi`:
Si intentas interactuar con cualquiera de las dos interfaces de usuario, funcionarán correctamente, porque el navegador podrá comunicarse con cada aplicación o sub-aplicación específica.
### Detalles Técnicos: `root_path` { #technical-details-root-path }
Cuando montas una sub-aplicación como se describe arriba, FastAPI se encargará de comunicar el path de montaje para la sub-aplicación usando un mecanismo de la especificación ASGI llamado `root_path`.
De esa manera, la sub-aplicación sabrá usar ese prefijo de path para la interfaz de documentación.
Y la sub-aplicación también podría tener sus propias sub-aplicaciones montadas y todo funcionaría correctamente, porque FastAPI maneja todos estos `root_path`s automáticamente.
Aprenderás más sobre el `root_path` y cómo usarlo explícitamente en la sección sobre [Detrás de un Proxy](behind-a-proxy.md).
================================================
FILE: docs/es/docs/advanced/templates.md
================================================
# Plantillas { #templates }
Puedes usar cualquier motor de plantillas que desees con **FastAPI**.
Una elección común es Jinja2, el mismo que usa Flask y otras herramientas.
Hay utilidades para configurarlo fácilmente que puedes usar directamente en tu aplicación de **FastAPI** (proporcionadas por Starlette).
## Instala dependencias { #install-dependencies }
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo e instalar `jinja2`:
/// info | Información
Existen otros formatos y herramientas para definir e instalar dependencias de paquetes.
///
### Crear el Código de **FastAPI** { #create-the-fastapi-code }
* Crea un directorio `app` y entra en él.
* Crea un archivo vacío `__init__.py`.
* Crea un archivo `main.py` con:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Ahora, en el mismo directorio del proyecto, crea un archivo `Dockerfile` con:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Comenzar desde la imagen base oficial de Python.
2. Establecer el directorio de trabajo actual a `/code`.
Aquí es donde pondremos el archivo `requirements.txt` y el directorio `app`.
3. Copiar el archivo con los requisitos al directorio `/code`.
Copiar **solo** el archivo con los requisitos primero, no el resto del código.
Como este archivo **no cambia a menudo**, Docker lo detectará y usará la **caché** para este paso, habilitando la caché para el siguiente paso también.
4. Instalar las dependencias de los paquetes en el archivo de requisitos.
La opción `--no-cache-dir` le dice a `pip` que no guarde los paquetes descargados localmente, ya que eso solo sería si `pip` fuese a ejecutarse de nuevo para instalar los mismos paquetes, pero ese no es el caso al trabajar con contenedores.
/// note | Nota
El `--no-cache-dir` está relacionado solo con `pip`, no tiene nada que ver con Docker o contenedores.
///
La opción `--upgrade` le dice a `pip` que actualice los paquetes si ya están instalados.
Debido a que el paso anterior de copiar el archivo podría ser detectado por la **caché de Docker**, este paso también **usará la caché de Docker** cuando esté disponible.
Usar la caché en este paso te **ahorrará** mucho **tiempo** al construir la imagen una y otra vez durante el desarrollo, en lugar de **descargar e instalar** todas las dependencias **cada vez**.
5. Copiar el directorio `./app` dentro del directorio `/code`.
Como esto contiene todo el código, que es lo que **cambia con más frecuencia**, la **caché de Docker** no se utilizará para este u otros **pasos siguientes** fácilmente.
Así que es importante poner esto **cerca del final** del `Dockerfile`, para optimizar los tiempos de construcción de la imagen del contenedor.
6. Establecer el **comando** para usar `fastapi run`, que utiliza Uvicorn debajo.
`CMD` toma una lista de cadenas, cada una de estas cadenas es lo que escribirías en la línea de comandos separado por espacios.
Este comando se ejecutará desde el **directorio de trabajo actual**, el mismo directorio `/code` que estableciste antes con `WORKDIR /code`.
/// tip | Consejo
Revisa qué hace cada línea haciendo clic en cada número en la burbuja del código. 👆
///
/// warning | Advertencia
Asegúrate de **siempre** usar la **forma exec** de la instrucción `CMD`, como se explica a continuación.
///
#### Usar `CMD` - Forma Exec { #use-cmd-exec-form }
La instrucción Docker [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) se puede escribir usando dos formas:
✅ **Forma Exec**:
```Dockerfile
# ✅ Haz esto
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Forma Shell**:
```Dockerfile
# ⛔️ No hagas esto
CMD fastapi run app/main.py --port 80
```
Asegúrate de siempre usar la **forma exec** para garantizar que FastAPI pueda cerrarse de manera adecuada y que [los eventos de lifespan](../advanced/events.md) sean disparados.
Puedes leer más sobre esto en la [documentación de Docker para formas de shell y exec](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Esto puede ser bastante notorio al usar `docker compose`. Consulta esta sección de preguntas frecuentes de Docker Compose para más detalles técnicos: [¿Por qué mis servicios tardan 10 segundos en recrearse o detenerse?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Estructura de Directorios { #directory-structure }
Ahora deberías tener una estructura de directorios como:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Detrás de un Proxy de Terminación TLS { #behind-a-tls-termination-proxy }
Si estás ejecutando tu contenedor detrás de un Proxy de Terminación TLS (load balancer) como Nginx o Traefik, añade la opción `--proxy-headers`, esto le dirá a Uvicorn (a través de la CLI de FastAPI) que confíe en los headers enviados por ese proxy indicando que la aplicación se está ejecutando detrás de HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Caché de Docker { #docker-cache }
Hay un truco importante en este `Dockerfile`, primero copiamos **el archivo con las dependencias solo**, no el resto del código. Déjame decirte por qué es así.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker y otras herramientas **construyen** estas imágenes de contenedor **incrementalmente**, añadiendo **una capa sobre la otra**, empezando desde la parte superior del `Dockerfile` y añadiendo cualquier archivo creado por cada una de las instrucciones del `Dockerfile`.
Docker y herramientas similares también usan una **caché interna** al construir la imagen, si un archivo no ha cambiado desde la última vez que se construyó la imagen del contenedor, entonces reutilizará la misma capa creada la última vez, en lugar de copiar el archivo de nuevo y crear una nueva capa desde cero.
Solo evitar copiar archivos no mejora necesariamente las cosas mucho, pero porque se usó la caché para ese paso, puede **usar la caché para el siguiente paso**. Por ejemplo, podría usar la caché para la instrucción que instala las dependencias con:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
El archivo con los requisitos de los paquetes **no cambiará con frecuencia**. Así que, al copiar solo ese archivo, Docker podrá **usar la caché** para ese paso.
Y luego, Docker podrá **usar la caché para el siguiente paso** que descarga e instala esas dependencias. Y aquí es donde **ahorramos mucho tiempo**. ✨ ...y evitamos el aburrimiento de esperar. 😪😆
Descargar e instalar las dependencias de los paquetes **podría llevar minutos**, pero usando la **caché** tomaría **segundos** como máximo.
Y como estarías construyendo la imagen del contenedor una y otra vez durante el desarrollo para comprobar que los cambios en tu código funcionan, hay una gran cantidad de tiempo acumulado que te ahorrarías.
Luego, cerca del final del `Dockerfile`, copiamos todo el código. Como esto es lo que **cambia con más frecuencia**, lo ponemos cerca del final, porque casi siempre, cualquier cosa después de este paso no podrá usar la caché.
```Dockerfile
COPY ./app /code/app
```
### Construir la Imagen de Docker { #build-the-docker-image }
Ahora que todos los archivos están en su lugar, vamos a construir la imagen del contenedor.
* Ve al directorio del proyecto (donde está tu `Dockerfile`, conteniendo tu directorio `app`).
* Construye tu imagen de FastAPI:
/// tip | Consejo
Fíjate en el `.` al final, es equivalente a `./`, le indica a Docker el directorio a usar para construir la imagen del contenedor.
En este caso, es el mismo directorio actual (`.`).
///
### Iniciar el Contenedor Docker { #start-the-docker-container }
* Ejecuta un contenedor basado en tu imagen:
## Revísalo { #check-it }
Deberías poder revisarlo en la URL de tu contenedor de Docker, por ejemplo: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) o [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (o equivalente, usando tu host de Docker).
Verás algo como:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Documentación Interactiva de la API { #interactive-api-docs }
Ahora puedes ir a [http://192.168.99.100/docs](http://192.168.99.100/docs) o [http://127.0.0.1/docs](http://127.0.0.1/docs) (o equivalente, usando tu host de Docker).
Verás la documentación interactiva automática de la API (proporcionada por [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Documentación Alternativa de la API { #alternative-api-docs }
Y también puedes ir a [http://192.168.99.100/redoc](http://192.168.99.100/redoc) o [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (o equivalente, usando tu host de Docker).
Verás la documentación alternativa automática (proporcionada por [ReDoc](https://github.com/Rebilly/ReDoc)):

## Construir una Imagen de Docker con un FastAPI de Un Solo Archivo { #build-a-docker-image-with-a-single-file-fastapi }
Si tu FastAPI es un solo archivo, por ejemplo, `main.py` sin un directorio `./app`, tu estructura de archivos podría verse así:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Entonces solo tendrías que cambiar las rutas correspondientes para copiar el archivo dentro del `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copia el archivo `main.py` directamente al directorio `/code` (sin ningún directorio `./app`).
2. Usa `fastapi run` para servir tu aplicación en el archivo único `main.py`.
Cuando pasas el archivo a `fastapi run`, detectará automáticamente que es un archivo único y no parte de un paquete y sabrá cómo importarlo y servir tu aplicación FastAPI. 😎
## Conceptos de Despliegue { #deployment-concepts }
Hablemos nuevamente de algunos de los mismos [Conceptos de Despliegue](concepts.md) en términos de contenedores.
Los contenedores son principalmente una herramienta para simplificar el proceso de **construcción y despliegue** de una aplicación, pero no imponen un enfoque particular para manejar estos **conceptos de despliegue**, y hay varias estrategias posibles.
La **buena noticia** es que con cada estrategia diferente hay una forma de cubrir todos los conceptos de despliegue. 🎉
Revisemos estos **conceptos de despliegue** en términos de contenedores:
* HTTPS
* Ejecutar en el inicio
* Reinicios
* Replicación (el número de procesos en ejecución)
* Memoria
* Pasos previos antes de comenzar
## HTTPS { #https }
Si nos enfocamos solo en la **imagen de contenedor** para una aplicación FastAPI (y luego el **contenedor** en ejecución), HTTPS normalmente sería manejado **externamente** por otra herramienta.
Podría ser otro contenedor, por ejemplo, con [Traefik](https://traefik.io/), manejando **HTTPS** y la adquisición **automática** de **certificados**.
/// tip | Consejo
Traefik tiene integraciones con Docker, Kubernetes, y otros, por lo que es muy fácil configurar y configurar HTTPS para tus contenedores con él.
///
Alternativamente, HTTPS podría ser manejado por un proveedor de la nube como uno de sus servicios (mientras que la aplicación aún se ejecuta en un contenedor).
## Ejecutar en el Inicio y Reinicios { #running-on-startup-and-restarts }
Normalmente hay otra herramienta encargada de **iniciar y ejecutar** tu contenedor.
Podría ser **Docker** directamente, **Docker Compose**, **Kubernetes**, un **servicio en la nube**, etc.
En la mayoría (o todas) de las casos, hay una opción sencilla para habilitar la ejecución del contenedor al inicio y habilitar los reinicios en caso de fallos. Por ejemplo, en Docker, es la opción de línea de comandos `--restart`.
Sin usar contenedores, hacer que las aplicaciones se ejecuten al inicio y con reinicios puede ser engorroso y difícil. Pero al **trabajar con contenedores** en la mayoría de los casos, esa funcionalidad se incluye por defecto. ✨
## Replicación - Número de Procesos { #replication-number-of-processes }
Si tienes un clúster de máquinas con **Kubernetes**, Docker Swarm Mode, Nomad, u otro sistema complejo similar para gestionar contenedores distribuidos en varias máquinas, entonces probablemente querrás manejar la **replicación** a nivel de **cluster** en lugar de usar un **gestor de procesos** (como Uvicorn con workers) en cada contenedor.
Uno de esos sistemas de gestión de contenedores distribuidos como Kubernetes normalmente tiene alguna forma integrada de manejar la **replicación de contenedores** mientras aún soporta el **load balancing** para las requests entrantes. Todo a nivel de **cluster**.
En esos casos, probablemente desearías construir una **imagen de Docker desde cero** como se [explica arriba](#dockerfile), instalando tus dependencias, y ejecutando **un solo proceso de Uvicorn** en lugar de usar múltiples workers de Uvicorn.
### Load Balancer { #load-balancer }
Al usar contenedores, normalmente tendrías algún componente **escuchando en el puerto principal**. Podría posiblemente ser otro contenedor que es también un **Proxy de Terminación TLS** para manejar **HTTPS** o alguna herramienta similar.
Como este componente tomaría la **carga** de las requests y las distribuiría entre los workers de una manera (esperablemente) **balanceada**, también se le llama comúnmente **Load Balancer**.
/// tip | Consejo
El mismo componente **Proxy de Terminación TLS** usado para HTTPS probablemente también sería un **Load Balancer**.
///
Y al trabajar con contenedores, el mismo sistema que usas para iniciarlos y gestionarlos ya tendría herramientas internas para transmitir la **comunicación en red** (e.g., requests HTTP) desde ese **load balancer** (que también podría ser un **Proxy de Terminación TLS**) a los contenedores con tu aplicación.
### Un Load Balancer - Múltiples Contenedores Worker { #one-load-balancer-multiple-worker-containers }
Al trabajar con **Kubernetes** u otros sistemas de gestión de contenedores distribuidos similares, usar sus mecanismos de red internos permitiría que el único **load balancer** que está escuchando en el **puerto** principal transmita la comunicación (requests) a posiblemente **múltiples contenedores** ejecutando tu aplicación.
Cada uno de estos contenedores ejecutando tu aplicación normalmente tendría **solo un proceso** (e.g., un proceso Uvicorn ejecutando tu aplicación FastAPI). Todos serían **contenedores idénticos**, ejecutando lo mismo, pero cada uno con su propio proceso, memoria, etc. De esa forma, aprovecharías la **paralelización** en **diferentes núcleos** de la CPU, o incluso en **diferentes máquinas**.
Y el sistema de contenedores distribuido con el **load balancer** **distribuiría las requests** a cada uno de los contenedores con tu aplicación **en turnos**. Así, cada request podría ser manejada por uno de los múltiples **contenedores replicados** ejecutando tu aplicación.
Y normalmente este **load balancer** podría manejar requests que vayan a *otras* aplicaciones en tu cluster (p. ej., a un dominio diferente, o bajo un prefijo de path de URL diferente), y transmitiría esa comunicación a los contenedores correctos para *esa otra* aplicación ejecutándose en tu cluster.
### Un Proceso por Contenedor { #one-process-per-container }
En este tipo de escenario, probablemente querrías tener **un solo proceso (Uvicorn) por contenedor**, ya que ya estarías manejando la replicación a nivel de cluster.
Así que, en este caso, **no** querrías tener múltiples workers en el contenedor, por ejemplo, con la opción de línea de comandos `--workers`. Querrías tener solo un **proceso Uvicorn por contenedor** (pero probablemente múltiples contenedores).
Tener otro gestor de procesos dentro del contenedor (como sería con múltiples workers) solo añadiría **complejidad innecesaria** que probablemente ya estés manejando con tu sistema de cluster.
### Contenedores con Múltiples Procesos y Casos Especiales { #containers-with-multiple-processes-and-special-cases }
Por supuesto, hay **casos especiales** donde podrías querer tener **un contenedor** con varios **worker processes de Uvicorn** dentro.
En esos casos, puedes usar la opción de línea de comandos `--workers` para establecer el número de workers que deseas ejecutar:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Aquí usamos la opción de línea de comandos `--workers` para establecer el número de workers a 4.
Aquí hay algunos ejemplos de cuándo eso podría tener sentido:
#### Una Aplicación Simple { #a-simple-app }
Podrías querer un gestor de procesos en el contenedor si tu aplicación es **lo suficientemente simple** que pueda ejecutarse en un **servidor único**, no un cluster.
#### Docker Compose { #docker-compose }
Podrías estar desplegando en un **servidor único** (no un cluster) con **Docker Compose**, por lo que no tendrías una forma fácil de gestionar la replicación de contenedores (con Docker Compose) mientras se preserva la red compartida y el **load balancing**.
Entonces podrías querer tener **un solo contenedor** con un **gestor de procesos** iniciando **varios worker processes** dentro.
---
El punto principal es que, **ninguna** de estas son **reglas escritas en piedra** que debas seguir a ciegas. Puedes usar estas ideas para **evaluar tu propio caso de uso** y decidir cuál es el mejor enfoque para tu sistema, verificando cómo gestionar los conceptos de:
* Seguridad - HTTPS
* Ejecutar en el inicio
* Reinicios
* Replicación (el número de procesos en ejecución)
* Memoria
* Pasos previos antes de comenzar
## Memoria { #memory }
Si ejecutas **un solo proceso por contenedor**, tendrás una cantidad de memoria más o menos bien definida, estable y limitada consumida por cada uno de esos contenedores (más de uno si están replicados).
Y luego puedes establecer esos mismos límites de memoria y requisitos en tus configuraciones para tu sistema de gestión de contenedores (por ejemplo, en **Kubernetes**). De esa manera, podrá **replicar los contenedores** en las **máquinas disponibles** teniendo en cuenta la cantidad de memoria necesaria por ellos, y la cantidad disponible en las máquinas en el cluster.
Si tu aplicación es **simple**, probablemente esto **no será un problema**, y puede que no necesites especificar límites de memoria estrictos. Pero si estás **usando mucha memoria** (por ejemplo, con modelos de **Machine Learning**), deberías verificar cuánta memoria estás consumiendo y ajustar el **número de contenedores** que se ejecutan en **cada máquina** (y tal vez agregar más máquinas a tu cluster).
Si ejecutas **múltiples procesos por contenedor**, tendrás que asegurarte de que el número de procesos iniciados no **consuma más memoria** de la que está disponible.
## Pasos Previos Antes de Comenzar y Contenedores { #previous-steps-before-starting-and-containers }
Si estás usando contenedores (por ejemplo, Docker, Kubernetes), entonces hay dos enfoques principales que puedes usar.
### Múltiples Contenedores { #multiple-containers }
Si tienes **múltiples contenedores**, probablemente cada uno ejecutando un **proceso único** (por ejemplo, en un cluster de **Kubernetes**), entonces probablemente querrías tener un **contenedor separado** realizando el trabajo de los **pasos previos** en un solo contenedor, ejecutando un solo proceso, **antes** de ejecutar los contenedores worker replicados.
/// info | Información
Si estás usando Kubernetes, probablemente sería un [Contenedor de Inicialización](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Si en tu caso de uso no hay problema en ejecutar esos pasos previos **múltiples veces en paralelo** (por ejemplo, si no estás ejecutando migraciones de base de datos, sino simplemente verificando si la base de datos está lista), entonces también podrías simplemente ponerlos en cada contenedor justo antes de iniciar el proceso principal.
### Un Contenedor Único { #single-container }
Si tienes una configuración simple, con un **contenedor único** que luego inicia múltiples **worker processes** (o también solo un proceso), entonces podrías ejecutar esos pasos previos en el mismo contenedor, justo antes de iniciar el proceso con la aplicación.
### Imagen Base de Docker { #base-docker-image }
Solía haber una imagen oficial de Docker de FastAPI: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Pero ahora está obsoleta. ⛔️
Probablemente **no** deberías usar esta imagen base de Docker (o cualquier otra similar).
Si estás usando **Kubernetes** (u otros) y ya estás configurando la **replicación** a nivel de cluster, con múltiples **contenedores**. En esos casos, es mejor que **construyas una imagen desde cero** como se describe arriba: [Construir una Imagen de Docker para FastAPI](#build-a-docker-image-for-fastapi).
Y si necesitas tener múltiples workers, puedes simplemente utilizar la opción de línea de comandos `--workers`.
/// note | Detalles Técnicos
La imagen de Docker se creó cuando Uvicorn no soportaba gestionar y reiniciar workers muertos, por lo que era necesario usar Gunicorn con Uvicorn, lo que añadía bastante complejidad, solo para que Gunicorn gestionara y reiniciara los worker processes de Uvicorn.
Pero ahora que Uvicorn (y el comando `fastapi`) soportan el uso de `--workers`, no hay razón para utilizar una imagen base de Docker en lugar de construir la tuya propia (es prácticamente la misma cantidad de código 😅).
///
## Desplegar la Imagen del Contenedor { #deploy-the-container-image }
Después de tener una Imagen de Contenedor (Docker) hay varias maneras de desplegarla.
Por ejemplo:
* Con **Docker Compose** en un servidor único
* Con un cluster de **Kubernetes**
* Con un cluster de Docker Swarm Mode
* Con otra herramienta como Nomad
* Con un servicio en la nube que tome tu imagen de contenedor y la despliegue
## Imagen de Docker con `uv` { #docker-image-with-uv }
Si estás usando [uv](https://github.com/astral-sh/uv) para instalar y gestionar tu proyecto, puedes seguir su [guía de Docker de uv](https://docs.astral.sh/uv/guides/integration/docker/).
## Resumen { #recap }
Usando sistemas de contenedores (por ejemplo, con **Docker** y **Kubernetes**) se vuelve bastante sencillo manejar todos los **conceptos de despliegue**:
* HTTPS
* Ejecutar en el inicio
* Reinicios
* Replicación (el número de procesos en ejecución)
* Memoria
* Pasos previos antes de comenzar
En la mayoría de los casos, probablemente no querrás usar ninguna imagen base, y en su lugar **construir una imagen de contenedor desde cero** basada en la imagen oficial de Docker de Python.
Teniendo en cuenta el **orden** de las instrucciones en el `Dockerfile` y la **caché de Docker** puedes **minimizar los tiempos de construcción**, para maximizar tu productividad (y evitar el aburrimiento). 😎
================================================
FILE: docs/es/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Puedes desplegar tu app de FastAPI en [FastAPI Cloud](https://fastapicloud.com) con **un solo comando**; ve y únete a la lista de espera si aún no lo has hecho. 🚀
## Iniciar sesión { #login }
Asegúrate de que ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉).
Luego inicia sesión:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Desplegar { #deploy }
Ahora despliega tu app, con **un solo comando**:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
## Acerca de FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** está creado por el mismo autor y equipo detrás de **FastAPI**.
Agiliza el proceso de **crear**, **desplegar** y **acceder** a una API con el mínimo esfuerzo.
Aporta la misma **experiencia de desarrollador** de crear apps con FastAPI al **desplegarlas** en la nube. 🎉
También se encargará de la mayoría de las cosas que necesitas al desplegar una app, como:
* HTTPS
* Replicación, con autoescalado basado en requests
* etc.
FastAPI Cloud es el sponsor principal y proveedor de financiación de los proyectos open source de *FastAPI and friends*. ✨
## Desplegar en otros proveedores de la nube { #deploy-to-other-cloud-providers }
FastAPI es open source y está basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor de la nube que elijas.
Sigue las guías de tu proveedor de la nube para desplegar apps de FastAPI con ellos. 🤓
## Despliega tu propio servidor { #deploy-your-own-server }
También te enseñaré más adelante en esta guía de **Despliegue** todos los detalles, para que puedas entender qué está pasando, qué tiene que ocurrir o cómo desplegar apps de FastAPI por tu cuenta, también con tus propios servidores. 🤓
================================================
FILE: docs/es/docs/deployment/https.md
================================================
# Sobre HTTPS { #about-https }
Es fácil asumir que HTTPS es algo que simplemente está "activado" o no.
Pero es mucho más complejo que eso.
/// tip | Consejo
Si tienes prisa o no te importa, continúa con las siguientes secciones para ver instrucciones paso a paso para configurar todo con diferentes técnicas.
///
Para **aprender los conceptos básicos de HTTPS**, desde una perspectiva de consumidor, revisa [https://howhttps.works/](https://howhttps.works/).
Ahora, desde una **perspectiva de desarrollador**, aquí hay varias cosas a tener en cuenta al pensar en HTTPS:
* Para HTTPS, **el servidor** necesita **tener "certificados"** generados por un **tercero**.
* Esos certificados en realidad son **adquiridos** del tercero, no "generados".
* Los certificados tienen una **vida útil**.
* Ellos **expiran**.
* Y luego necesitan ser **renovados**, **adquiridos nuevamente** del tercero.
* La encriptación de la conexión ocurre a nivel de **TCP**.
* Esa es una capa **debajo de HTTP**.
* Por lo tanto, el manejo de **certificados y encriptación** se realiza **antes de HTTP**.
* **TCP no sabe acerca de "dominios"**. Solo sobre direcciones IP.
* La información sobre el **dominio específico** solicitado va en los **datos HTTP**.
* Los **certificados HTTPS** "certifican" un **cierto dominio**, pero el protocolo y la encriptación ocurren a nivel de TCP, **antes de saber** con cuál dominio se está tratando.
* **Por defecto**, eso significaría que solo puedes tener **un certificado HTTPS por dirección IP**.
* No importa cuán grande sea tu servidor o qué tan pequeña pueda ser cada aplicación que tengas en él.
* Sin embargo, hay una **solución** para esto.
* Hay una **extensión** para el protocolo **TLS** (el que maneja la encriptación a nivel de TCP, antes de HTTP) llamada **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* Esta extensión SNI permite que un solo servidor (con una **sola dirección IP**) tenga **varios certificados HTTPS** y sirva **múltiples dominios/aplicaciones HTTPS**.
* Para que esto funcione, un componente (programa) **único** que se ejecute en el servidor, escuchando en la **dirección IP pública**, debe tener **todos los certificados HTTPS** en el servidor.
* **Después** de obtener una conexión segura, el protocolo de comunicación sigue siendo **HTTP**.
* Los contenidos están **encriptados**, aunque se envién con el **protocolo HTTP**.
Es una práctica común tener **un programa/servidor HTTP** ejecutándose en el servidor (la máquina, host, etc.) y **gestionando todas las partes de HTTPS**: recibiendo los **requests HTTPS encriptados**, enviando los **requests HTTP desencriptados** a la aplicación HTTP real que se ejecuta en el mismo servidor (la aplicación **FastAPI**, en este caso), tomando el **response HTTP** de la aplicación, **encriptándolo** usando el **certificado HTTPS** adecuado y enviándolo de vuelta al cliente usando **HTTPS**. Este servidor a menudo se llama un **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)**.
Algunas de las opciones que podrías usar como un TLS Termination Proxy son:
* Traefik (que también puede manejar la renovación de certificados)
* Caddy (que también puede manejar la renovación de certificados)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Antes de Let's Encrypt, estos **certificados HTTPS** eran vendidos por terceros.
El proceso para adquirir uno de estos certificados solía ser complicado, requerir bastante papeleo y los certificados eran bastante costosos.
Pero luego se creó **[Let's Encrypt](https://letsencrypt.org/)**.
Es un proyecto de la Linux Foundation. Proporciona **certificados HTTPS de forma gratuita**, de manera automatizada. Estos certificados usan toda la seguridad criptográfica estándar, y tienen una corta duración (aproximadamente 3 meses), por lo que la **seguridad es en realidad mejor** debido a su lifespan reducida.
Los dominios son verificados de manera segura y los certificados se generan automáticamente. Esto también permite automatizar la renovación de estos certificados.
La idea es automatizar la adquisición y renovación de estos certificados para que puedas tener **HTTPS seguro, gratuito, para siempre**.
## HTTPS para Desarrolladores { #https-for-developers }
Aquí tienes un ejemplo de cómo podría ser una API HTTPS, paso a paso, prestando atención principalmente a las ideas importantes para los desarrolladores.
### Nombre de Dominio { #domain-name }
Probablemente todo comenzaría adquiriendo un **nombre de dominio**. Luego, lo configurarías en un servidor DNS (posiblemente tu mismo proveedor de la nube).
Probablemente conseguirías un servidor en la nube (una máquina virtual) o algo similar, y tendría una **dirección IP pública** fija.
En el/los servidor(es) DNS configurarías un registro (un "`A record`") para apuntar **tu dominio** a la **dirección IP pública de tu servidor**.
Probablemente harías esto solo una vez, la primera vez, al configurar todo.
/// tip | Consejo
Esta parte del Nombre de Dominio es mucho antes de HTTPS, pero como todo depende del dominio y la dirección IP, vale la pena mencionarlo aquí.
///
### DNS { #dns }
Ahora centrémonos en todas las partes realmente de HTTPS.
Primero, el navegador consultaría con los **servidores DNS** cuál es la **IP del dominio**, en este caso, `someapp.example.com`.
Los servidores DNS le dirían al navegador que use una **dirección IP** específica. Esa sería la dirección IP pública utilizada por tu servidor, que configuraste en los servidores DNS.
### Inicio del Handshake TLS { #tls-handshake-start }
El navegador luego se comunicaría con esa dirección IP en el **puerto 443** (el puerto HTTPS).
La primera parte de la comunicación es solo para establecer la conexión entre el cliente y el servidor y decidir las claves criptográficas que usarán, etc.
Esta interacción entre el cliente y el servidor para establecer la conexión TLS se llama **handshake TLS**.
### TLS con Extensión SNI { #tls-with-sni-extension }
**Solo un proceso** en el servidor puede estar escuchando en un **puerto** específico en una **dirección IP** específica. Podría haber otros procesos escuchando en otros puertos en la misma dirección IP, pero solo uno para cada combinación de dirección IP y puerto.
TLS (HTTPS) utiliza el puerto específico `443` por defecto. Así que ese es el puerto que necesitaríamos.
Como solo un proceso puede estar escuchando en este puerto, el proceso que lo haría sería el **TLS Termination Proxy**.
El TLS Termination Proxy tendría acceso a uno o más **certificados TLS** (certificados HTTPS).
Usando la **extensión SNI** discutida anteriormente, el TLS Termination Proxy verificaría cuál de los certificados TLS (HTTPS) disponibles debería usar para esta conexión, usando el que coincida con el dominio esperado por el cliente.
En este caso, usaría el certificado para `someapp.example.com`.
El cliente ya **confía** en la entidad que generó ese certificado TLS (en este caso Let's Encrypt, pero lo veremos más adelante), por lo que puede **verificar** que el certificado sea válido.
Luego, usando el certificado, el cliente y el TLS Termination Proxy **deciden cómo encriptar** el resto de la **comunicación TCP**. Esto completa la parte de **Handshake TLS**.
Después de esto, el cliente y el servidor tienen una **conexión TCP encriptada**, esto es lo que proporciona TLS. Y luego pueden usar esa conexión para iniciar la comunicación **HTTP real**.
Y eso es lo que es **HTTPS**, es simplemente HTTP simple **dentro de una conexión TLS segura** en lugar de una conexión TCP pura (sin encriptar).
/// tip | Consejo
Ten en cuenta que la encriptación de la comunicación ocurre a nivel de **TCP**, no a nivel de HTTP.
///
### Request HTTPS { #https-request }
Ahora que el cliente y el servidor (específicamente el navegador y el TLS Termination Proxy) tienen una **conexión TCP encriptada**, pueden iniciar la **comunicación HTTP**.
Así que, el cliente envía un **request HTTPS**. Esto es simplemente un request HTTP a través de una conexión TLS encriptada.
### Desencriptar el Request { #decrypt-the-request }
El TLS Termination Proxy usaría la encriptación acordada para **desencriptar el request**, y transmitiría el **request HTTP simple (desencriptado)** al proceso que ejecuta la aplicación (por ejemplo, un proceso con Uvicorn ejecutando la aplicación FastAPI).
### Response HTTP { #http-response }
La aplicación procesaría el request y enviaría un **response HTTP simple (sin encriptar)** al TLS Termination Proxy.
### Response HTTPS { #https-response }
El TLS Termination Proxy entonces **encriptaría el response** usando la criptografía acordada antes (que comenzó con el certificado para `someapp.example.com`), y lo enviaría de vuelta al navegador.
Luego, el navegador verificaría que el response sea válido y encriptado con la clave criptográfica correcta, etc. Entonces **desencriptaría el response** y lo procesaría.
El cliente (navegador) sabrá que el response proviene del servidor correcto porque está utilizando la criptografía que acordaron usando el **certificado HTTPS** anteriormente.
### Múltiples Aplicaciones { #multiple-applications }
En el mismo servidor (o servidores), podrían haber **múltiples aplicaciones**, por ejemplo, otros programas API o una base de datos.
Solo un proceso puede estar gestionando la IP y puerto específica (el TLS Termination Proxy en nuestro ejemplo) pero las otras aplicaciones/procesos pueden estar ejecutándose en el/los servidor(es) también, siempre y cuando no intenten usar la misma **combinación de IP pública y puerto**.
De esa manera, el TLS Termination Proxy podría gestionar HTTPS y certificados para **múltiples dominios**, para múltiples aplicaciones, y luego transmitir los requests a la aplicación correcta en cada caso.
### Renovación de Certificados { #certificate-renewal }
En algún momento en el futuro, cada certificado **expiraría** (alrededor de 3 meses después de haberlo adquirido).
Y entonces, habría otro programa (en algunos casos es otro programa, en algunos casos podría ser el mismo TLS Termination Proxy) que hablaría con Let's Encrypt y renovaría el/los certificado(s).
Los **certificados TLS** están **asociados con un nombre de dominio**, no con una dirección IP.
Entonces, para renovar los certificados, el programa de renovación necesita **probar** a la autoridad (Let's Encrypt) que de hecho **"posee" y controla ese dominio**.
Para hacer eso, y para acomodar diferentes necesidades de aplicaciones, hay varias formas en que puede hacerlo. Algunas formas populares son:
* **Modificar algunos registros DNS**.
* Para esto, el programa de renovación necesita soportar las API del proveedor de DNS, por lo que, dependiendo del proveedor de DNS que estés utilizando, esto podría o no ser una opción.
* **Ejecutarse como un servidor** (al menos durante el proceso de adquisición del certificado) en la dirección IP pública asociada con el dominio.
* Como dijimos anteriormente, solo un proceso puede estar escuchando en una IP y puerto específicos.
* Esta es una de las razones por las que es muy útil cuando el mismo TLS Termination Proxy también se encarga del proceso de renovación del certificado.
* De lo contrario, podrías tener que detener momentáneamente el TLS Termination Proxy, iniciar el programa de renovación para adquirir los certificados, luego configurarlos con el TLS Termination Proxy, y luego reiniciar el TLS Termination Proxy. Esto no es ideal, ya que tus aplicaciones no estarán disponibles durante el tiempo que el TLS Termination Proxy esté apagado.
Todo este proceso de renovación, mientras aún se sirve la aplicación, es una de las principales razones por las que querrías tener un **sistema separado para gestionar el HTTPS** con un TLS Termination Proxy en lugar de simplemente usar los certificados TLS con el servidor de aplicaciones directamente (por ejemplo, Uvicorn).
## Headers reenviados por el proxy { #proxy-forwarded-headers }
Al usar un proxy para gestionar HTTPS, tu **servidor de aplicaciones** (por ejemplo Uvicorn vía FastAPI CLI) no sabe nada sobre el proceso HTTPS, se comunica con HTTP simple con el **TLS Termination Proxy**.
Este **proxy** normalmente configuraría algunos headers HTTP sobre la marcha antes de transmitir el request al **servidor de aplicaciones**, para hacerle saber al servidor de aplicaciones que el request está siendo **reenviado** por el proxy.
/// note | Detalles técnicos
Los headers del proxy son:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Aun así, como el **servidor de aplicaciones** no sabe que está detrás de un **proxy** de confianza, por defecto, no confiaría en esos headers.
Pero puedes configurar el **servidor de aplicaciones** para confiar en los headers reenviados enviados por el **proxy**. Si estás usando FastAPI CLI, puedes usar la *Opción de la CLI* `--forwarded-allow-ips` para indicarle desde qué IPs debería confiar en esos headers reenviados.
Por ejemplo, si el **servidor de aplicaciones** solo está recibiendo comunicación del **proxy** de confianza, puedes establecerlo en `--forwarded-allow-ips="*"` para hacer que confíe en todas las IPs entrantes, ya que solo recibirá requests desde la IP que sea utilizada por el **proxy**.
De esta manera la aplicación podrá saber cuál es su propia URL pública, si está usando HTTPS, el dominio, etc.
Esto sería útil, por ejemplo, para manejar correctamente redirecciones.
/// tip | Consejo
Puedes aprender más sobre esto en la documentación de [Detrás de un proxy - Habilitar headers reenviados por el proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Resumen { #recap }
Tener **HTTPS** es muy importante y bastante **crítico** en la mayoría de los casos. La mayor parte del esfuerzo que como desarrollador tienes que poner en torno a HTTPS es solo sobre **entender estos conceptos** y cómo funcionan.
Pero una vez que conoces la información básica de **HTTPS para desarrolladores** puedes combinar y configurar fácilmente diferentes herramientas para ayudarte a gestionar todo de una manera sencilla.
En algunos de los siguientes capítulos, te mostraré varios ejemplos concretos de cómo configurar **HTTPS** para aplicaciones **FastAPI**. 🔒
================================================
FILE: docs/es/docs/deployment/index.md
================================================
# Despliegue { #deployment }
Desplegar una aplicación **FastAPI** es relativamente fácil.
## Qué Significa Despliegue { #what-does-deployment-mean }
**Desplegar** una aplicación significa realizar los pasos necesarios para hacerla **disponible para los usuarios**.
Para una **API web**, normalmente implica ponerla en una **máquina remota**, con un **programa de servidor** que proporcione buen rendimiento, estabilidad, etc., para que tus **usuarios** puedan **acceder** a la aplicación de manera eficiente y sin interrupciones o problemas.
Esto contrasta con las etapas de **desarrollo**, donde estás constantemente cambiando el código, rompiéndolo y arreglándolo, deteniendo y reiniciando el servidor de desarrollo, etc.
## Estrategias de Despliegue { #deployment-strategies }
Hay varias maneras de hacerlo dependiendo de tu caso de uso específico y las herramientas que utilices.
Podrías **desplegar un servidor** tú mismo utilizando una combinación de herramientas, podrías usar un **servicio en la nube** que hace parte del trabajo por ti, u otras opciones posibles.
Por ejemplo, nosotros, el equipo detrás de FastAPI, construimos [**FastAPI Cloud**](https://fastapicloud.com), para hacer que desplegar aplicaciones de FastAPI en la nube sea lo más ágil posible, con la misma experiencia de desarrollador de trabajar con FastAPI.
Te mostraré algunos de los conceptos principales que probablemente deberías tener en cuenta al desplegar una aplicación **FastAPI** (aunque la mayoría se aplica a cualquier otro tipo de aplicación web).
Verás más detalles a tener en cuenta y algunas de las técnicas para hacerlo en las siguientes secciones. ✨
================================================
FILE: docs/es/docs/deployment/manually.md
================================================
# Ejecutar un Servidor Manualmente { #run-a-server-manually }
## Usa el Comando `fastapi run` { #use-the-fastapi-run-command }
En resumen, usa `fastapi run` para servir tu aplicación FastAPI:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Eso funcionaría para la mayoría de los casos. 😎
Podrías usar ese comando, por ejemplo, para iniciar tu app **FastAPI** en un contenedor, en un servidor, etc.
## Servidores ASGI { #asgi-servers }
Vamos a profundizar un poquito en los detalles.
FastAPI usa un estándar para construir frameworks de web y servidores de Python llamado ASGI. FastAPI es un framework web ASGI.
Lo principal que necesitas para ejecutar una aplicación **FastAPI** (o cualquier otra aplicación ASGI) en una máquina de servidor remota es un programa de servidor ASGI como **Uvicorn**, que es el que viene por defecto en el comando `fastapi`.
Hay varias alternativas, incluyendo:
* [Uvicorn](https://www.uvicorn.dev/): un servidor ASGI de alto rendimiento.
* [Hypercorn](https://hypercorn.readthedocs.io/): un servidor ASGI compatible con HTTP/2 y Trio entre otras funcionalidades.
* [Daphne](https://github.com/django/daphne): el servidor ASGI construido para Django Channels.
* [Granian](https://github.com/emmett-framework/granian): Un servidor HTTP Rust para aplicaciones en Python.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit es un runtime para aplicaciones web ligero y versátil.
## Máquina Servidor y Programa Servidor { #server-machine-and-server-program }
Hay un pequeño detalle sobre los nombres que hay que tener en cuenta. 💡
La palabra "**servidor**" se utiliza comúnmente para referirse tanto al computador remoto/en la nube (la máquina física o virtual) como al programa que se está ejecutando en esa máquina (por ejemplo, Uvicorn).
Solo ten en cuenta que cuando leas "servidor" en general, podría referirse a una de esas dos cosas.
Al referirse a la máquina remota, es común llamarla **servidor**, pero también **máquina**, **VM** (máquina virtual), **nodo**. Todos esos se refieren a algún tipo de máquina remota, generalmente con Linux, donde ejecutas programas.
## Instala el Programa del Servidor { #install-the-server-program }
Cuando instalas FastAPI, viene con un servidor de producción, Uvicorn, y puedes iniciarlo con el comando `fastapi run`.
Pero también puedes instalar un servidor ASGI manualmente.
Asegúrate de crear un [entorno virtual](../virtual-environments.md), actívalo, y luego puedes instalar la aplicación del servidor.
Por ejemplo, para instalar Uvicorn:
Un proceso similar se aplicaría a cualquier otro programa de servidor ASGI.
/// tip | Consejo
Al añadir `standard`, Uvicorn instalará y usará algunas dependencias adicionales recomendadas.
Eso incluye `uvloop`, el reemplazo de alto rendimiento para `asyncio`, que proporciona un gran impulso de rendimiento en concurrencia.
Cuando instalas FastAPI con algo como `pip install "fastapi[standard]"` ya obtienes `uvicorn[standard]` también.
///
## Ejecuta el Programa del Servidor { #run-the-server-program }
Si instalaste un servidor ASGI manualmente, normalmente necesitarías pasar una cadena de import en un formato especial para que importe tu aplicación FastAPI:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Nota
El comando `uvicorn main:app` se refiere a:
* `main`: el archivo `main.py` (el "módulo" de Python).
* `app`: el objeto creado dentro de `main.py` con la línea `app = FastAPI()`.
Es equivalente a:
```Python
from main import app
```
///
Cada programa alternativo de servidor ASGI tendría un comando similar, puedes leer más en su respectiva documentación.
/// warning | Advertencia
Uvicorn y otros servidores soportan una opción `--reload` que es útil durante el desarrollo.
La opción `--reload` consume muchos más recursos, es más inestable, etc.
Ayuda mucho durante el **desarrollo**, pero **no** deberías usarla en **producción**.
///
## Conceptos de Despliegue { #deployment-concepts }
Estos ejemplos ejecutan el programa del servidor (por ejemplo, Uvicorn), iniciando **un solo proceso**, escuchando en todas las IPs (`0.0.0.0`) en un puerto predefinido (por ejemplo, `80`).
Esta es la idea básica. Pero probablemente querrás encargarte de algunas cosas adicionales, como:
* Seguridad - HTTPS
* Ejecución en el arranque
* Reinicios
* Replicación (el número de procesos ejecutándose)
* Memoria
* Pasos previos antes de comenzar
Te contaré más sobre cada uno de estos conceptos, cómo pensarlos, y algunos ejemplos concretos con estrategias para manejarlos en los próximos capítulos. 🚀
================================================
FILE: docs/es/docs/deployment/server-workers.md
================================================
# Servidores Workers - Uvicorn con Workers { #server-workers-uvicorn-with-workers }
Vamos a revisar esos conceptos de despliegue de antes:
* Seguridad - HTTPS
* Ejecución al inicio
* Reinicios
* **Replicación (el número de procesos en ejecución)**
* Memoria
* Pasos previos antes de empezar
Hasta este punto, con todos los tutoriales en la documentación, probablemente has estado ejecutando un **programa de servidor**, por ejemplo, usando el comando `fastapi`, que ejecuta Uvicorn, corriendo un **solo proceso**.
Al desplegar aplicaciones probablemente querrás tener algo de **replicación de procesos** para aprovechar **múltiples núcleos** y poder manejar más requests.
Como viste en el capítulo anterior sobre [Conceptos de Despliegue](concepts.md), hay múltiples estrategias que puedes usar.
Aquí te mostraré cómo usar **Uvicorn** con **worker processes** usando el comando `fastapi` o el comando `uvicorn` directamente.
/// info | Información
Si estás usando contenedores, por ejemplo con Docker o Kubernetes, te contaré más sobre eso en el próximo capítulo: [FastAPI en Contenedores - Docker](docker.md).
En particular, cuando corras en **Kubernetes** probablemente **no** querrás usar workers y en cambio correr **un solo proceso de Uvicorn por contenedor**, pero te contaré sobre eso más adelante en ese capítulo.
///
## Múltiples Workers { #multiple-workers }
Puedes iniciar múltiples workers con la opción de línea de comando `--workers`:
//// tab | `fastapi`
Si usas el comando `fastapi`:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Si prefieres usar el comando `uvicorn` directamente:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
La única opción nueva aquí es `--workers` indicando a Uvicorn que inicie 4 worker processes.
También puedes ver que muestra el **PID** de cada proceso, `27365` para el proceso padre (este es el **gestor de procesos**) y uno para cada worker process: `27368`, `27369`, `27370`, y `27367`.
## Conceptos de Despliegue { #deployment-concepts }
Aquí viste cómo usar múltiples **workers** para **paralelizar** la ejecución de la aplicación, aprovechar los **múltiples núcleos** del CPU, y poder servir **más requests**.
De la lista de conceptos de despliegue de antes, usar workers ayudaría principalmente con la parte de **replicación**, y un poquito con los **reinicios**, pero aún necesitas encargarte de los otros:
* **Seguridad - HTTPS**
* **Ejecución al inicio**
* ***Reinicios***
* Replicación (el número de procesos en ejecución)
* **Memoria**
* **Pasos previos antes de empezar**
## Contenedores y Docker { #containers-and-docker }
En el próximo capítulo sobre [FastAPI en Contenedores - Docker](docker.md) te explicaré algunas estrategias que podrías usar para manejar los otros **conceptos de despliegue**.
Te mostraré cómo **construir tu propia imagen desde cero** para ejecutar un solo proceso de Uvicorn. Es un proceso sencillo y probablemente es lo que querrías hacer al usar un sistema de gestión de contenedores distribuido como **Kubernetes**.
## Resumen { #recap }
Puedes usar múltiples worker processes con la opción CLI `--workers` con los comandos `fastapi` o `uvicorn` para aprovechar los **CPUs de múltiples núcleos**, para ejecutar **múltiples procesos en paralelo**.
Podrías usar estas herramientas e ideas si estás instalando **tu propio sistema de despliegue** mientras te encargas tú mismo de los otros conceptos de despliegue.
Revisa el próximo capítulo para aprender sobre **FastAPI** con contenedores (por ejemplo, Docker y Kubernetes). Verás que esas herramientas tienen formas sencillas de resolver los otros **conceptos de despliegue** también. ✨
================================================
FILE: docs/es/docs/deployment/versions.md
================================================
# Sobre las versiones de FastAPI { #about-fastapi-versions }
**FastAPI** ya se está utilizando en producción en muchas aplicaciones y sistemas. Y la cobertura de tests se mantiene al 100%. Pero su desarrollo sigue avanzando rápidamente.
Se añaden nuevas funcionalidades con frecuencia, se corrigen bugs regularmente, y el código sigue mejorando continuamente.
Por eso las versiones actuales siguen siendo `0.x.x`, esto refleja que cada versión podría tener potencialmente cambios incompatibles. Esto sigue las convenciones de [Semantic Versioning](https://semver.org/).
Puedes crear aplicaciones de producción con **FastAPI** ahora mismo (y probablemente ya lo has estado haciendo desde hace algún tiempo), solo debes asegurarte de que utilizas una versión que funciona correctamente con el resto de tu código.
## Fija tu versión de `fastapi` { #pin-your-fastapi-version }
Lo primero que debes hacer es "fijar" la versión de **FastAPI** que estás usando a la versión específica más reciente que sabes que funciona correctamente para tu aplicación.
Por ejemplo, digamos que estás utilizando la versión `0.112.0` en tu aplicación.
Si usas un archivo `requirements.txt` podrías especificar la versión con:
```txt
fastapi[standard]==0.112.0
```
eso significaría que usarías exactamente la versión `0.112.0`.
O también podrías fijarla con:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
eso significaría que usarías las versiones `0.112.0` o superiores, pero menores que `0.113.0`, por ejemplo, una versión `0.112.2` todavía sería aceptada.
Si utilizas cualquier otra herramienta para gestionar tus instalaciones, como `uv`, Poetry, Pipenv, u otras, todas tienen una forma que puedes usar para definir versiones específicas para tus paquetes.
## Versiones disponibles { #available-versions }
Puedes ver las versiones disponibles (por ejemplo, para revisar cuál es la más reciente) en las [Release Notes](../release-notes.md).
## Sobre las versiones { #about-versions }
Siguiendo las convenciones del Semantic Versioning, cualquier versión por debajo de `1.0.0` podría potencialmente añadir cambios incompatibles.
FastAPI también sigue la convención de que cualquier cambio de versión "PATCH" es para corrección de bugs y cambios no incompatibles.
/// tip | Consejo
El "PATCH" es el último número, por ejemplo, en `0.2.3`, la versión PATCH es `3`.
///
Así que deberías poder fijar a una versión como:
```txt
fastapi>=0.45.0,<0.46.0
```
Los cambios incompatibles y nuevas funcionalidades se añaden en versiones "MINOR".
/// tip | Consejo
El "MINOR" es el número en el medio, por ejemplo, en `0.2.3`, la versión MINOR es `2`.
///
## Actualizando las versiones de FastAPI { #upgrading-the-fastapi-versions }
Deberías añadir tests para tu aplicación.
Con **FastAPI** es muy fácil (gracias a Starlette), revisa la documentación: [Escribir pruebas](../tutorial/testing.md)
Después de tener tests, puedes actualizar la versión de **FastAPI** a una más reciente, y asegurarte de que todo tu código está funcionando correctamente ejecutando tus tests.
Si todo está funcionando, o después de hacer los cambios necesarios, y todos tus tests pasan, entonces puedes fijar tu `fastapi` a esa nueva versión más reciente.
## Sobre Starlette { #about-starlette }
No deberías fijar la versión de `starlette`.
Diferentes versiones de **FastAPI** utilizarán una versión más reciente específica de Starlette.
Así que, puedes simplemente dejar que **FastAPI** use la versión correcta de Starlette.
## Sobre Pydantic { #about-pydantic }
Pydantic incluye los tests para **FastAPI** con sus propios tests, así que nuevas versiones de Pydantic (por encima de `1.0.0`) siempre son compatibles con FastAPI.
Puedes fijar Pydantic a cualquier versión por encima de `1.0.0` que funcione para ti.
Por ejemplo:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/es/docs/editor-support.md
================================================
# Soporte del editor { #editor-support }
La [Extensión de FastAPI](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) oficial mejora tu flujo de trabajo de desarrollo con FastAPI con descubrimiento de *path operation*, navegación, además de deployment a FastAPI Cloud y streaming en vivo de logs.
Para más detalles sobre la extensión, consulta el README en el [repositorio de GitHub](https://github.com/fastapi/fastapi-vscode).
## Configuración e instalación { #setup-and-installation }
La **Extensión de FastAPI** está disponible tanto para [VS Code](https://code.visualstudio.com/) como para [Cursor](https://www.cursor.com/). Se puede instalar directamente desde el panel de Extensiones en cada editor buscando "FastAPI" y seleccionando la extensión publicada por **FastAPI Labs**. La extensión también funciona en editores basados en navegador como [vscode.dev](https://vscode.dev) y [github.dev](https://github.dev).
### Descubrimiento de la aplicación { #application-discovery }
Por defecto, la extensión descubrirá automáticamente aplicaciones FastAPI en tu espacio de trabajo escaneando archivos que creen un instance de `FastAPI()`. Si la detección automática no funciona con la estructura de tu proyecto, puedes especificar un punto de entrada mediante `[tool.fastapi]` en `pyproject.toml` o la configuración de VS Code `fastapi.entryPoint` usando notación de módulo (p. ej. `myapp.main:app`).
## Funcionalidades { #features }
- **Explorador de Path Operations** - Una vista en árbol en la barra lateral de todas las *path operations* de tu aplicación. Haz clic para saltar a cualquier definición de ruta o de router.
- **Búsqueda de rutas** - Busca por path, método o nombre con Ctrl + Shift + E (en macOS: Cmd + Shift + E).
- **Navegación con CodeLens** - Enlaces clicables encima de llamadas del cliente de tests (p. ej. `client.get('/items')`) que saltan a la *path operation* correspondiente para navegar rápidamente entre tests e implementación.
- **Desplegar en FastAPI Cloud** - Deployment con un clic de tu app a [FastAPI Cloud](https://fastapicloud.com/).
- **Streaming de logs de la aplicación** - Streaming en tiempo real de logs desde tu aplicación desplegada en FastAPI Cloud, con filtrado por nivel y búsqueda de texto.
Si quieres familiarizarte con las funcionalidades de la extensión, puedes revisar el recorrido guiado de la extensión abriendo la Paleta de Comandos (Ctrl + Shift + P o en macOS: Cmd + Shift + P) y seleccionando "Welcome: Open walkthrough..." y luego eligiendo el recorrido "Get started with FastAPI".
================================================
FILE: docs/es/docs/environment-variables.md
================================================
# Variables de Entorno { #environment-variables }
/// tip | Consejo
Si ya sabes qué son las "variables de entorno" y cómo usarlas, siéntete libre de saltarte esto.
///
Una variable de entorno (también conocida como "**env var**") es una variable que vive **fuera** del código de Python, en el **sistema operativo**, y podría ser leída por tu código de Python (o por otros programas también).
Las variables de entorno pueden ser útiles para manejar **configuraciones** de aplicaciones, como parte de la **instalación** de Python, etc.
## Crear y Usar Variables de Entorno { #create-and-use-env-vars }
Puedes **crear** y usar variables de entorno en la **shell (terminal)**, sin necesidad de Python:
//// tab | Linux, macOS, Windows Bash
```console
// Podrías crear una env var MY_NAME con
$ export MY_NAME="Wade Wilson"
// Luego podrías usarla con otros programas, como
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Crea una env var MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Úsala con otros programas, como
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Leer Variables de Entorno en Python { #read-env-vars-in-python }
También podrías crear variables de entorno **fuera** de Python, en la terminal (o con cualquier otro método), y luego **leerlas en Python**.
Por ejemplo, podrías tener un archivo `main.py` con:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Consejo
El segundo argumento de [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) es el valor por defecto a retornar.
Si no se proporciona, es `None` por defecto; aquí proporcionamos `"World"` como el valor por defecto para usar.
///
Luego podrías llamar a ese programa Python:
//// tab | Linux, macOS, Windows Bash
```console
// Aquí todavía no configuramos la env var
$ python main.py
// Como no configuramos la env var, obtenemos el valor por defecto
Hello World from Python
// Pero si creamos una variable de entorno primero
$ export MY_NAME="Wade Wilson"
// Y luego llamamos al programa nuevamente
$ python main.py
// Ahora puede leer la variable de entorno
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Aquí todavía no configuramos la env var
$ python main.py
// Como no configuramos la env var, obtenemos el valor por defecto
Hello World from Python
// Pero si creamos una variable de entorno primero
$ $Env:MY_NAME = "Wade Wilson"
// Y luego llamamos al programa nuevamente
$ python main.py
// Ahora puede leer la variable de entorno
Hello Wade Wilson from Python
```
////
Dado que las variables de entorno pueden configurarse fuera del código, pero pueden ser leídas por el código, y no tienen que ser almacenadas (committed en `git`) con el resto de los archivos, es común usarlas para configuraciones o **ajustes**.
También puedes crear una variable de entorno solo para una **invocación específica de un programa**, que está disponible solo para ese programa, y solo durante su duración.
Para hacer eso, créala justo antes del programa en sí, en la misma línea:
```console
// Crea una env var MY_NAME en línea para esta llamada del programa
$ MY_NAME="Wade Wilson" python main.py
// Ahora puede leer la variable de entorno
Hello Wade Wilson from Python
// La env var ya no existe después
$ python main.py
Hello World from Python
```
/// tip | Consejo
Puedes leer más al respecto en [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Tipos y Validación { #types-and-validation }
Estas variables de entorno solo pueden manejar **strings de texto**, ya que son externas a Python y deben ser compatibles con otros programas y el resto del sistema (e incluso con diferentes sistemas operativos, como Linux, Windows, macOS).
Esto significa que **cualquier valor** leído en Python desde una variable de entorno **será un `str`**, y cualquier conversión a un tipo diferente o cualquier validación tiene que hacerse en el código.
Aprenderás más sobre cómo usar variables de entorno para manejar **configuraciones de aplicación** en la [Guía del Usuario Avanzado - Ajustes y Variables de Entorno](./advanced/settings.md).
## Variable de Entorno `PATH` { #path-environment-variable }
Hay una variable de entorno **especial** llamada **`PATH`** que es utilizada por los sistemas operativos (Linux, macOS, Windows) para encontrar programas a ejecutar.
El valor de la variable `PATH` es un string largo que consiste en directorios separados por dos puntos `:` en Linux y macOS, y por punto y coma `;` en Windows.
Por ejemplo, la variable de entorno `PATH` podría verse así:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Esto significa que el sistema debería buscar programas en los directorios:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Esto significa que el sistema debería buscar programas en los directorios:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Cuando escribes un **comando** en la terminal, el sistema operativo **busca** el programa en **cada uno de esos directorios** listados en la variable de entorno `PATH`.
Por ejemplo, cuando escribes `python` en la terminal, el sistema operativo busca un programa llamado `python` en el **primer directorio** de esa lista.
Si lo encuentra, entonces lo **utilizará**. De lo contrario, continúa buscando en los **otros directorios**.
### Instalando Python y Actualizando el `PATH` { #installing-python-and-updating-the-path }
Cuando instalas Python, se te podría preguntar si deseas actualizar la variable de entorno `PATH`.
//// tab | Linux, macOS
Digamos que instalas Python y termina en un directorio `/opt/custompython/bin`.
Si dices que sí para actualizar la variable de entorno `PATH`, entonces el instalador añadirá `/opt/custompython/bin` a la variable de entorno `PATH`.
Podría verse así:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
De esta manera, cuando escribes `python` en la terminal, el sistema encontrará el programa Python en `/opt/custompython/bin` (el último directorio) y usará ese.
////
//// tab | Windows
Digamos que instalas Python y termina en un directorio `C:\opt\custompython\bin`.
Si dices que sí para actualizar la variable de entorno `PATH`, entonces el instalador añadirá `C:\opt\custompython\bin` a la variable de entorno `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
De esta manera, cuando escribes `python` en la terminal, el sistema encontrará el programa Python en `C:\opt\custompython\bin` (el último directorio) y usará ese.
////
Entonces, si escribes:
```console
$ python
```
//// tab | Linux, macOS
El sistema **encontrará** el programa `python` en `/opt/custompython/bin` y lo ejecutará.
Esto sería más o menos equivalente a escribir:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
El sistema **encontrará** el programa `python` en `C:\opt\custompython\bin\python` y lo ejecutará.
Esto sería más o menos equivalente a escribir:
```console
$ C:\opt\custompython\bin\python
```
////
Esta información será útil al aprender sobre [Entornos Virtuales](virtual-environments.md).
## Conclusión { #conclusion }
Con esto deberías tener una comprensión básica de qué son las **variables de entorno** y cómo usarlas en Python.
También puedes leer más sobre ellas en la [Wikipedia para Variable de Entorno](https://en.wikipedia.org/wiki/Environment_variable).
En muchos casos no es muy obvio cómo las variables de entorno serían útiles y aplicables de inmediato. Pero siguen apareciendo en muchos escenarios diferentes cuando estás desarrollando, así que es bueno conocerlas.
Por ejemplo, necesitarás esta información en la siguiente sección, sobre [Entornos Virtuales](virtual-environments.md).
================================================
FILE: docs/es/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** es un programa de línea de comandos que puedes usar para servir tu aplicación FastAPI, gestionar tu proyecto FastAPI, y más.
Cuando instalas FastAPI (por ejemplo, con `pip install "fastapi[standard]"`), viene con un programa de línea de comandos que puedes ejecutar en la terminal.
Para ejecutar tu aplicación FastAPI en modo de desarrollo, puedes usar el comando `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Consejo
Para producción usarías `fastapi run` en lugar de `fastapi dev`. 🚀
///
Internamente, **FastAPI CLI** usa [Uvicorn](https://www.uvicorn.dev), un servidor ASGI de alto rendimiento y listo para producción. 😎
El CLI `fastapi` intentará detectar automáticamente la app de FastAPI que debe ejecutar, asumiendo que es un objeto llamado `app` en un archivo `main.py` (o un par de variantes más).
Pero puedes configurar explícitamente la app a usar.
## Configura el `entrypoint` de la app en `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Puedes configurar dónde está tu app en un archivo `pyproject.toml` así:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Ese `entrypoint` le dirá al comando `fastapi` que debe importar la app así:
```python
from main import app
```
Si tu código estuviera estructurado así:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Entonces establecerías el `entrypoint` como:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
lo cual sería equivalente a:
```python
from backend.main import app
```
### `fastapi dev` con path { #fastapi-dev-with-path }
También puedes pasar el path del archivo al comando `fastapi dev`, y adivinará el objeto app de FastAPI a usar:
```console
$ fastapi dev main.py
```
Pero tendrías que recordar pasar el path correcto cada vez que llames al comando `fastapi`.
Adicionalmente, otras herramientas podrían no ser capaces de encontrarla, por ejemplo la [Extensión de VS Code](editor-support.md) o [FastAPI Cloud](https://fastapicloud.com), así que se recomienda usar el `entrypoint` en `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
Ejecutar `fastapi dev` inicia el modo de desarrollo.
Por defecto, **auto-reload** está habilitado, recargando automáticamente el servidor cuando realizas cambios en tu código. Esto consume muchos recursos y podría ser menos estable que cuando está deshabilitado. Deberías usarlo solo para desarrollo. También escucha en la dirección IP `127.0.0.1`, que es la IP para que tu máquina se comunique solo consigo misma (`localhost`).
## `fastapi run` { #fastapi-run }
Ejecutar `fastapi run` inicia FastAPI en modo de producción por defecto.
Por defecto, **auto-reload** está deshabilitado. También escucha en la dirección IP `0.0.0.0`, lo que significa todas las direcciones IP disponibles, de esta manera será accesible públicamente por cualquiera que pueda comunicarse con la máquina. Esta es la manera en la que normalmente lo ejecutarías en producción, por ejemplo, en un contenedor.
En la mayoría de los casos tendrías (y deberías) tener un "proxy de terminación" manejando HTTPS por ti, esto dependerá de cómo despliegues tu aplicación, tu proveedor podría hacer esto por ti, o podrías necesitar configurarlo tú mismo.
/// tip | Consejo
Puedes aprender más al respecto en la [documentación de despliegue](deployment/index.md).
///
================================================
FILE: docs/es/docs/features.md
================================================
# Funcionalidades { #features }
## Funcionalidades de FastAPI { #fastapi-features }
**FastAPI** te ofrece lo siguiente:
### Basado en estándares abiertos { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) para la creación de APIs, incluyendo declaraciones de path operations, parámetros, request bodies, seguridad, etc.
* Documentación automática de modelos de datos con [**JSON Schema**](https://json-schema.org/) (ya que OpenAPI en sí mismo está basado en JSON Schema).
* Diseñado alrededor de estos estándares, tras un estudio meticuloso. En lugar de ser una capa adicional.
* Esto también permite el uso de **generación de código cliente automática** en muchos idiomas.
### Documentación automática { #automatic-docs }
Interfaces web de documentación y exploración de APIs interactivas. Como el framework está basado en OpenAPI, hay múltiples opciones, 2 incluidas por defecto.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), con exploración interactiva, llama y prueba tu API directamente desde el navegador.

* Documentación alternativa de API con [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Solo Python moderno { #just-modern-python }
Todo está basado en declaraciones estándar de **tipos en Python** (gracias a Pydantic). Sin nueva sintaxis que aprender. Solo Python moderno estándar.
Si necesitas un repaso de 2 minutos sobre cómo usar tipos en Python (aunque no uses FastAPI), revisa el tutorial corto: [Tipos en Python](python-types.md).
Escribes Python estándar con tipos:
```Python
from datetime import date
from pydantic import BaseModel
# Declara una variable como un str
# y obtiene soporte del editor dentro de la función
def main(user_id: str):
return user_id
# Un modelo de Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Que luego puede ser usado como:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Información
`**second_user_data` significa:
Pasa las claves y valores del dict `second_user_data` directamente como argumentos de clave-valor, equivalente a: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Soporte del editor { #editor-support }
Todo el framework fue diseñado para ser fácil e intuitivo de usar, todas las decisiones fueron probadas en múltiples editores incluso antes de comenzar el desarrollo, para asegurar la mejor experiencia de desarrollo.
En las encuestas a desarrolladores de Python, es claro [que una de las funcionalidades más usadas es el "autocompletado"](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Todo el framework **FastAPI** está basado para satisfacer eso. El autocompletado funciona en todas partes.
Rara vez necesitarás regresar a la documentación.
Aquí está cómo tu editor podría ayudarte:
* en [Visual Studio Code](https://code.visualstudio.com/):

* en [PyCharm](https://www.jetbrains.com/pycharm/):

Obtendrás autocompletado en código que podrías considerar imposible antes. Por ejemplo, la clave `price` dentro de un cuerpo JSON (que podría haber estado anidado) que proviene de un request.
No más escribir nombres de claves incorrectos, yendo de un lado a otro entre la documentación, o desplazándote hacia arriba y abajo para encontrar si finalmente usaste `username` o `user_name`.
### Breve { #short }
Tiene **valores por defecto** sensatos para todo, con configuraciones opcionales en todas partes. Todos los parámetros se pueden ajustar finamente para hacer lo que necesitas y para definir el API que necesitas.
Pero por defecto, todo **"simplemente funciona"**.
### Validación { #validation }
* Validación para la mayoría (¿o todas?) de los **tipos de datos** de Python, incluyendo:
* Objetos JSON (`dict`).
* array JSON (`list`) definiendo tipos de elementos.
* Campos de string (`str`), definiendo longitudes mínimas y máximas.
* Números (`int`, `float`) con valores mínimos y máximos, etc.
* Validación para tipos más exóticos, como:
* URL.
* Email.
* UUID.
* ...y otros.
Toda la validación es manejada por **Pydantic**, una herramienta bien establecida y robusta.
### Seguridad y autenticación { #security-and-authentication }
Seguridad y autenticación integradas. Sin ningún compromiso con bases de datos o modelos de datos.
Todos los esquemas de seguridad definidos en OpenAPI, incluyendo:
* HTTP Básico.
* **OAuth2** (también con **tokens JWT**). Revisa el tutorial sobre [OAuth2 con JWT](tutorial/security/oauth2-jwt.md).
* API keys en:
* Headers.
* Parámetros de query.
* Cookies, etc.
Además de todas las características de seguridad de Starlette (incluyendo **cookies de sesión**).
Todo construido como herramientas y componentes reutilizables que son fáciles de integrar con tus sistemas, almacenes de datos, bases de datos relacionales y NoSQL, etc.
### Inyección de dependencias { #dependency-injection }
FastAPI incluye un sistema de Inyección de Dependencias extremadamente fácil de usar, pero extremadamente potente.
* Incluso las dependencias pueden tener dependencias, creando una jerarquía o **"grafo de dependencias"**.
* Todo **manejado automáticamente** por el framework.
* Todas las dependencias pueden requerir datos de los requests y **aumentar las restricciones de la path operation** y la documentación automática.
* **Validación automática** incluso para los parámetros de *path operation* definidos en las dependencias.
* Soporte para sistemas de autenticación de usuario complejos, **conexiones a bases de datos**, etc.
* **Sin compromisos** con bases de datos, frontends, etc. Pero fácil integración con todos ellos.
### "Plug-ins" ilimitados { #unlimited-plug-ins }
O de otra manera, no hay necesidad de ellos, importa y usa el código que necesitas.
Cualquier integración está diseñada para ser tan simple de usar (con dependencias) que puedes crear un "plug-in" para tu aplicación en 2 líneas de código usando la misma estructura y sintaxis utilizada para tus *path operations*.
### Probado { #tested }
* 100% de cobertura de tests.
* 100% anotada con tipos code base.
* Usado en aplicaciones en producción.
## Funcionalidades de Starlette { #starlette-features }
**FastAPI** es totalmente compatible con (y está basado en) [**Starlette**](https://www.starlette.dev/). Así que, cualquier código adicional de Starlette que tengas, también funcionará.
`FastAPI` es en realidad una subclase de `Starlette`. Así que, si ya conoces o usas Starlette, la mayoría de las funcionalidades funcionarán de la misma manera.
Con **FastAPI** obtienes todas las funcionalidades de **Starlette** (ya que FastAPI es simplemente Starlette potenciado):
* Rendimiento seriamente impresionante. Es [uno de los frameworks de Python más rápidos disponibles, a la par de **NodeJS** y **Go**](https://github.com/encode/starlette#performance).
* Soporte para **WebSocket**.
* Tareas en segundo plano en el mismo proceso.
* Eventos de inicio y apagado.
* Cliente de prueba basado en HTTPX.
* **CORS**, GZip, archivos estáticos, responses en streaming.
* Soporte para **Session y Cookie**.
* Cobertura de tests del 100%.
* code base 100% anotada con tipos.
## Funcionalidades de Pydantic { #pydantic-features }
**FastAPI** es totalmente compatible con (y está basado en) [**Pydantic**](https://docs.pydantic.dev/). Por lo tanto, cualquier código adicional de Pydantic que tengas, también funcionará.
Incluyendo paquetes externos también basados en Pydantic, como ORMs, ODMs para bases de datos.
Esto también significa que, en muchos casos, puedes pasar el mismo objeto que obtienes de un request **directamente a la base de datos**, ya que todo se valida automáticamente.
Lo mismo aplica al revés, en muchos casos puedes simplemente pasar el objeto que obtienes de la base de datos **directamente al cliente**.
Con **FastAPI** obtienes todas las funcionalidades de **Pydantic** (ya que FastAPI está basado en Pydantic para todo el manejo de datos):
* **Sin complicaciones**:
* Sin micro-lenguaje de definición de esquemas nuevo que aprender.
* Si conoces los tipos en Python sabes cómo usar Pydantic.
* Se lleva bien con tu **IDE/linter/cerebro**:
* Porque las estructuras de datos de pydantic son solo instances de clases que defines; autocompletado, linting, mypy y tu intuición deberían funcionar correctamente con tus datos validados.
* Valida **estructuras complejas**:
* Uso de modelos jerárquicos de Pydantic, `List` y `Dict` de `typing` de Python, etc.
* Y los validadores permiten definir, verificar y documentar de manera clara y fácil esquemas de datos complejos como JSON Schema.
* Puedes tener objetos JSON profundamente **anidados** y validarlos todos y anotarlos.
* **Extensible**:
* Pydantic permite definir tipos de datos personalizados o puedes extender la validación con métodos en un modelo decorados con el decorador validator.
* Cobertura de tests del 100%.
================================================
FILE: docs/es/docs/help-fastapi.md
================================================
# Ayuda a FastAPI - Consigue Ayuda { #help-fastapi-get-help }
¿Te gusta **FastAPI**?
¿Te gustaría ayudar a FastAPI, a otros usuarios y al autor?
¿O te gustaría conseguir ayuda con **FastAPI**?
Hay formas muy sencillas de ayudar (varias implican solo uno o dos clics).
Y también hay varias formas de conseguir ayuda.
## Suscríbete al boletín { #subscribe-to-the-newsletter }
Puedes suscribirte al (esporádico) [boletín **FastAPI and friends**](newsletter.md) para mantenerte al día sobre:
* Noticias sobre FastAPI y amigos 🚀
* Guías 📝
* Funcionalidades ✨
* Cambios importantes 🚨
* Consejos y trucos ✅
## Sigue a FastAPI en X (Twitter) { #follow-fastapi-on-x-twitter }
[Sigue a @fastapi en **X (Twitter)**](https://x.com/fastapi) para obtener las últimas noticias sobre **FastAPI**. 🐦
## Dale una estrella a **FastAPI** en GitHub { #star-fastapi-in-github }
Puedes "darle una estrella" a FastAPI en GitHub (haciendo clic en el botón de estrella en la parte superior derecha): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Al agregar una estrella, otros usuarios podrán encontrarlo más fácilmente y ver que ya ha sido útil para otros.
## Observa el repositorio de GitHub para lanzamientos { #watch-the-github-repository-for-releases }
Puedes "observar" FastAPI en GitHub (haciendo clic en el botón "watch" en la parte superior derecha): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Allí puedes seleccionar "Releases only".
Al hacerlo, recibirás notificaciones (en tu email) cada vez que haya un nuevo lanzamiento (una nueva versión) de **FastAPI** con correcciones de bugs y nuevas funcionalidades.
## Conéctate con el autor { #connect-with-the-author }
Puedes conectar [conmigo (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), el autor.
Puedes:
* [Seguirme en **GitHub**](https://github.com/tiangolo).
* Ver otros proyectos de Código Abierto que he creado y que podrían ayudarte.
* Seguirme para ver cuándo creo un nuevo proyecto de Código Abierto.
* [Seguirme en **X (Twitter)**](https://x.com/tiangolo) o [Mastodon](https://fosstodon.org/@tiangolo).
* Contarme cómo usas FastAPI (me encanta oír eso).
* Enterarte cuando hago anuncios o lanzo nuevas herramientas.
* También puedes [seguir @fastapi en X (Twitter)](https://x.com/fastapi) (una cuenta aparte).
* [Seguirme en **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Enterarte cuando hago anuncios o lanzo nuevas herramientas (aunque uso X (Twitter) más a menudo 🤷♂).
* Leer lo que escribo (o seguirme) en [**Dev.to**](https://dev.to/tiangolo) o [**Medium**](https://medium.com/@tiangolo).
* Leer otras ideas, artículos, y leer sobre las herramientas que he creado.
* Seguirme para leer lo que publico nuevo.
## Twittea sobre **FastAPI** { #tweet-about-fastapi }
[Twittea sobre **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) y dime a mí y a otros por qué te gusta. 🎉
Me encanta escuchar cómo se está utilizando **FastAPI**, qué te ha gustado, en qué proyecto/empresa lo estás usando, etc.
## Vota por FastAPI { #vote-for-fastapi }
* [Vota por **FastAPI** en Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Vota por **FastAPI** en AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Di que usas **FastAPI** en StackShare](https://stackshare.io/pypi-fastapi).
## Ayuda a otros con preguntas en GitHub { #help-others-with-questions-in-github }
Puedes intentar ayudar a otros con sus preguntas en:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
En muchos casos, probablemente ya conozcas la respuesta a esas preguntas. 🤓
Si estás ayudando mucho a la gente con sus preguntas, te convertirás en un [FastAPI Expert](fastapi-people.md#fastapi-experts) oficial. 🎉
Solo recuerda, el punto más importante es: trata de ser amable. La gente llega con sus frustraciones y, en muchos casos, no pregunta de la mejor manera, pero haz todo lo posible por ser amable. 🤗
La idea es que la comunidad de **FastAPI** sea amable y acogedora. Al mismo tiempo, no aceptes acoso o comportamiento irrespetuoso hacia los demás. Tenemos que cuidarnos unos a otros.
---
Aquí te explico cómo ayudar a otros con preguntas (en discusiones o issues):
### Entiende la pregunta { #understand-the-question }
* Revisa si puedes entender cuál es el **propósito** y el caso de uso de la persona que pregunta.
* Luego revisa si la pregunta (la gran mayoría son preguntas) es **clara**.
* En muchos casos, la pregunta planteada es sobre una solución imaginaria del usuario, pero podría haber una **mejor**. Si puedes entender mejor el problema y el caso de uso, podrías sugerir una mejor **solución alternativa**.
* Si no puedes entender la pregunta, pide más **detalles**.
### Reproduce el problema { #reproduce-the-problem }
En la mayoría de los casos y preguntas hay algo relacionado con el **código original** de la persona.
En muchos casos solo copiarán un fragmento del código, pero eso no es suficiente para **reproducir el problema**.
* Puedes pedirles que proporcionen un [ejemplo mínimo, reproducible](https://stackoverflow.com/help/minimal-reproducible-example), que puedas **copiar-pegar** y ejecutar localmente para ver el mismo error o comportamiento que están viendo, o para entender mejor su caso de uso.
* Si te sientes muy generoso, puedes intentar **crear un ejemplo** así tú mismo, solo basado en la descripción del problema. Solo ten en cuenta que esto podría llevar mucho tiempo y podría ser mejor pedirles que aclaren el problema primero.
### Sugerir soluciones { #suggest-solutions }
* Después de poder entender la pregunta, puedes darles una posible **respuesta**.
* En muchos casos, es mejor entender su **problema subyacente o caso de uso**, porque podría haber una mejor manera de resolverlo que lo que están intentando hacer.
### Pide cerrar { #ask-to-close }
Si responden, hay una alta probabilidad de que hayas resuelto su problema, felicidades, ¡**eres un héroe**! 🦸
* Ahora, si eso resolvió su problema, puedes pedirles que:
* En GitHub Discussions: marquen el comentario como la **respuesta**.
* En GitHub Issues: **cierren** el issue.
## Observa el repositorio de GitHub { #watch-the-github-repository }
Puedes "observar" FastAPI en GitHub (haciendo clic en el botón "watch" en la parte superior derecha): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Si seleccionas "Watching" en lugar de "Releases only", recibirás notificaciones cuando alguien cree un nuevo issue o pregunta. También puedes especificar que solo deseas que te notifiquen sobre nuevos issues, discusiones, PRs, etc.
Luego puedes intentar ayudarlos a resolver esas preguntas.
## Haz preguntas { #ask-questions }
Puedes [crear una nueva pregunta](https://github.com/fastapi/fastapi/discussions/new?category=questions) en el repositorio de GitHub, por ejemplo, para:
* Hacer una **pregunta** o preguntar sobre un **problema**.
* Sugerir una nueva **funcionalidad**.
**Nota**: si lo haces, entonces te voy a pedir que también ayudes a otros. 😉
## Revisa Pull Requests { #review-pull-requests }
Puedes ayudarme a revisar pull requests de otros.
De nuevo, por favor, haz tu mejor esfuerzo por ser amable. 🤗
---
Aquí está lo que debes tener en cuenta y cómo revisar un pull request:
### Entiende el problema { #understand-the-problem }
* Primero, asegúrate de **entender el problema** que el pull request está intentando resolver. Podría tener una discusión más larga en una GitHub Discussion o issue.
* También hay una buena posibilidad de que el pull request no sea realmente necesario porque el problema se puede resolver de una manera **diferente**. Entonces puedes sugerir o preguntar sobre eso.
### No te preocupes por el estilo { #dont-worry-about-style }
* No te preocupes demasiado por cosas como los estilos de los mensajes de commit, yo haré squash y merge personalizando el commit manualmente.
* Tampoco te preocupes por las reglas de estilo, hay herramientas automatizadas verificando eso.
Y si hay alguna otra necesidad de estilo o consistencia, pediré directamente eso, o agregaré commits encima con los cambios necesarios.
### Revisa el código { #check-the-code }
* Revisa y lee el código, ve si tiene sentido, **ejecútalo localmente** y ve si realmente resuelve el problema.
* Luego **comenta** diciendo que hiciste eso, así sabré que realmente lo revisaste.
/// info | Información
Desafortunadamente, no puedo simplemente confiar en PRs que solo tienen varias aprobaciones.
Varias veces ha sucedido que hay PRs con 3, 5 o más aprobaciones, probablemente porque la descripción es atractiva, pero cuando reviso los PRs, en realidad están rotos, tienen un bug, o no resuelven el problema que dicen resolver. 😅
Así que, es realmente importante que realmente leas y ejecutes el código, y me hagas saber en los comentarios que lo hiciste. 🤓
///
* Si el PR se puede simplificar de alguna manera, puedes pedir eso, pero no hay necesidad de ser demasiado exigente, podría haber muchos puntos de vista subjetivos (y yo tendré el mío también 🙈), así que es mejor si puedes centrarte en las cosas fundamentales.
### Tests { #tests }
* Ayúdame a verificar que el PR tenga **tests**.
* Verifica que los tests **fallen** antes del PR. 🚨
* Luego verifica que los tests **pasen** después del PR. ✅
* Muchos PRs no tienen tests, puedes **recordarles** que agreguen tests, o incluso puedes **sugerir** algunos tests tú mismo. Eso es una de las cosas que consume más tiempo y puedes ayudar mucho con eso.
* Luego también comenta lo que intentaste, de esa manera sabré que lo revisaste. 🤓
## Crea un Pull Request { #create-a-pull-request }
Puedes [contribuir](contributing.md) al código fuente con Pull Requests, por ejemplo:
* Para corregir un error tipográfico que encontraste en la documentación.
* Para compartir un artículo, video o podcast que creaste o encontraste sobre FastAPI [editando este archivo](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Asegúrate de agregar tu enlace al inicio de la sección correspondiente.
* Para ayudar a [traducir la documentación](contributing.md#translations) a tu idioma.
* También puedes ayudar a revisar las traducciones creadas por otros.
* Para proponer nuevas secciones de documentación.
* Para corregir un issue/bug existente.
* Asegúrate de agregar tests.
* Para agregar una nueva funcionalidad.
* Asegúrate de agregar tests.
* Asegúrate de agregar documentación si es relevante.
## Ayuda a Mantener FastAPI { #help-maintain-fastapi }
¡Ayúdame a mantener **FastAPI**! 🤓
Hay mucho trabajo por hacer, y para la mayoría de ello, **TÚ** puedes hacerlo.
Las tareas principales que puedes hacer ahora son:
* [Ayudar a otros con preguntas en GitHub](#help-others-with-questions-in-github) (ver la sección arriba).
* [Revisar Pull Requests](#review-pull-requests) (ver la sección arriba).
Esas dos tareas son las que **consumen más tiempo**. Ese es el trabajo principal de mantener FastAPI.
Si puedes ayudarme con eso, **me estás ayudando a mantener FastAPI** y asegurando que siga **avanzando más rápido y mejor**. 🚀
## Únete al chat { #join-the-chat }
Únete al servidor de chat 👥 [Discord](https://discord.gg/VQjSZaeJmf) 👥 y charla con otros en la comunidad de FastAPI.
/// tip | Consejo
Para preguntas, házlas en [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), hay muchas más probabilidades de que recibas ayuda de parte de los [FastAPI Experts](fastapi-people.md#fastapi-experts).
Usa el chat solo para otras conversaciones generales.
///
### No uses el chat para preguntas { #dont-use-the-chat-for-questions }
Ten en cuenta que dado que los chats permiten una "conversación más libre", es fácil hacer preguntas que son demasiado generales y más difíciles de responder, por lo que es posible que no recibas respuestas.
En GitHub, la plantilla te guiará para escribir la pregunta correcta para que puedas obtener más fácilmente una buena respuesta, o incluso resolver el problema por ti mismo antes de preguntar. Y en GitHub puedo asegurarme de responder siempre todo, incluso si lleva tiempo. No puedo hacer eso personalmente con los sistemas de chat. 😅
Las conversaciones en los sistemas de chat tampoco son tan fácilmente buscables como en GitHub, por lo que las preguntas y respuestas podrían perderse en la conversación. Y solo las que están en GitHub cuentan para convertirse en un [FastAPI Expert](fastapi-people.md#fastapi-experts), por lo que probablemente recibirás más atención en GitHub.
Por otro lado, hay miles de usuarios en los sistemas de chat, por lo que hay muchas posibilidades de que encuentres a alguien con quien hablar allí, casi todo el tiempo. 😄
## Hazte sponsor del autor { #sponsor-the-author }
Si tu **producto/empresa** depende de o está relacionado con **FastAPI** y quieres llegar a sus usuarios, puedes hacerte sponsor del autor (de mí) a través de [GitHub sponsors](https://github.com/sponsors/tiangolo). Según el nivel, podrías obtener algunos beneficios extra, como una insignia en la documentación. 🎁
---
¡Gracias! 🚀
================================================
FILE: docs/es/docs/history-design-future.md
================================================
# Historia, Diseño y Futuro { #history-design-and-future }
Hace algún tiempo, [un usuario de **FastAPI** preguntó](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> ¿Cuál es la historia de este proyecto? Parece haber surgido de la nada y ser increíble en pocas semanas [...]
Aquí hay un poquito de esa historia.
## Alternativas { #alternatives }
He estado creando APIs con requisitos complejos durante varios años (Machine Learning, sistemas distribuidos, trabajos asíncronos, bases de datos NoSQL, etc.), liderando varios equipos de desarrolladores.
Como parte de eso, necesitaba investigar, probar y usar muchas alternativas.
La historia de **FastAPI** es en gran parte la historia de sus predecesores.
Como se dice en la sección [Alternativas](alternatives.md):
**FastAPI** no existiría si no fuera por el trabajo previo de otros.
Ha habido muchas herramientas creadas antes que han ayudado a inspirar su creación.
He estado evitando la creación de un nuevo framework durante varios años. Primero traté de resolver todas las funcionalidades cubiertas por **FastAPI** usando varios frameworks, plug-ins y herramientas diferentes.
Pero en algún momento, no había otra opción que crear algo que proporcionara todas estas funcionalidades, tomando las mejores ideas de herramientas anteriores y combinándolas de la mejor manera posible, usando funcionalidades del lenguaje que ni siquiera estaban disponibles antes (anotaciones de tipos de Python 3.6+).
## Investigación { #investigation }
Al usar todas las alternativas anteriores, tuve la oportunidad de aprender de todas ellas, tomar ideas y combinarlas de la mejor manera que pude encontrar para mí y los equipos de desarrolladores con los que he trabajado.
Por ejemplo, estaba claro que idealmente debería estar basado en las anotaciones de tipos estándar de Python.
También, el mejor enfoque era usar estándares ya existentes.
Entonces, antes de siquiera empezar a programar **FastAPI**, pasé varios meses estudiando las especificaciones de OpenAPI, JSON Schema, OAuth2, etc. Entendiendo su relación, superposición y diferencias.
## Diseño { #design }
Luego pasé algún tiempo diseñando la "API" de desarrollador que quería tener como usuario (como desarrollador usando FastAPI).
Probé varias ideas en los editores de Python más populares: PyCharm, VS Code, editores basados en Jedi.
Según la última [Encuesta de Desarrolladores de Python](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), estos editores cubren alrededor del 80% de los usuarios.
Esto significa que **FastAPI** fue específicamente probado con los editores usados por el 80% de los desarrolladores de Python. Y como la mayoría de los otros editores tienden a funcionar de manera similar, todos sus beneficios deberían funcionar prácticamente para todos los editores.
De esa manera, pude encontrar las mejores maneras de reducir la duplicación de código tanto como fuera posible, para tener autocompletado en todas partes, chequeos de tipos y errores, etc.
Todo de una manera que proporcionara la mejor experiencia de desarrollo para todos los desarrolladores.
## Requisitos { #requirements }
Después de probar varias alternativas, decidí que iba a usar [**Pydantic**](https://docs.pydantic.dev/) por sus ventajas.
Luego contribuí a este, para hacerlo totalmente compatible con JSON Schema, para soportar diferentes maneras de definir declaraciones de restricciones, y para mejorar el soporte de los editores (chequeo de tipos, autocompletado) basado en las pruebas en varios editores.
Durante el desarrollo, también contribuí a [**Starlette**](https://www.starlette.dev/), el otro requisito clave.
## Desarrollo { #development }
Para cuando comencé a crear el propio **FastAPI**, la mayoría de las piezas ya estaban en su lugar, el diseño estaba definido, los requisitos y herramientas estaban listos, y el conocimiento sobre los estándares y especificaciones estaba claro y fresco.
## Futuro { #future }
A este punto, ya está claro que **FastAPI** con sus ideas está siendo útil para muchas personas.
Está siendo elegido sobre alternativas anteriores por adaptarse mejor a muchos casos de uso.
Muchos desarrolladores y equipos ya dependen de **FastAPI** para sus proyectos (incluyéndome a mí y a mi equipo).
Pero aún así, hay muchas mejoras y funcionalidades por venir.
**FastAPI** tiene un gran futuro por delante.
Y [tu ayuda](help-fastapi.md) es muy apreciada.
================================================
FILE: docs/es/docs/how-to/authentication-error-status-code.md
================================================
# Usar los códigos de estado antiguos 403 para errores de autenticación { #use-old-403-authentication-error-status-codes }
Antes de FastAPI versión `0.122.0`, cuando las utilidades de seguridad integradas devolvían un error al cliente después de una autenticación fallida, usaban el código de estado HTTP `403 Forbidden`.
A partir de FastAPI versión `0.122.0`, usan el código de estado HTTP `401 Unauthorized`, más apropiado, y devuelven un `WWW-Authenticate` header adecuado en la response, siguiendo las especificaciones HTTP, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Pero si por alguna razón tus clientes dependen del comportamiento anterior, puedes volver a él sobrescribiendo el método `make_not_authenticated_error` en tus clases de seguridad.
Por ejemplo, puedes crear una subclase de `HTTPBearer` que devuelva un error `403 Forbidden` en lugar del `401 Unauthorized` por defecto:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Consejo
Ten en cuenta que la función devuelve la instance de la excepción, no la lanza. El lanzamiento se hace en el resto del código interno.
///
================================================
FILE: docs/es/docs/how-to/conditional-openapi.md
================================================
# OpenAPI condicional { #conditional-openapi }
Si lo necesitaras, podrías usar configuraciones y variables de entorno para configurar OpenAPI condicionalmente según el entorno, e incluso desactivarlo por completo.
## Sobre seguridad, APIs y documentación { #about-security-apis-and-docs }
Ocultar las interfaces de usuario de la documentación en producción *no debería* ser la forma de proteger tu API.
Eso no añade ninguna seguridad extra a tu API, las *path operations* seguirán estando disponibles donde están.
Si hay una falla de seguridad en tu código, seguirá existiendo.
Ocultar la documentación solo hace que sea más difícil entender cómo interactuar con tu API y podría dificultar más depurarla en producción. Podría considerarse simplemente una forma de [Seguridad mediante oscuridad](https://en.wikipedia.org/wiki/Security_through_obscurity).
Si quieres asegurar tu API, hay varias cosas mejores que puedes hacer, por ejemplo:
* Asegúrate de tener modelos Pydantic bien definidos para tus request bodies y responses.
* Configura los permisos y roles necesarios usando dependencias.
* Nunca guardes contraseñas en texto plano, solo hashes de contraseñas.
* Implementa y utiliza herramientas criptográficas bien conocidas, como pwdlib y JWT tokens, etc.
* Añade controles de permisos más detallados con Scopes de OAuth2 donde sea necesario.
* ...etc.
No obstante, podrías tener un caso de uso muy específico donde realmente necesites desactivar la documentación de la API para algún entorno (por ejemplo, para producción) o dependiendo de configuraciones de variables de entorno.
## OpenAPI condicional desde configuraciones y variables de entorno { #conditional-openapi-from-settings-and-env-vars }
Puedes usar fácilmente las mismas configuraciones de Pydantic para configurar tu OpenAPI generado y las interfaces de usuario de la documentación.
Por ejemplo:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Aquí declaramos la configuración `openapi_url` con el mismo valor por defecto de `"/openapi.json"`.
Y luego la usamos al crear la app de `FastAPI`.
Entonces podrías desactivar OpenAPI (incluyendo las UI de documentación) configurando la variable de entorno `OPENAPI_URL` a una string vacía, así:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Luego, si vas a las URLs en `/openapi.json`, `/docs`, o `/redoc`, solo obtendrás un error `404 Not Found` como:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/es/docs/how-to/configure-swagger-ui.md
================================================
# Configurar Swagger UI { #configure-swagger-ui }
Puedes configurar algunos [parámetros adicionales de Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
Para configurarlos, pasa el argumento `swagger_ui_parameters` al crear el objeto de la app `FastAPI()` o a la función `get_swagger_ui_html()`.
`swagger_ui_parameters` recibe un diccionario con las configuraciones pasadas directamente a Swagger UI.
FastAPI convierte las configuraciones a **JSON** para hacerlas compatibles con JavaScript, ya que eso es lo que Swagger UI necesita.
## Desactivar el resaltado de sintaxis { #disable-syntax-highlighting }
Por ejemplo, podrías desactivar el resaltado de sintaxis en Swagger UI.
Sin cambiar la configuración, el resaltado de sintaxis está activado por defecto:
Pero puedes desactivarlo estableciendo `syntaxHighlight` en `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...y entonces Swagger UI ya no mostrará el resaltado de sintaxis:
## Cambiar el tema { #change-the-theme }
De la misma manera, podrías configurar el tema del resaltado de sintaxis con la clave `"syntaxHighlight.theme"` (ten en cuenta que tiene un punto en el medio):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Esa configuración cambiaría el tema de color del resaltado de sintaxis:
## Cambiar los parámetros por defecto de Swagger UI { #change-default-swagger-ui-parameters }
FastAPI incluye algunos parámetros de configuración por defecto apropiados para la mayoría de los casos de uso.
Incluye estas configuraciones por defecto:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Puedes sobrescribir cualquiera de ellos estableciendo un valor diferente en el argumento `swagger_ui_parameters`.
Por ejemplo, para desactivar `deepLinking` podrías pasar estas configuraciones a `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Otros parámetros de Swagger UI { #other-swagger-ui-parameters }
Para ver todas las demás configuraciones posibles que puedes usar, lee la [documentación oficial de los parámetros de Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Configuraciones solo de JavaScript { #javascript-only-settings }
Swagger UI también permite otras configuraciones que son objetos **solo de JavaScript** (por ejemplo, funciones de JavaScript).
FastAPI también incluye estas configuraciones `presets` solo de JavaScript:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Estos son objetos de **JavaScript**, no strings, por lo que no puedes pasarlos directamente desde código de Python.
Si necesitas usar configuraciones solo de JavaScript como esas, puedes usar uno de los métodos anteriores. Sobrescribe toda la *path operation* de Swagger UI y escribe manualmente cualquier JavaScript que necesites.
================================================
FILE: docs/es/docs/how-to/custom-docs-ui-assets.md
================================================
# Recursos Estáticos Personalizados para la Docs UI (self hosting) { #custom-docs-ui-static-assets-self-hosting }
La documentación de la API utiliza **Swagger UI** y **ReDoc**, y cada uno de estos necesita algunos archivos JavaScript y CSS.
Por defecto, esos archivos se sirven desde un CDN.
Pero es posible personalizarlo, puedes establecer un CDN específico, o servir los archivos tú mismo.
## CDN Personalizado para JavaScript y CSS { #custom-cdn-for-javascript-and-css }
Digamos que quieres usar un CDN diferente, por ejemplo, quieres usar `https://unpkg.com/`.
Esto podría ser útil si, por ejemplo, vives en un país que restringe algunas URLs.
### Desactiva la documentación automática { #disable-the-automatic-docs }
El primer paso es desactivar la documentación automática, ya que por defecto, esos usan el CDN por defecto.
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Incluye la documentación personalizada { #include-the-custom-docs }
Ahora puedes crear las *path operations* para la documentación personalizada.
Puedes reutilizar las funciones internas de FastAPI para crear las páginas HTML para la documentación, y pasarles los argumentos necesarios:
* `openapi_url`: la URL donde la página HTML para la documentación puede obtener el OpenAPI esquema de tu API. Puedes usar aquí el atributo `app.openapi_url`.
* `title`: el título de tu API.
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor por defecto.
* `swagger_js_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **JavaScript**. Esta es la URL personalizada del CDN.
* `swagger_css_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **CSS**. Esta es la URL personalizada del CDN.
Y de manera similar para ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Consejo
La *path operation* para `swagger_ui_redirect` es una herramienta cuando utilizas OAuth2.
Si integras tu API con un proveedor OAuth2, podrás autenticarte y regresar a la documentación de la API con las credenciales adquiridas. E interactuar con ella usando la autenticación real de OAuth2.
Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar de "redirección".
///
### Crea una *path operation* para probarlo { #create-a-path-operation-to-test-it }
Ahora, para poder probar que todo funciona, crea una *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Pruébalo { #test-it }
Ahora, deberías poder ir a tu documentación en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), y recargar la página, cargará esos recursos desde el nuevo CDN.
## self hosting de JavaScript y CSS para la documentación { #self-hosting-javascript-and-css-for-docs }
El self hosting de JavaScript y CSS podría ser útil si, por ejemplo, necesitas que tu aplicación siga funcionando incluso offline, sin acceso a Internet, o en una red local.
Aquí verás cómo servir esos archivos tú mismo, en la misma aplicación de FastAPI, y configurar la documentación para usarla.
### Estructura de archivos del proyecto { #project-file-structure }
Supongamos que la estructura de archivos de tu proyecto se ve así:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Ahora crea un directorio para almacenar esos archivos estáticos.
Tu nueva estructura de archivos podría verse así:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Descarga los archivos { #download-the-files }
Descarga los archivos estáticos necesarios para la documentación y ponlos en ese directorio `static/`.
Probablemente puedas hacer clic derecho en cada enlace y seleccionar una opción similar a `Guardar enlace como...`.
**Swagger UI** utiliza los archivos:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
Y **ReDoc** utiliza el archivo:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Después de eso, tu estructura de archivos podría verse así:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Sirve los archivos estáticos { #serve-the-static-files }
* Importa `StaticFiles`.
* "Monta" una instance de `StaticFiles()` en un path específico.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Prueba los archivos estáticos { #test-the-static-files }
Inicia tu aplicación y ve a [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Deberías ver un archivo JavaScript muy largo de **ReDoc**.
Podría comenzar con algo como:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Eso confirma que puedes servir archivos estáticos desde tu aplicación, y que colocaste los archivos estáticos para la documentación en el lugar correcto.
Ahora podemos configurar la aplicación para usar esos archivos estáticos para la documentación.
### Desactiva la documentación automática para archivos estáticos { #disable-the-automatic-docs-for-static-files }
Igual que cuando usas un CDN personalizado, el primer paso es desactivar la documentación automática, ya que esos usan el CDN por defecto.
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Incluye la documentación personalizada para archivos estáticos { #include-the-custom-docs-for-static-files }
Y de la misma manera que con un CDN personalizado, ahora puedes crear las *path operations* para la documentación personalizada.
Nuevamente, puedes reutilizar las funciones internas de FastAPI para crear las páginas HTML para la documentación, y pasarles los argumentos necesarios:
* `openapi_url`: la URL donde la página HTML para la documentación puede obtener el OpenAPI esquema de tu API. Puedes usar aquí el atributo `app.openapi_url`.
* `title`: el título de tu API.
* `oauth2_redirect_url`: puedes usar `app.swagger_ui_oauth2_redirect_url` aquí para usar el valor por defecto.
* `swagger_js_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **JavaScript**. **Este es el que tu propia aplicación está sirviendo ahora**.
* `swagger_css_url`: la URL donde el HTML para tu documentación de Swagger UI puede obtener el archivo **CSS**. **Este es el que tu propia aplicación está sirviendo ahora**.
Y de manera similar para ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Consejo
La *path operation* para `swagger_ui_redirect` es una herramienta cuando utilizas OAuth2.
Si integras tu API con un proveedor OAuth2, podrás autenticarte y regresar a la documentación de la API con las credenciales adquiridas. Y interactuar con ella usando la autenticación real de OAuth2.
Swagger UI lo manejará detrás de escena para ti, pero necesita este auxiliar de "redirección".
///
### Crea una *path operation* para probar archivos estáticos { #create-a-path-operation-to-test-static-files }
Ahora, para poder probar que todo funciona, crea una *path operation*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Prueba la UI de Archivos Estáticos { #test-static-files-ui }
Ahora, deberías poder desconectar tu WiFi, ir a tu documentación en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), y recargar la página.
E incluso sin Internet, podrás ver la documentación de tu API e interactuar con ella.
================================================
FILE: docs/es/docs/how-to/custom-request-and-route.md
================================================
# Clase personalizada de Request y APIRoute { #custom-request-and-apiroute-class }
En algunos casos, puede que quieras sobrescribir la lógica utilizada por las clases `Request` y `APIRoute`.
En particular, esta puede ser una buena alternativa a la lógica en un middleware.
Por ejemplo, si quieres leer o manipular el request body antes de que sea procesado por tu aplicación.
/// danger | Advertencia
Esta es una funcionalidad "avanzada".
Si apenas estás comenzando con **FastAPI**, quizás quieras saltar esta sección.
///
## Casos de uso { #use-cases }
Algunos casos de uso incluyen:
* Convertir cuerpos de requests no-JSON a JSON (por ejemplo, [`msgpack`](https://msgpack.org/index.html)).
* Descomprimir cuerpos de requests comprimidos con gzip.
* Registrar automáticamente todos los request bodies.
## Manejo de codificaciones personalizadas de request body { #handling-custom-request-body-encodings }
Veamos cómo hacer uso de una subclase personalizada de `Request` para descomprimir requests gzip.
Y una subclase de `APIRoute` para usar esa clase de request personalizada.
### Crear una clase personalizada `GzipRequest` { #create-a-custom-gziprequest-class }
/// tip | Consejo
Este es un ejemplo sencillo para demostrar cómo funciona. Si necesitas soporte para Gzip, puedes usar el [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) proporcionado.
///
Primero, creamos una clase `GzipRequest`, que sobrescribirá el método `Request.body()` para descomprimir el request body si hay un header apropiado.
Si no hay `gzip` en el header, no intentará descomprimir el request body.
De esa manera, la misma clase de ruta puede manejar requests comprimidos con gzip o no comprimidos.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Crear una clase personalizada `GzipRoute` { #create-a-custom-gziproute-class }
A continuación, creamos una subclase personalizada de `fastapi.routing.APIRoute` que hará uso de `GzipRequest`.
Esta vez, sobrescribirá el método `APIRoute.get_route_handler()`.
Este método devuelve una función. Y esa función es la que recibirá un request y devolverá un response.
Aquí lo usamos para crear un `GzipRequest` a partir del request original.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Detalles técnicos
Un `Request` tiene un atributo `request.scope`, que es simplemente un `dict` de Python que contiene los metadatos relacionados con el request.
Un `Request` también tiene un `request.receive`, que es una función para "recibir" el request body.
El `dict` `scope` y la función `receive` son ambos parte de la especificación ASGI.
Y esas dos cosas, `scope` y `receive`, son lo que se necesita para crear una nueva *Request instance*.
Para aprender más sobre el `Request`, revisa [la documentación de Starlette sobre Requests](https://www.starlette.dev/requests/).
///
La única cosa que la función devuelta por `GzipRequest.get_route_handler` hace diferente es convertir el `Request` en un `GzipRequest`.
Haciendo esto, nuestro `GzipRequest` se encargará de descomprimir los datos (si es necesario) antes de pasarlos a nuestras *path operations*.
Después de eso, toda la lógica de procesamiento es la misma.
Pero debido a nuestros cambios en `GzipRequest.body`, el request body se descomprimirá automáticamente cuando sea cargado por **FastAPI** si es necesario.
## Accediendo al request body en un manejador de excepciones { #accessing-the-request-body-in-an-exception-handler }
/// tip | Consejo
Para resolver este mismo problema, probablemente sea mucho más fácil usar el `body` en un manejador personalizado para `RequestValidationError` ([Manejo de Errores](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Pero este ejemplo sigue siendo válido y muestra cómo interactuar con los componentes internos.
///
También podemos usar este mismo enfoque para acceder al request body en un manejador de excepciones.
Todo lo que necesitamos hacer es manejar el request dentro de un bloque `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Si ocurre una excepción, la `Request instance` aún estará en el alcance, así que podemos leer y hacer uso del request body cuando manejamos el error:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Clase personalizada `APIRoute` en un router { #custom-apiroute-class-in-a-router }
También puedes establecer el parámetro `route_class` de un `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
En este ejemplo, las *path operations* bajo el `router` usarán la clase personalizada `TimedRoute`, y tendrán un header `X-Response-Time` extra en el response con el tiempo que tomó generar el response:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/es/docs/how-to/extending-openapi.md
================================================
# Extender OpenAPI { #extending-openapi }
Hay algunos casos en los que podrías necesitar modificar el esquema de OpenAPI generado.
En esta sección verás cómo hacerlo.
## El proceso normal { #the-normal-process }
El proceso normal (por defecto) es el siguiente.
Una aplicación (instance) de `FastAPI` tiene un método `.openapi()` que se espera que devuelva el esquema de OpenAPI.
Como parte de la creación del objeto de la aplicación, se registra una *path operation* para `/openapi.json` (o para lo que sea que configures tu `openapi_url`).
Simplemente devuelve un response JSON con el resultado del método `.openapi()` de la aplicación.
Por defecto, lo que hace el método `.openapi()` es revisar la propiedad `.openapi_schema` para ver si tiene contenido y devolverlo.
Si no lo tiene, lo genera usando la función de utilidad en `fastapi.openapi.utils.get_openapi`.
Y esa función `get_openapi()` recibe como parámetros:
* `title`: El título de OpenAPI, mostrado en la documentación.
* `version`: La versión de tu API, por ejemplo `2.5.0`.
* `openapi_version`: La versión de la especificación OpenAPI utilizada. Por defecto, la más reciente: `3.1.0`.
* `summary`: Un breve resumen de la API.
* `description`: La descripción de tu API, esta puede incluir markdown y se mostrará en la documentación.
* `routes`: Una list de rutas, estas son cada una de las *path operations* registradas. Se toman de `app.routes`.
/// info | Información
El parámetro `summary` está disponible en OpenAPI 3.1.0 y versiones superiores, soportado por FastAPI 0.99.0 y superiores.
///
## Sobrescribir los valores por defecto { #overriding-the-defaults }
Usando la información anterior, puedes usar la misma función de utilidad para generar el esquema de OpenAPI y sobrescribir cada parte que necesites.
Por ejemplo, vamos a añadir [la extensión OpenAPI de ReDoc para incluir un logo personalizado](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### **FastAPI** normal { #normal-fastapi }
Primero, escribe toda tu aplicación **FastAPI** como normalmente:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Generar el esquema de OpenAPI { #generate-the-openapi-schema }
Luego, usa la misma función de utilidad para generar el esquema de OpenAPI, dentro de una función `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modificar el esquema de OpenAPI { #modify-the-openapi-schema }
Ahora puedes añadir la extensión de ReDoc, agregando un `x-logo` personalizado al "objeto" `info` en el esquema de OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Cachear el esquema de OpenAPI { #cache-the-openapi-schema }
Puedes usar la propiedad `.openapi_schema` como un "cache", para almacenar tu esquema generado.
De esa forma, tu aplicación no tendrá que generar el esquema cada vez que un usuario abra la documentación de tu API.
Se generará solo una vez, y luego se usará el mismo esquema cacheado para las siguientes requests.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Sobrescribir el método { #override-the-method }
Ahora puedes reemplazar el método `.openapi()` por tu nueva función.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Revisa { #check-it }
Una vez que vayas a [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) verás que estás usando tu logo personalizado (en este ejemplo, el logo de **FastAPI**):
================================================
FILE: docs/es/docs/how-to/general.md
================================================
# General - Cómo Hacer - Recetas { #general-how-to-recipes }
Aquí tienes varias indicaciones hacia otros lugares en la documentación, para preguntas generales o frecuentes.
## Filtrar Datos - Seguridad { #filter-data-security }
Para asegurarte de que no devuelves más datos de los que deberías, lee la documentación para [Tutorial - Modelo de Response - Tipo de Retorno](../tutorial/response-model.md).
## Optimizar el Rendimiento del Response - Modelo de Response - Tipo de Retorno { #optimize-response-performance-response-model-return-type }
Para optimizar el rendimiento al devolver datos JSON, usa un tipo de retorno o un modelo de Response; de esa manera Pydantic se encargará de la serialización a JSON del lado de Rust, sin pasar por Python. Lee más en la documentación para [Tutorial - Modelo de Response - Tipo de Retorno](../tutorial/response-model.md).
## Etiquetas de Documentación - OpenAPI { #documentation-tags-openapi }
Para agregar etiquetas a tus *path operations*, y agruparlas en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Etiquetas](../tutorial/path-operation-configuration.md#tags).
## Resumen y Descripción de Documentación - OpenAPI { #documentation-summary-and-description-openapi }
Para agregar un resumen y descripción a tus *path operations*, y mostrarlos en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Resumen y Descripción](../tutorial/path-operation-configuration.md#summary-and-description).
## Documentación de Descripción de Response - OpenAPI { #documentation-response-description-openapi }
Para definir la descripción del response, mostrada en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Descripción del Response](../tutorial/path-operation-configuration.md#response-description).
## Documentar la Deprecación de una *Path Operation* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Para deprecar una *path operation*, y mostrarla en la interfaz de usuario de la documentación, lee la documentación para [Tutorial - Configuraciones de Path Operation - Deprecación](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Convertir cualquier Dato a Compatible con JSON { #convert-any-data-to-json-compatible }
Para convertir cualquier dato a compatible con JSON, lee la documentación para [Tutorial - Codificador Compatible con JSON](../tutorial/encoder.md).
## Metadatos OpenAPI - Documentación { #openapi-metadata-docs }
Para agregar metadatos a tu esquema de OpenAPI, incluyendo una licencia, versión, contacto, etc, lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md).
## URL Personalizada de OpenAPI { #openapi-custom-url }
Para personalizar la URL de OpenAPI (o eliminarla), lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md#openapi-url).
## URLs de Documentación de OpenAPI { #openapi-docs-urls }
Para actualizar las URLs usadas para las interfaces de usuario de documentación generadas automáticamente, lee la documentación para [Tutorial - Metadatos y URLs de Documentación](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/es/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Como **FastAPI** se basa en el estándar **ASGI**, es muy fácil integrar cualquier paquete de **GraphQL** que también sea compatible con ASGI.
Puedes combinar las *path operations* normales de FastAPI con GraphQL en la misma aplicación.
/// tip | Consejo
**GraphQL** resuelve algunos casos de uso muy específicos.
Tiene **ventajas** y **desventajas** en comparación con las **APIs web** comunes.
Asegúrate de evaluar si los **beneficios** para tu caso de uso compensan los **inconvenientes**. 🤓
///
## Paquetes de GraphQL { #graphql-libraries }
Aquí algunos de los paquetes de **GraphQL** que tienen soporte **ASGI**. Podrías usarlos con **FastAPI**:
* [Strawberry](https://strawberry.rocks/) 🍓
* Con [documentación para FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* Con [documentación para FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* Con [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) para proporcionar integración con ASGI
* [Graphene](https://graphene-python.org/)
* Con [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL con Strawberry { #graphql-with-strawberry }
Si necesitas o quieres trabajar con **GraphQL**, [**Strawberry**](https://strawberry.rocks/) es el paquete **recomendado** ya que tiene un diseño muy similar al diseño de **FastAPI**, todo basado en **anotaciones de tipos**.
Dependiendo de tu caso de uso, podrías preferir usar un paquete diferente, pero si me preguntas, probablemente te sugeriría probar **Strawberry**.
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Puedes aprender más sobre Strawberry en la [documentación de Strawberry](https://strawberry.rocks/).
Y también la documentación sobre [Strawberry con FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## `GraphQLApp` viejo de Starlette { #older-graphqlapp-from-starlette }
Las versiones anteriores de Starlette incluían una clase `GraphQLApp` para integrar con [Graphene](https://graphene-python.org/).
Fue deprecada de Starlette, pero si tienes código que lo usaba, puedes fácilmente **migrar** a [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), que cubre el mismo caso de uso y tiene una **interfaz casi idéntica**.
/// tip | Consejo
Si necesitas GraphQL, aún te recomendaría revisar [Strawberry](https://strawberry.rocks/), ya que se basa en anotaciones de tipos en lugar de clases y tipos personalizados.
///
## Aprende Más { #learn-more }
Puedes aprender más sobre **GraphQL** en la [documentación oficial de GraphQL](https://graphql.org/).
También puedes leer más sobre cada uno de esos paquetes descritos arriba en sus enlaces.
================================================
FILE: docs/es/docs/how-to/index.md
================================================
# Cómo hacer - Recetas { #how-to-recipes }
Aquí verás diferentes recetas o guías de "cómo hacer" para **varios temas**.
La mayoría de estas ideas serían más o menos **independientes**, y en la mayoría de los casos solo deberías estudiarlas si aplican directamente a **tu proyecto**.
Si algo parece interesante y útil para tu proyecto, adelante y revísalo, pero de lo contrario, probablemente puedas simplemente omitirlas.
/// tip | Consejo
Si quieres **aprender FastAPI** de una manera estructurada (recomendado), ve y lee el [Tutorial - Guía de Usuario](../tutorial/index.md) capítulo por capítulo en su lugar.
///
================================================
FILE: docs/es/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Migra de Pydantic v1 a Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Si tienes una app de FastAPI antigua, podrías estar usando Pydantic versión 1.
FastAPI versión 0.100.0 tenía compatibilidad con Pydantic v1 o v2. Usaba la que tuvieras instalada.
FastAPI versión 0.119.0 introdujo compatibilidad parcial con Pydantic v1 desde dentro de Pydantic v2 (como `pydantic.v1`), para facilitar la migración a v2.
FastAPI 0.126.0 eliminó la compatibilidad con Pydantic v1, aunque siguió soportando `pydantic.v1` por un poquito más de tiempo.
/// warning | Advertencia
El equipo de Pydantic dejó de dar soporte a Pydantic v1 para las versiones más recientes de Python, comenzando con **Python 3.14**.
Esto incluye `pydantic.v1`, que ya no está soportado en Python 3.14 y superiores.
Si quieres usar las funcionalidades más recientes de Python, tendrás que asegurarte de usar Pydantic v2.
///
Si tienes una app de FastAPI antigua con Pydantic v1, aquí te muestro cómo migrarla a Pydantic v2, y las **funcionalidades en FastAPI 0.119.0** para ayudarte con una migración gradual.
## Guía oficial { #official-guide }
Pydantic tiene una [Guía de migración](https://docs.pydantic.dev/latest/migration/) oficial de v1 a v2.
También incluye qué cambió, cómo las validaciones ahora son más correctas y estrictas, posibles consideraciones, etc.
Puedes leerla para entender mejor qué cambió.
## Tests { #tests }
Asegúrate de tener [tests](../tutorial/testing.md) para tu app y de ejecutarlos en integración continua (CI).
Así podrás hacer la actualización y asegurarte de que todo sigue funcionando como esperas.
## `bump-pydantic` { #bump-pydantic }
En muchos casos, cuando usas modelos de Pydantic normales sin personalizaciones, podrás automatizar gran parte del proceso de migración de Pydantic v1 a Pydantic v2.
Puedes usar [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) del mismo equipo de Pydantic.
Esta herramienta te ayudará a cambiar automáticamente la mayor parte del código que necesita cambiarse.
Después de esto, puedes ejecutar los tests y revisa si todo funciona. Si es así, ya terminaste. 😎
## Pydantic v1 en v2 { #pydantic-v1-in-v2 }
Pydantic v2 incluye todo lo de Pydantic v1 como un submódulo `pydantic.v1`. Pero esto ya no está soportado en versiones por encima de Python 3.13.
Esto significa que puedes instalar la versión más reciente de Pydantic v2 e importar y usar los componentes viejos de Pydantic v1 desde este submódulo, como si tuvieras instalado el Pydantic v1 antiguo.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Compatibilidad de FastAPI con Pydantic v1 en v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Desde FastAPI 0.119.0, también hay compatibilidad parcial para Pydantic v1 desde dentro de Pydantic v2, para facilitar la migración a v2.
Así que podrías actualizar Pydantic a la última versión 2 y cambiar los imports para usar el submódulo `pydantic.v1`, y en muchos casos simplemente funcionaría.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Advertencia
Ten en cuenta que, como el equipo de Pydantic ya no da soporte a Pydantic v1 en versiones recientes de Python, empezando por Python 3.14, usar `pydantic.v1` tampoco está soportado en Python 3.14 y superiores.
///
### Pydantic v1 y v2 en la misma app { #pydantic-v1-and-v2-on-the-same-app }
**No está soportado** por Pydantic tener un modelo de Pydantic v2 con sus propios campos definidos como modelos de Pydantic v1 o viceversa.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...pero puedes tener modelos separados usando Pydantic v1 y v2 en la misma app.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
En algunos casos, incluso es posible tener modelos de Pydantic v1 y v2 en la misma **path operation** de tu app de FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
En el ejemplo anterior, el modelo de entrada es un modelo de Pydantic v1 y el modelo de salida (definido en `response_model=ItemV2`) es un modelo de Pydantic v2.
### Parámetros de Pydantic v1 { #pydantic-v1-parameters }
Si necesitas usar algunas de las herramientas específicas de FastAPI para parámetros como `Body`, `Query`, `Form`, etc. con modelos de Pydantic v1, puedes importarlas de `fastapi.temp_pydantic_v1_params` mientras terminas la migración a Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migra por pasos { #migrate-in-steps }
/// tip | Consejo
Primero prueba con `bump-pydantic`, si tus tests pasan y eso funciona, entonces terminaste con un solo comando. ✨
///
Si `bump-pydantic` no funciona para tu caso de uso, puedes usar la compatibilidad de modelos Pydantic v1 y v2 en la misma app para hacer la migración a Pydantic v2 de forma gradual.
Podrías primero actualizar Pydantic para usar la última versión 2, y cambiar los imports para usar `pydantic.v1` para todos tus modelos.
Luego puedes empezar a migrar tus modelos de Pydantic v1 a v2 por grupos, en pasos graduales. 🚶
================================================
FILE: docs/es/docs/how-to/separate-openapi-schemas.md
================================================
# Separación de Esquemas OpenAPI para Entrada y Salida o No { #separate-openapi-schemas-for-input-and-output-or-not }
Desde que se lanzó **Pydantic v2**, el OpenAPI generado es un poco más exacto y **correcto** que antes. 😎
De hecho, en algunos casos, incluso tendrá **dos JSON Schemas** en OpenAPI para el mismo modelo Pydantic, para entrada y salida, dependiendo de si tienen **valores por defecto**.
Veamos cómo funciona eso y cómo cambiarlo si necesitas hacerlo.
## Modelos Pydantic para Entrada y Salida { #pydantic-models-for-input-and-output }
Digamos que tienes un modelo Pydantic con valores por defecto, como este:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modelo para Entrada { #model-for-input }
Si usas este modelo como entrada, como aquí:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
...entonces el campo `description` **no será requerido**. Porque tiene un valor por defecto de `None`.
### Modelo de Entrada en la Documentación { #input-model-in-docs }
Puedes confirmar eso en la documentación, el campo `description` no tiene un **asterisco rojo**, no está marcado como requerido:
### Modelo para Salida { #model-for-output }
Pero si usas el mismo modelo como salida, como aquí:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
...entonces, porque `description` tiene un valor por defecto, si **no devuelves nada** para ese campo, aún tendrá ese **valor por defecto**.
### Modelo para Datos de Response de Salida { #model-for-output-response-data }
Si interactúas con la documentación y revisas el response, aunque el código no agregó nada en uno de los campos `description`, el response JSON contiene el valor por defecto (`null`):
Esto significa que **siempre tendrá un valor**, solo que a veces el valor podría ser `None` (o `null` en JSON).
Eso significa que, los clientes que usan tu API no tienen que revisar si el valor existe o no, pueden **asumir que el campo siempre estará allí**, pero solo que en algunos casos tendrá el valor por defecto de `None`.
La forma de describir esto en OpenAPI es marcar ese campo como **requerido**, porque siempre estará allí.
Debido a eso, el JSON Schema para un modelo puede ser diferente dependiendo de si se usa para **entrada o salida**:
* para **entrada** el `description` **no será requerido**
* para **salida** será **requerido** (y posiblemente `None`, o en términos de JSON, `null`)
### Modelo para Salida en la Documentación { #model-for-output-in-docs }
También puedes revisar el modelo de salida en la documentación, **ambos** `name` y `description` están marcados como **requeridos** con un **asterisco rojo**:
### Modelo para Entrada y Salida en la Documentación { #model-for-input-and-output-in-docs }
Y si revisas todos los esquemas disponibles (JSON Schemas) en OpenAPI, verás que hay dos, uno `Item-Input` y uno `Item-Output`.
Para `Item-Input`, `description` **no es requerido**, no tiene un asterisco rojo.
Pero para `Item-Output`, `description` **es requerido**, tiene un asterisco rojo.
Con esta funcionalidad de **Pydantic v2**, la documentación de tu API es más **precisa**, y si tienes clientes y SDKs autogenerados, también serán más precisos, con una mejor **experiencia para desarrolladores** y consistencia. 🎉
## No Separar Esquemas { #do-not-separate-schemas }
Ahora, hay algunos casos donde podrías querer tener el **mismo esquema para entrada y salida**.
Probablemente el caso principal para esto es si ya tienes algún código cliente/SDKs autogenerado y no quieres actualizar todo el código cliente/SDKs autogenerado aún, probablemente querrás hacerlo en algún momento, pero tal vez no ahora.
En ese caso, puedes desactivar esta funcionalidad en **FastAPI**, con el parámetro `separate_input_output_schemas=False`.
/// info | Información
El soporte para `separate_input_output_schemas` fue agregado en FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Mismo Esquema para Modelos de Entrada y Salida en la Documentación { #same-schema-for-input-and-output-models-in-docs }
Y ahora habrá un único esquema para entrada y salida para el modelo, solo `Item`, y tendrá `description` como **no requerido**:
================================================
FILE: docs/es/docs/how-to/testing-database.md
================================================
# Escribir pruebas para una base de datos { #testing-a-database }
Puedes estudiar sobre bases de datos, SQL y SQLModel en la [documentación de SQLModel](https://sqlmodel.tiangolo.com/). 🤓
Hay un mini [tutorial sobre el uso de SQLModel con FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
Ese tutorial incluye una sección sobre [escribir pruebas para bases de datos SQL](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/es/docs/index.md
================================================
# FastAPI { #fastapi }
FastAPI framework, alto rendimiento, fácil de aprender, rápido de programar, listo para producción
---
**Documentación**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com/es)
**Código Fuente**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI es un framework web moderno, rápido (de alto rendimiento), para construir APIs con Python basado en las anotaciones de tipos estándar de Python.
Las funcionalidades clave son:
* **Rápido**: Muy alto rendimiento, a la par con **NodeJS** y **Go** (gracias a Starlette y Pydantic). [Uno de los frameworks Python más rápidos disponibles](#performance).
* **Rápido de programar**: Aumenta la velocidad para desarrollar funcionalidades en aproximadamente un 200% a 300%. *
* **Menos bugs**: Reduce en aproximadamente un 40% los errores inducidos por humanos (desarrolladores). *
* **Intuitivo**: Gran soporte para editores. Autocompletado en todas partes. Menos tiempo depurando.
* **Fácil**: Diseñado para ser fácil de usar y aprender. Menos tiempo leyendo documentación.
* **Corto**: Minimiza la duplicación de código. Múltiples funcionalidades desde cada declaración de parámetro. Menos bugs.
* **Robusto**: Obtén código listo para producción. Con documentación interactiva automática.
* **Basado en estándares**: Basado (y completamente compatible) con los estándares abiertos para APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (anteriormente conocido como Swagger) y [JSON Schema](https://json-schema.org/).
* estimación basada en pruebas con un equipo de desarrollo interno, construyendo aplicaciones de producción.
## Sponsors { #sponsors }
### Sponsor Keystone { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Sponsors Oro y Plata { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Otros sponsors](https://fastapi.tiangolo.com/es/fastapi-people/#sponsors)
## Opiniones { #opinions }
"_[...] Estoy usando **FastAPI** un montón estos días. [...] De hecho, estoy planeando usarlo para todos los servicios de **ML de mi equipo en Microsoft**. Algunos de ellos se están integrando en el núcleo del producto **Windows** y algunos productos de **Office**._"
---
"_Adoptamos el paquete **FastAPI** para crear un servidor **REST** que pueda ser consultado para obtener **predicciones**. [para Ludwig]_"
Piero Molino, Yaroslav Dudin, y Sai Sumanth Miryala - Uber(ref)
---
"_**Netflix** se complace en anunciar el lanzamiento de código abierto de nuestro framework de orquestación de **gestión de crisis**: **Dispatch**! [construido con **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_Estoy súper emocionado con **FastAPI**. ¡Es tan divertido!_"
Brian Okken - [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855) host del podcast(ref)
---
"_Honestamente, lo que has construido parece súper sólido y pulido. En muchos aspectos, es lo que quería que **Hug** fuera; es realmente inspirador ver a alguien construir eso._"
---
"_Si estás buscando aprender un **framework moderno** para construir APIs REST, échale un vistazo a **FastAPI** [...] Es rápido, fácil de usar y fácil de aprender [...]_"
"_Nos hemos cambiado a **FastAPI** para nuestras **APIs** [...] Creo que te gustará [...]_"
Ines Montani - Matthew Honnibal - [fundadores de Explosion AI](https://explosion.ai) - [creadores de spaCy](https://spacy.io)(ref) - (ref)
---
"_Si alguien está buscando construir una API de Python para producción, altamente recomendaría **FastAPI**. Está **hermosamente diseñado**, es **simple de usar** y **altamente escalable**, se ha convertido en un **componente clave** en nuestra estrategia de desarrollo API primero y está impulsando muchas automatizaciones y servicios como nuestro Ingeniero Virtual TAC._"
---
## Mini documental de FastAPI { #fastapi-mini-documentary }
Hay un [mini documental de FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE) lanzado a finales de 2025, puedes verlo online:
## **Typer**, el FastAPI de las CLIs { #typer-the-fastapi-of-clis }
Si estás construyendo una aplicación de CLI para ser usada en la terminal en lugar de una API web, revisa [**Typer**](https://typer.tiangolo.com/).
**Typer** es el hermano pequeño de FastAPI. Y está destinado a ser el **FastAPI de las CLIs**. ⌨️ 🚀
## Requisitos { #requirements }
FastAPI se apoya en hombros de gigantes:
* [Starlette](https://www.starlette.dev/) para las partes web.
* [Pydantic](https://docs.pydantic.dev/) para las partes de datos.
## Instalación { #installation }
Crea y activa un [entorno virtual](https://fastapi.tiangolo.com/es/virtual-environments/) y luego instala FastAPI:
**Nota**: Asegúrate de poner `"fastapi[standard]"` entre comillas para asegurar que funcione en todas las terminales.
## Ejemplo { #example }
### Créalo { #create-it }
Crea un archivo `main.py` con:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
O usa async def...
Si tu código usa `async` / `await`, usa `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Nota**:
Si no lo sabes, revisa la sección _"¿Con prisa?"_ sobre [`async` y `await` en la documentación](https://fastapi.tiangolo.com/es/async/#in-a-hurry).
### Córrelo { #run-it }
Corre el servidor con:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
Acerca del comando fastapi dev...
El comando `fastapi dev` lee tu archivo `main.py` automáticamente, detecta la app **FastAPI** en él y arranca un servidor usando [Uvicorn](https://www.uvicorn.dev).
Por defecto, `fastapi dev` comenzará con auto-recarga habilitada para el desarrollo local.
Puedes leer más sobre esto en la [documentación del CLI de FastAPI](https://fastapi.tiangolo.com/es/fastapi-cli/).
### Revísalo { #check-it }
Abre tu navegador en [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Verás el response JSON como:
```JSON
{"item_id": 5, "q": "somequery"}
```
Ya creaste una API que:
* Recibe requests HTTP en los _paths_ `/` y `/items/{item_id}`.
* Ambos _paths_ toman _operaciones_ `GET` (también conocidas como métodos HTTP).
* El _path_ `/items/{item_id}` tiene un _parámetro de path_ `item_id` que debe ser un `int`.
* El _path_ `/items/{item_id}` tiene un _parámetro de query_ `q` opcional que es un `str`.
### Documentación interactiva de la API { #interactive-api-docs }
Ahora ve a [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás la documentación interactiva automática de la API (proporcionada por [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Documentación alternativa de la API { #alternative-api-docs }
Y ahora, ve a [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Verás la documentación alternativa automática (proporcionada por [ReDoc](https://github.com/Rebilly/ReDoc)):

## Actualización del ejemplo { #example-upgrade }
Ahora modifica el archivo `main.py` para recibir un body desde un request `PUT`.
Declara el body usando tipos estándar de Python, gracias a Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
El servidor `fastapi dev` debería recargarse automáticamente.
### Actualización de la documentación interactiva de la API { #interactive-api-docs-upgrade }
Ahora ve a [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* La documentación interactiva de la API se actualizará automáticamente, incluyendo el nuevo body:

* Haz clic en el botón "Try it out", te permite llenar los parámetros e interactuar directamente con la API:

* Luego haz clic en el botón "Execute", la interfaz de usuario se comunicará con tu API, enviará los parámetros, obtendrá los resultados y los mostrará en la pantalla:

### Actualización de la documentación alternativa de la API { #alternative-api-docs-upgrade }
Y ahora, ve a [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* La documentación alternativa también reflejará el nuevo parámetro de query y body:

### Resumen { #recap }
En resumen, declaras **una vez** los tipos de parámetros, body, etc. como parámetros de función.
Lo haces con tipos estándar modernos de Python.
No tienes que aprender una nueva sintaxis, los métodos o clases de un paquete específico, etc.
Solo **Python** estándar.
Por ejemplo, para un `int`:
```Python
item_id: int
```
o para un modelo `Item` más complejo:
```Python
item: Item
```
...y con esa única declaración obtienes:
* Soporte para editores, incluyendo:
* Autocompletado.
* Chequeo de tipos.
* Validación de datos:
* Errores automáticos y claros cuando los datos son inválidos.
* Validación incluso para objetos JSON profundamente anidados.
* Conversión de datos de entrada: de la red a los datos y tipos de Python. Leyendo desde:
* JSON.
* Parámetros de path.
* Parámetros de query.
* Cookies.
* Headers.
* Formularios.
* Archivos.
* Conversión de datos de salida: convirtiendo de datos y tipos de Python a datos de red (como JSON):
* Convertir tipos de Python (`str`, `int`, `float`, `bool`, `list`, etc).
* Objetos `datetime`.
* Objetos `UUID`.
* Modelos de base de datos.
* ...y muchos más.
* Documentación interactiva automática de la API, incluyendo 2 interfaces de usuario alternativas:
* Swagger UI.
* ReDoc.
---
Volviendo al ejemplo de código anterior, **FastAPI**:
* Validará que haya un `item_id` en el path para requests `GET` y `PUT`.
* Validará que el `item_id` sea del tipo `int` para requests `GET` y `PUT`.
* Si no lo es, el cliente verá un error útil y claro.
* Revisa si hay un parámetro de query opcional llamado `q` (como en `http://127.0.0.1:8000/items/foo?q=somequery`) para requests `GET`.
* Como el parámetro `q` está declarado con `= None`, es opcional.
* Sin el `None` sería requerido (como lo es el body en el caso con `PUT`).
* Para requests `PUT` a `/items/{item_id}`, leerá el body como JSON:
* Revisa que tiene un atributo requerido `name` que debe ser un `str`.
* Revisa que tiene un atributo requerido `price` que debe ser un `float`.
* Revisa que tiene un atributo opcional `is_offer`, que debe ser un `bool`, si está presente.
* Todo esto también funcionaría para objetos JSON profundamente anidados.
* Convertirá de y a JSON automáticamente.
* Documentará todo con OpenAPI, que puede ser usado por:
* Sistemas de documentación interactiva.
* Sistemas de generación automática de código cliente, para muchos lenguajes.
* Proporcionará 2 interfaces web de documentación interactiva directamente.
---
Solo tocamos los conceptos básicos, pero ya te haces una idea de cómo funciona todo.
Intenta cambiar la línea con:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...desde:
```Python
... "item_name": item.name ...
```
...a:
```Python
... "item_price": item.price ...
```
...y observa cómo tu editor autocompleta los atributos y conoce sus tipos:

Para un ejemplo más completo incluyendo más funcionalidades, ve al Tutorial - Guía del Usuario.
**Alerta de spoilers**: el tutorial - guía del usuario incluye:
* Declaración de **parámetros** desde otros lugares diferentes como: **headers**, **cookies**, **campos de formulario** y **archivos**.
* Cómo establecer **restricciones de validación** como `maximum_length` o `regex`.
* Un sistema de **Inyección de Dependencias** muy poderoso y fácil de usar.
* Seguridad y autenticación, incluyendo soporte para **OAuth2** con **tokens JWT** y autenticación **HTTP Basic**.
* Técnicas más avanzadas (pero igualmente fáciles) para declarar **modelos JSON profundamente anidados** (gracias a Pydantic).
* Integración con **GraphQL** usando [Strawberry](https://strawberry.rocks) y otros paquetes.
* Muchas funcionalidades extra (gracias a Starlette) como:
* **WebSockets**
* pruebas extremadamente fáciles basadas en HTTPX y `pytest`
* **CORS**
* **Sesiones de Cookies**
* ...y más.
### Despliega tu app (opcional) { #deploy-your-app-optional }
Opcionalmente puedes desplegar tu app de FastAPI en [FastAPI Cloud](https://fastapicloud.com), ve y únete a la lista de espera si no lo has hecho. 🚀
Si ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉), puedes desplegar tu aplicación con un solo comando.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
#### Acerca de FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** está construido por el mismo autor y equipo detrás de **FastAPI**.
Optimiza el proceso de **construir**, **desplegar** y **acceder** a una API con un esfuerzo mínimo.
Trae la misma **experiencia de desarrollador** de construir apps con FastAPI a **desplegarlas** en la nube. 🎉
FastAPI Cloud es el sponsor principal y proveedor de financiamiento para los proyectos open source *FastAPI and friends*. ✨
#### Despliega en otros proveedores de cloud { #deploy-to-other-cloud-providers }
FastAPI es open source y está basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor de cloud que elijas.
Sigue las guías de tu proveedor de cloud para desplegar apps de FastAPI con ellos. 🤓
## Rendimiento { #performance }
Benchmarks independientes de TechEmpower muestran aplicaciones **FastAPI** ejecutándose bajo Uvicorn como [uno de los frameworks Python más rápidos disponibles](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), solo por debajo de Starlette y Uvicorn (usados internamente por FastAPI). (*)
Para entender más sobre esto, ve la sección [Benchmarks](https://fastapi.tiangolo.com/es/benchmarks/).
## Dependencias { #dependencies }
FastAPI depende de Pydantic y Starlette.
### Dependencias `standard` { #standard-dependencies }
Cuando instalas FastAPI con `pip install "fastapi[standard]"` viene con el grupo `standard` de dependencias opcionales:
Usadas por Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - para validación de correos electrónicos.
Usadas por Starlette:
* [`httpx`](https://www.python-httpx.org) - Requerido si deseas usar el `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - Requerido si deseas usar la configuración de plantilla por defecto.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - Requerido si deseas soportar form "parsing", con `request.form()`.
Usadas por FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) - para el servidor que carga y sirve tu aplicación. Esto incluye `uvicorn[standard]`, que incluye algunas dependencias (por ejemplo, `uvloop`) necesarias para servir con alto rendimiento.
* `fastapi-cli[standard]` - para proporcionar el comando `fastapi`.
* Esto incluye `fastapi-cloud-cli`, que te permite desplegar tu aplicación de FastAPI en [FastAPI Cloud](https://fastapicloud.com).
### Sin Dependencias `standard` { #without-standard-dependencies }
Si no deseas incluir las dependencias opcionales `standard`, puedes instalar con `pip install fastapi` en lugar de `pip install "fastapi[standard]"`.
### Sin `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Si quieres instalar FastAPI con las dependencias standard pero sin `fastapi-cloud-cli`, puedes instalar con `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Dependencias Opcionales Adicionales { #additional-optional-dependencies }
Existen algunas dependencias adicionales que podrías querer instalar.
Dependencias opcionales adicionales de Pydantic:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - para la gestión de configuraciones.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - para tipos extra para ser usados con Pydantic.
Dependencias opcionales adicionales de FastAPI:
* [`orjson`](https://github.com/ijl/orjson) - Requerido si deseas usar `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - Requerido si deseas usar `UJSONResponse`.
## Licencia { #license }
Este proyecto tiene licencia bajo los términos de la licencia MIT.
================================================
FILE: docs/es/docs/learn/index.md
================================================
# Aprende { #learn }
Aquí están las secciones introductorias y los tutoriales para aprender **FastAPI**.
Podrías considerar esto un **libro**, un **curso**, la forma **oficial** y recomendada de aprender FastAPI. 😎
================================================
FILE: docs/es/docs/project-generation.md
================================================
# Plantilla Full Stack FastAPI { #full-stack-fastapi-template }
Las plantillas, aunque normalmente vienen con una configuración específica, están diseñadas para ser flexibles y personalizables. Esto te permite modificarlas y adaptarlas a los requisitos de tu proyecto, haciéndolas un excelente punto de partida. 🏁
Puedes usar esta plantilla para comenzar, ya que incluye gran parte de la configuración inicial, seguridad, base de datos y algunos endpoints de API ya hechos para ti.
Repositorio de GitHub: [Plantilla Full Stack FastAPI](https://github.com/tiangolo/full-stack-fastapi-template)
## Plantilla Full Stack FastAPI - Stack de tecnología y funcionalidades { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/es) para la API del backend en Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para las interacciones con bases de datos SQL en Python (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), utilizado por FastAPI, para la validación de datos y gestión de configuraciones.
- 💾 [PostgreSQL](https://www.postgresql.org) como base de datos SQL.
- 🚀 [React](https://react.dev) para el frontend.
- 💃 Usando TypeScript, hooks, Vite, y otras partes de una stack moderna de frontend.
- 🎨 [Tailwind CSS](https://tailwindcss.com) y [shadcn/ui](https://ui.shadcn.com) para los componentes del frontend.
- 🤖 Un cliente de frontend generado automáticamente.
- 🧪 [Playwright](https://playwright.dev) para escribir pruebas End-to-End.
- 🦇 Soporte para modo oscuro.
- 🐋 [Docker Compose](https://www.docker.com) para desarrollo y producción.
- 🔒 Hashing seguro de contraseñas por defecto.
- 🔑 Autenticación con tokens JWT.
- 📫 Recuperación de contraseñas basada en email.
- ✅ Pruebas con [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) como proxy inverso / load balancer.
- 🚢 Instrucciones de despliegue usando Docker Compose, incluyendo cómo configurar un proxy Traefik frontend para manejar certificados HTTPS automáticos.
- 🏭 CI (integración continua) y CD (despliegue continuo) basados en GitHub Actions.
================================================
FILE: docs/es/docs/python-types.md
================================================
# Introducción a Tipos en Python { #python-types-intro }
Python tiene soporte para "anotaciones de tipos" opcionales (también llamadas "type hints").
Estas **"anotaciones de tipos"** o type hints son una sintaxis especial que permite declarar el tipo de una variable.
Al declarar tipos para tus variables, los editores y herramientas te pueden proporcionar un mejor soporte.
Este es solo un **tutorial rápido / recordatorio** sobre las anotaciones de tipos en Python. Cubre solo lo mínimo necesario para usarlas con **FastAPI**... que en realidad es muy poco.
**FastAPI** se basa completamente en estas anotaciones de tipos, dándole muchas ventajas y beneficios.
Pero incluso si nunca usas **FastAPI**, te beneficiaría aprender un poco sobre ellas.
/// note | Nota
Si eres un experto en Python, y ya sabes todo sobre las anotaciones de tipos, salta al siguiente capítulo.
///
## Motivación { #motivation }
Comencemos con un ejemplo simple:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Llamar a este programa genera:
```
John Doe
```
La función hace lo siguiente:
* Toma un `first_name` y `last_name`.
* Convierte la primera letra de cada uno a mayúsculas con `title()`.
* Concatena ambos con un espacio en el medio.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Edítalo { #edit-it }
Es un programa muy simple.
Pero ahora imagina que lo escribieras desde cero.
En algún momento habrías empezado la definición de la función, tenías los parámetros listos...
Pero luego tienes que llamar "ese método que convierte la primera letra a mayúscula".
¿Era `upper`? ¿Era `uppercase`? `first_uppercase`? `capitalize`?
Entonces, pruebas con el amigo del viejo programador, el autocompletado del editor.
Escribes el primer parámetro de la función, `first_name`, luego un punto (`.`) y luego presionas `Ctrl+Espacio` para activar el autocompletado.
Pero, tristemente, no obtienes nada útil:
### Añadir tipos { #add-types }
Modifiquemos una sola línea de la versión anterior.
Cambiaremos exactamente este fragmento, los parámetros de la función, de:
```Python
first_name, last_name
```
a:
```Python
first_name: str, last_name: str
```
Eso es todo.
Esas son las "anotaciones de tipos":
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Eso no es lo mismo que declarar valores por defecto como sería con:
```Python
first_name="john", last_name="doe"
```
Es una cosa diferente.
Estamos usando dos puntos (`:`), no igualdades (`=`).
Y agregar anotaciones de tipos normalmente no cambia lo que sucede de lo que ocurriría sin ellas.
Pero ahora, imagina que nuevamente estás en medio de la creación de esa función, pero con anotaciones de tipos.
En el mismo punto, intentas activar el autocompletado con `Ctrl+Espacio` y ves:
Con eso, puedes desplazarte, viendo las opciones, hasta que encuentres la que "te suene":
## Más motivación { #more-motivation }
Revisa esta función, ya tiene anotaciones de tipos:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Porque el editor conoce los tipos de las variables, no solo obtienes autocompletado, también obtienes chequeo de errores:
Ahora sabes que debes corregirlo, convertir `age` a un string con `str(age)`:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Declaración de tipos { #declaring-types }
Acabas de ver el lugar principal para declarar anotaciones de tipos. Como parámetros de función.
Este también es el lugar principal donde los utilizarías con **FastAPI**.
### Tipos simples { #simple-types }
Puedes declarar todos los tipos estándar de Python, no solo `str`.
Puedes usar, por ejemplo:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Módulo `typing` { #typing-module }
Para algunos casos adicionales, podrías necesitar importar algunas cosas del módulo `typing` de la standard library, por ejemplo cuando quieres declarar que algo tiene "cualquier tipo", puedes usar `Any` de `typing`:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Tipos genéricos { #generic-types }
Algunos tipos pueden tomar "parámetros de tipo" entre corchetes, para definir sus tipos internos, por ejemplo una "lista de strings" se declararía `list[str]`.
Estos tipos que pueden tomar parámetros de tipo se llaman **Tipos Genéricos** o **Genéricos**.
Puedes usar los mismos tipos integrados como genéricos (con corchetes y tipos dentro):
* `list`
* `tuple`
* `set`
* `dict`
#### Lista { #list }
Por ejemplo, vamos a definir una variable para ser una `list` de `str`.
Declara la variable, con la misma sintaxis de dos puntos (`:`).
Como tipo, pon `list`.
Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Información
Esos tipos internos en los corchetes se denominan "parámetros de tipo".
En este caso, `str` es el parámetro de tipo pasado a `list`.
///
Eso significa: "la variable `items` es una `list`, y cada uno de los ítems en esta lista es un `str`".
Al hacer eso, tu editor puede proporcionar soporte incluso mientras procesa elementos de la lista:
Sin tipos, eso es casi imposible de lograr.
Nota que la variable `item` es uno de los elementos en la lista `items`.
Y aún así, el editor sabe que es un `str` y proporciona soporte para eso.
#### Tuple y Set { #tuple-and-set }
Harías lo mismo para declarar `tuple`s y `set`s:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Esto significa:
* La variable `items_t` es un `tuple` con 3 ítems, un `int`, otro `int`, y un `str`.
* La variable `items_s` es un `set`, y cada uno de sus ítems es del tipo `bytes`.
#### Dict { #dict }
Para definir un `dict`, pasas 2 parámetros de tipo, separados por comas.
El primer parámetro de tipo es para las claves del `dict`.
El segundo parámetro de tipo es para los valores del `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Esto significa:
* La variable `prices` es un `dict`:
* Las claves de este `dict` son del tipo `str` (digamos, el nombre de cada ítem).
* Los valores de este `dict` son del tipo `float` (digamos, el precio de cada ítem).
#### Union { #union }
Puedes declarar que una variable puede ser cualquiera de **varios tipos**, por ejemplo, un `int` o un `str`.
Para definirlo usas la barra vertical (`|`) para separar ambos tipos.
Esto se llama una "unión", porque la variable puede ser cualquiera en la unión de esos dos conjuntos de tipos.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Esto significa que `item` podría ser un `int` o un `str`.
#### Posiblemente `None` { #possibly-none }
Puedes declarar que un valor podría tener un tipo, como `str`, pero que también podría ser `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Usar `str | None` en lugar de solo `str` te permitirá al editor ayudarte a detectar errores donde podrías estar asumiendo que un valor siempre es un `str`, cuando en realidad también podría ser `None`.
### Clases como tipos { #classes-as-types }
También puedes declarar una clase como el tipo de una variable.
Digamos que tienes una clase `Person`, con un nombre:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Luego puedes declarar una variable para que sea de tipo `Person`:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
Y luego, nuevamente, obtienes todo el soporte del editor:
Nota que esto significa "`one_person` es una **instance** de la clase `Person`".
No significa "`one_person` es la **clase** llamada `Person`".
## Modelos Pydantic { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) es un paquete de Python para realizar la validación de datos.
Declaras la "forma" de los datos como clases con atributos.
Y cada atributo tiene un tipo.
Entonces creas un instance de esa clase con algunos valores y validará los valores, los convertirá al tipo adecuado (si es el caso) y te dará un objeto con todos los datos.
Y obtienes todo el soporte del editor con ese objeto resultante.
Un ejemplo de la documentación oficial de Pydantic:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Información
Para saber más sobre [Pydantic, revisa su documentación](https://docs.pydantic.dev/).
///
**FastAPI** está completamente basado en Pydantic.
Verás mucho más de todo esto en práctica en el [Tutorial - Guía del Usuario](tutorial/index.md).
## Anotaciones de tipos con metadata { #type-hints-with-metadata-annotations }
Python también tiene una funcionalidad que permite poner **metadata adicional** en estas anotaciones de tipos usando `Annotated`.
Puedes importar `Annotated` desde `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python en sí no hace nada con este `Annotated`. Y para los editores y otras herramientas, el tipo sigue siendo `str`.
Pero puedes usar este espacio en `Annotated` para proporcionar a **FastAPI** metadata adicional sobre cómo quieres que se comporte tu aplicación.
Lo importante a recordar es que **el primer *parámetro de tipo*** que pasas a `Annotated` es el **tipo real**. El resto es solo metadata para otras herramientas.
Por ahora, solo necesitas saber que `Annotated` existe, y que es Python estándar. 😎
Luego verás lo **poderoso** que puede ser.
/// tip | Consejo
El hecho de que esto sea **Python estándar** significa que seguirás obteniendo la **mejor experiencia de desarrollador posible** en tu editor, con las herramientas que usas para analizar y refactorizar tu código, etc. ✨
Y también que tu código será muy compatible con muchas otras herramientas y paquetes de Python. 🚀
///
## Anotaciones de tipos en **FastAPI** { #type-hints-in-fastapi }
**FastAPI** aprovecha estas anotaciones de tipos para hacer varias cosas.
Con **FastAPI** declaras parámetros con anotaciones de tipos y obtienes:
* **Soporte del editor**.
* **Chequeo de tipos**.
...y **FastAPI** usa las mismas declaraciones para:
* **Definir requerimientos**: de parámetros de path de la request, parámetros de query, headers, bodies, dependencias, etc.
* **Convertir datos**: de la request al tipo requerido.
* **Validar datos**: provenientes de cada request:
* Generando **errores automáticos** devueltos al cliente cuando los datos son inválidos.
* **Documentar** la API usando OpenAPI:
* Que luego es usada por las interfaces de documentación interactiva automática.
Todo esto puede sonar abstracto. No te preocupes. Verás todo esto en acción en el [Tutorial - Guía del Usuario](tutorial/index.md).
Lo importante es que al usar tipos estándar de Python, en un solo lugar (en lugar de agregar más clases, decoradores, etc.), **FastAPI** hará gran parte del trabajo por ti.
/// info | Información
Si ya revisaste todo el tutorial y volviste para ver más sobre tipos, un buen recurso es [la "cheat sheet" de `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/es/docs/resources/index.md
================================================
# Recursos { #resources }
Recursos adicionales, enlaces externos y más. ✈️
================================================
FILE: docs/es/docs/translation-banner.md
================================================
/// details | 🌐 Traducción por IA y humanos
Esta traducción fue hecha por IA guiada por humanos. 🤝
Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖
Puedes mejorar esta traducción [ayudándonos a guiar mejor al LLM de IA](https://fastapi.tiangolo.com/es/contributing/#translations).
[Versión en inglés](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/es/docs/tutorial/background-tasks.md
================================================
# Tareas en Segundo Plano { #background-tasks }
Puedes definir tareas en segundo plano para que se ejecuten *después* de devolver un response.
Esto es útil para operaciones que necesitan ocurrir después de un request, pero para las que el cliente realmente no necesita esperar a que la operación termine antes de recibir el response.
Esto incluye, por ejemplo:
* Notificaciones por email enviadas después de realizar una acción:
* Como conectarse a un servidor de email y enviar un email tiende a ser "lento" (varios segundos), puedes devolver el response de inmediato y enviar la notificación por email en segundo plano.
* Procesamiento de datos:
* Por ejemplo, supongamos que recibes un archivo que debe pasar por un proceso lento, puedes devolver un response de "Accepted" (HTTP 202) y procesar el archivo en segundo plano.
## Usando `BackgroundTasks` { #using-backgroundtasks }
Primero, importa `BackgroundTasks` y define un parámetro en tu *path operation function* con una declaración de tipo de `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** creará el objeto de tipo `BackgroundTasks` por ti y lo pasará como ese parámetro.
## Crear una función de tarea { #create-a-task-function }
Crea una función para que se ejecute como la tarea en segundo plano.
Es solo una función estándar que puede recibir parámetros.
Puede ser una función `async def` o una función normal `def`, **FastAPI** sabrá cómo manejarla correctamente.
En este caso, la función de tarea escribirá en un archivo (simulando el envío de un email).
Y como la operación de escritura no usa `async` y `await`, definimos la función con un `def` normal:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Agregar la tarea en segundo plano { #add-the-background-task }
Dentro de tu *path operation function*, pasa tu función de tarea al objeto de *background tasks* con el método `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` recibe como argumentos:
* Una función de tarea para ejecutar en segundo plano (`write_notification`).
* Cualquier secuencia de argumentos que deba pasarse a la función de tarea en orden (`email`).
* Cualquier argumento de palabras clave que deba pasarse a la función de tarea (`message="some notification"`).
## Inyección de Dependencias { #dependency-injection }
Usar `BackgroundTasks` también funciona con el sistema de inyección de dependencias, puedes declarar un parámetro de tipo `BackgroundTasks` en varios niveles: en una *path operation function*, en una dependencia (dependable), en una sub-dependencia, etc.
**FastAPI** sabe qué hacer en cada caso y cómo reutilizar el mismo objeto, de modo que todas las tareas en segundo plano se combinan y ejecutan en segundo plano después:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
En este ejemplo, los mensajes se escribirán en el archivo `log.txt` *después* de que se envíe el response.
Si hay un query en el request, se escribirá en el log en una tarea en segundo plano.
Y luego otra tarea en segundo plano generada en la *path operation function* escribirá un mensaje usando el parámetro de path `email`.
## Detalles Técnicos { #technical-details }
La clase `BackgroundTasks` proviene directamente de [`starlette.background`](https://www.starlette.dev/background/).
Se importa/incluye directamente en FastAPI para que puedas importarla desde `fastapi` y evitar importar accidentalmente la alternativa `BackgroundTask` (sin la `s` al final) de `starlette.background`.
Al usar solo `BackgroundTasks` (y no `BackgroundTask`), es posible usarla como un parámetro de *path operation function* y dejar que **FastAPI** maneje el resto por ti, tal como cuando usas el objeto `Request` directamente.
Todavía es posible usar `BackgroundTask` solo en FastAPI, pero debes crear el objeto en tu código y devolver una `Response` de Starlette incluyéndolo.
Puedes ver más detalles en [la documentación oficial de Starlette sobre Background Tasks](https://www.starlette.dev/background/).
## Advertencia { #caveat }
Si necesitas realizar una computación intensa en segundo plano y no necesariamente necesitas que se ejecute por el mismo proceso (por ejemplo, no necesitas compartir memoria, variables, etc.), podrías beneficiarte del uso de otras herramientas más grandes como [Celery](https://docs.celeryq.dev).
Tienden a requerir configuraciones más complejas, un gestor de cola de mensajes/trabajos, como RabbitMQ o Redis, pero te permiten ejecutar tareas en segundo plano en múltiples procesos, y especialmente, en múltiples servidores.
Pero si necesitas acceder a variables y objetos de la misma app de **FastAPI**, o necesitas realizar pequeñas tareas en segundo plano (como enviar una notificación por email), simplemente puedes usar `BackgroundTasks`.
## Resumen { #recap }
Importa y usa `BackgroundTasks` con parámetros en *path operation functions* y dependencias para agregar tareas en segundo plano.
================================================
FILE: docs/es/docs/tutorial/bigger-applications.md
================================================
# Aplicaciones más grandes - Múltiples archivos { #bigger-applications-multiple-files }
Si estás construyendo una aplicación o una API web, rara vez podrás poner todo en un solo archivo.
**FastAPI** proporciona una herramienta conveniente para estructurar tu aplicación manteniendo toda la flexibilidad.
/// info | Información
Si vienes de Flask, esto sería el equivalente a los Blueprints de Flask.
///
## Un ejemplo de estructura de archivos { #an-example-file-structure }
Digamos que tienes una estructura de archivos como esta:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Consejo
Hay varios archivos `__init__.py`: uno en cada directorio o subdirectorio.
Esto es lo que permite importar código de un archivo a otro.
Por ejemplo, en `app/main.py` podrías tener una línea como:
```
from app.routers import items
```
///
* El directorio `app` contiene todo. Y tiene un archivo vacío `app/__init__.py`, por lo que es un "paquete de Python" (una colección de "módulos de Python"): `app`.
* Contiene un archivo `app/main.py`. Como está dentro de un paquete de Python (un directorio con un archivo `__init__.py`), es un "módulo" de ese paquete: `app.main`.
* También hay un archivo `app/dependencies.py`, al igual que `app/main.py`, es un "módulo": `app.dependencies`.
* Hay un subdirectorio `app/routers/` con otro archivo `__init__.py`, por lo que es un "subpaquete de Python": `app.routers`.
* El archivo `app/routers/items.py` está dentro de un paquete, `app/routers/`, por lo que es un submódulo: `app.routers.items`.
* Lo mismo con `app/routers/users.py`, es otro submódulo: `app.routers.users`.
* También hay un subdirectorio `app/internal/` con otro archivo `__init__.py`, por lo que es otro "subpaquete de Python": `app.internal`.
* Y el archivo `app/internal/admin.py` es otro submódulo: `app.internal.admin`.
La misma estructura de archivos con comentarios:
```bash
.
├── app # "app" es un paquete de Python
│ ├── __init__.py # este archivo hace que "app" sea un "paquete de Python"
│ ├── main.py # módulo "main", por ejemplo import app.main
│ ├── dependencies.py # módulo "dependencies", por ejemplo import app.dependencies
│ └── routers # "routers" es un "subpaquete de Python"
│ │ ├── __init__.py # hace que "routers" sea un "subpaquete de Python"
│ │ ├── items.py # submódulo "items", por ejemplo import app.routers.items
│ │ └── users.py # submódulo "users", por ejemplo import app.routers.users
│ └── internal # "internal" es un "subpaquete de Python"
│ ├── __init__.py # hace que "internal" sea un "subpaquete de Python"
│ └── admin.py # submódulo "admin", por ejemplo import app.internal.admin
```
## `APIRouter` { #apirouter }
Digamos que el archivo dedicado solo a manejar usuarios es el submódulo en `/app/routers/users.py`.
Quieres tener las *path operations* relacionadas con tus usuarios separadas del resto del código, para mantenerlo organizado.
Pero todavía es parte de la misma aplicación/web API de **FastAPI** (es parte del mismo "paquete de Python").
Puedes crear las *path operations* para ese módulo usando `APIRouter`.
### Importar `APIRouter` { #import-apirouter }
Lo importas y creas una "instance" de la misma manera que lo harías con la clase `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Path operations* con `APIRouter` { #path-operations-with-apirouter }
Y luego lo usas para declarar tus *path operations*.
Úsalo de la misma manera que usarías la clase `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Puedes pensar en `APIRouter` como una clase "mini `FastAPI`".
Se soportan todas las mismas opciones.
Todos los mismos `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip | Consejo
En este ejemplo, la variable se llama `router`, pero puedes nombrarla como quieras.
///
Vamos a incluir este `APIRouter` en la aplicación principal de `FastAPI`, pero primero, revisemos las dependencias y otro `APIRouter`.
## Dependencias { #dependencies }
Vemos que vamos a necesitar algunas dependencias usadas en varios lugares de la aplicación.
Así que las ponemos en su propio módulo `dependencies` (`app/dependencies.py`).
Ahora utilizaremos una dependencia simple para leer un header `X-Token` personalizado:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Consejo
Estamos usando un header inventado para simplificar este ejemplo.
Pero en casos reales obtendrás mejores resultados usando las [utilidades de Seguridad](security/index.md) integradas.
///
## Otro módulo con `APIRouter` { #another-module-with-apirouter }
Digamos que también tienes los endpoints dedicados a manejar "items" de tu aplicación en el módulo `app/routers/items.py`.
Tienes *path operations* para:
* `/items/`
* `/items/{item_id}`
Es toda la misma estructura que con `app/routers/users.py`.
Pero queremos ser más inteligentes y simplificar un poco el código.
Sabemos que todas las *path operations* en este módulo tienen el mismo:
* Prefijo de path: `/items`.
* `tags`: (solo una etiqueta: `items`).
* `responses` extra.
* `dependencies`: todas necesitan esa dependencia `X-Token` que creamos.
Entonces, en lugar de agregar todo eso a cada *path operation*, podemos agregarlo al `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Como el path de cada *path operation* tiene que empezar con `/`, como en:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...el prefijo no debe incluir un `/` final.
Así que, el prefijo en este caso es `/items`.
También podemos agregar una lista de `tags` y `responses` extra que se aplicarán a todas las *path operations* incluidas en este router.
Y podemos agregar una lista de `dependencies` que se añadirá a todas las *path operations* en el router y se ejecutarán/solucionarán por cada request que les haga.
/// tip | Consejo
Nota que, al igual que [dependencias en decoradores de *path operations*](dependencies/dependencies-in-path-operation-decorators.md), ningún valor será pasado a tu *path operation function*.
///
El resultado final es que los paths de item son ahora:
* `/items/`
* `/items/{item_id}`
...como pretendíamos.
* Serán marcados con una lista de tags que contiene un solo string `"items"`.
* Estos "tags" son especialmente útiles para los sistemas de documentación interactiva automática (usando OpenAPI).
* Todos incluirán las `responses` predefinidas.
* Todas estas *path operations* tendrán la lista de `dependencies` evaluadas/ejecutadas antes de ellas.
* Si también declaras dependencias en una *path operation* específica, **también se ejecutarán**.
* Las dependencias del router se ejecutan primero, luego las [`dependencies` en el decorador](dependencies/dependencies-in-path-operation-decorators.md), y luego las dependencias de parámetros normales.
* También puedes agregar [dependencias de `Security` con `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Consejo
Tener `dependencies` en el `APIRouter` puede ser usado, por ejemplo, para requerir autenticación para un grupo completo de *path operations*. Incluso si las dependencias no son añadidas individualmente a cada una de ellas.
///
/// check | Revisa
Los parámetros `prefix`, `tags`, `responses`, y `dependencies` son (como en muchos otros casos) solo una funcionalidad de **FastAPI** para ayudarte a evitar la duplicación de código.
///
### Importar las dependencias { #import-the-dependencies }
Este código vive en el módulo `app.routers.items`, el archivo `app/routers/items.py`.
Y necesitamos obtener la función de dependencia del módulo `app.dependencies`, el archivo `app/dependencies.py`.
Así que usamos un import relativo con `..` para las dependencias:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Cómo funcionan los imports relativos { #how-relative-imports-work }
/// tip | Consejo
Si sabes perfectamente cómo funcionan los imports, continúa a la siguiente sección abajo.
///
Un solo punto `.`, como en:
```Python
from .dependencies import get_token_header
```
significaría:
* Partiendo en el mismo paquete en el que este módulo (el archivo `app/routers/items.py`) habita (el directorio `app/routers/`)...
* busca el módulo `dependencies` (un archivo imaginario en `app/routers/dependencies.py`)...
* y de él, importa la función `get_token_header`.
Pero ese archivo no existe, nuestras dependencias están en un archivo en `app/dependencies.py`.
Recuerda cómo se ve nuestra estructura de aplicación/archivo:
---
Los dos puntos `..`, como en:
```Python
from ..dependencies import get_token_header
```
significan:
* Partiendo en el mismo paquete en el que este módulo (el archivo `app/routers/items.py`) habita (el directorio `app/routers/`)...
* ve al paquete padre (el directorio `app/`)...
* y allí, busca el módulo `dependencies` (el archivo en `app/dependencies.py`)...
* y de él, importa la función `get_token_header`.
¡Eso funciona correctamente! 🎉
---
De la misma manera, si hubiéramos usado tres puntos `...`, como en:
```Python
from ...dependencies import get_token_header
```
eso significaría:
* Partiendo en el mismo paquete en el que este módulo (el archivo `app/routers/items.py`) habita (el directorio `app/routers/`)...
* ve al paquete padre (el directorio `app/`)...
* luego ve al paquete padre de ese paquete (no hay paquete padre, `app` es el nivel superior 😱)...
* y allí, busca el módulo `dependencies` (el archivo en `app/dependencies.py`)...
* y de él, importa la función `get_token_header`.
Eso se referiría a algún paquete arriba de `app/`, con su propio archivo `__init__.py`, etc. Pero no tenemos eso. Así que, eso lanzaría un error en nuestro ejemplo. 🚨
Pero ahora sabes cómo funciona, para que puedas usar imports relativos en tus propias apps sin importar cuán complejas sean. 🤓
### Agregar algunos `tags`, `responses`, y `dependencies` personalizados { #add-some-custom-tags-responses-and-dependencies }
No estamos agregando el prefijo `/items` ni los `tags=["items"]` a cada *path operation* porque los hemos añadido al `APIRouter`.
Pero aún podemos agregar _más_ `tags` que se aplicarán a una *path operation* específica, y también algunas `responses` extra específicas para esa *path operation*:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Consejo
Esta última path operation tendrá la combinación de tags: `["items", "custom"]`.
Y también tendrá ambas responses en la documentación, una para `404` y otra para `403`.
///
## El `FastAPI` principal { #the-main-fastapi }
Ahora, veamos el módulo en `app/main.py`.
Aquí es donde importas y usas la clase `FastAPI`.
Este será el archivo principal en tu aplicación que conecta todo.
Y como la mayor parte de tu lógica ahora vivirá en su propio módulo específico, el archivo principal será bastante simple.
### Importar `FastAPI` { #import-fastapi }
Importas y creas una clase `FastAPI` como normalmente.
Y podemos incluso declarar [dependencias globales](dependencies/global-dependencies.md) que se combinarán con las dependencias para cada `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Importar el `APIRouter` { #import-the-apirouter }
Ahora importamos los otros submódulos que tienen `APIRouter`s:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Como los archivos `app/routers/users.py` y `app/routers/items.py` son submódulos que son parte del mismo paquete de Python `app`, podemos usar un solo punto `.` para importarlos usando "imports relativos".
### Cómo funciona la importación { #how-the-importing-works }
La sección:
```Python
from .routers import items, users
```
significa:
* Partiendo en el mismo paquete en el que este módulo (el archivo `app/main.py`) habita (el directorio `app/`)...
* busca el subpaquete `routers` (el directorio en `app/routers/`)...
* y de él, importa el submódulo `items` (el archivo en `app/routers/items.py`) y `users` (el archivo en `app/routers/users.py`)...
El módulo `items` tendrá una variable `router` (`items.router`). Este es el mismo que creamos en el archivo `app/routers/items.py`, es un objeto `APIRouter`.
Y luego hacemos lo mismo para el módulo `users`.
También podríamos importarlos así:
```Python
from app.routers import items, users
```
/// info | Información
La primera versión es un "import relativo":
```Python
from .routers import items, users
```
La segunda versión es un "import absoluto":
```Python
from app.routers import items, users
```
Para aprender más sobre Paquetes y Módulos de Python, lee [la documentación oficial de Python sobre Módulos](https://docs.python.org/3/tutorial/modules.html).
///
### Evitar colisiones de nombres { #avoid-name-collisions }
Estamos importando el submódulo `items` directamente, en lugar de importar solo su variable `router`.
Esto se debe a que también tenemos otra variable llamada `router` en el submódulo `users`.
Si hubiéramos importado uno después del otro, como:
```Python
from .routers.items import router
from .routers.users import router
```
el `router` de `users` sobrescribiría el de `items` y no podríamos usarlos al mismo tiempo.
Así que, para poder usar ambos en el mismo archivo, importamos los submódulos directamente:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Incluir los `APIRouter`s para `users` y `items` { #include-the-apirouters-for-users-and-items }
Ahora, incluyamos los `router`s de los submódulos `users` y `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Información
`users.router` contiene el `APIRouter` dentro del archivo `app/routers/users.py`.
Y `items.router` contiene el `APIRouter` dentro del archivo `app/routers/items.py`.
///
Con `app.include_router()` podemos agregar cada `APIRouter` a la aplicación principal de `FastAPI`.
Incluirá todas las rutas de ese router como parte de ella.
/// note | Detalles Técnicos
En realidad creará internamente una *path operation* para cada *path operation* que fue declarada en el `APIRouter`.
Así, detrás de escena, funcionará como si todo fuera la misma única app.
///
/// check | Revisa
No tienes que preocuparte por el rendimiento al incluir routers.
Esto tomará microsegundos y solo sucederá al inicio.
Así que no afectará el rendimiento. ⚡
///
### Incluir un `APIRouter` con un `prefix`, `tags`, `responses`, y `dependencies` personalizados { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Ahora, imaginemos que tu organización te dio el archivo `app/internal/admin.py`.
Contiene un `APIRouter` con algunas *path operations* de administración que tu organización comparte entre varios proyectos.
Para este ejemplo será súper simple. Pero digamos que porque está compartido con otros proyectos en la organización, no podemos modificarlo y agregar un `prefix`, `dependencies`, `tags`, etc. directamente al `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Pero aún queremos configurar un `prefix` personalizado al incluir el `APIRouter` para que todas sus *path operations* comiencen con `/admin`, queremos asegurarlo con las `dependencies` que ya tenemos para este proyecto, y queremos incluir `tags` y `responses`.
Podemos declarar todo eso sin tener que modificar el `APIRouter` original pasando esos parámetros a `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
De esa manera, el `APIRouter` original permanecerá sin modificar, por lo que aún podemos compartir ese mismo archivo `app/internal/admin.py` con otros proyectos en la organización.
El resultado es que, en nuestra app, cada una de las *path operations* del módulo `admin` tendrá:
* El prefix `/admin`.
* El tag `admin`.
* La dependencia `get_token_header`.
* La response `418`. 🍵
Pero eso solo afectará a ese `APIRouter` en nuestra app, no en ningún otro código que lo utilice.
Así, por ejemplo, otros proyectos podrían usar el mismo `APIRouter` con un método de autenticación diferente.
### Incluir una *path operation* { #include-a-path-operation }
También podemos agregar *path operations* directamente a la app de `FastAPI`.
Aquí lo hacemos... solo para mostrar que podemos 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
y funcionará correctamente, junto con todas las otras *path operations* añadidas con `app.include_router()`.
/// info | Detalles Muy Técnicos
**Nota**: este es un detalle muy técnico que probablemente puedes **simplemente omitir**.
---
Los `APIRouter`s no están "montados", no están aislados del resto de la aplicación.
Esto se debe a que queremos incluir sus *path operations* en el esquema de OpenAPI y las interfaces de usuario.
Como no podemos simplemente aislarlos y "montarlos" independientemente del resto, las *path operations* se "clonan" (se vuelven a crear), no se incluyen directamente.
///
## Configurar el `entrypoint` en `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
Como tu objeto `app` de FastAPI vive en `app/main.py`, puedes configurar el `entrypoint` en tu archivo `pyproject.toml` así:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
que es equivalente a importar como:
```python
from app.main import app
```
De esa manera el comando `fastapi` sabrá dónde encontrar tu app.
/// Note | Nota
También podrías pasar la ruta al comando, como:
```console
$ fastapi dev app/main.py
```
Pero tendrías que recordar pasar la ruta correcta cada vez que llames al comando `fastapi`.
Además, otras herramientas podrían no ser capaces de encontrarla, por ejemplo la [Extensión de VS Code](../editor-support.md) o [FastAPI Cloud](https://fastapicloud.com), así que se recomienda usar el `entrypoint` en `pyproject.toml`.
///
## Revisa la documentación automática de la API { #check-the-automatic-api-docs }
Ahora, ejecuta tu app:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Y abre la documentación en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás la documentación automática de la API, incluyendo los paths de todos los submódulos, usando los paths correctos (y prefijos) y los tags correctos:
## Incluir el mismo router múltiples veces con diferentes `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
También puedes usar `.include_router()` múltiples veces con el *mismo* router usando diferentes prefijos.
Esto podría ser útil, por ejemplo, para exponer la misma API bajo diferentes prefijos, por ejemplo, `/api/v1` y `/api/latest`.
Este es un uso avanzado que quizás no necesites realmente, pero está allí en caso de que lo necesites.
## Incluir un `APIRouter` en otro { #include-an-apirouter-in-another }
De la misma manera que puedes incluir un `APIRouter` en una aplicación `FastAPI`, puedes incluir un `APIRouter` en otro `APIRouter` usando:
```Python
router.include_router(other_router)
```
Asegúrate de hacerlo antes de incluir `router` en la app de `FastAPI`, para que las *path operations* de `other_router` también se incluyan.
================================================
FILE: docs/es/docs/tutorial/body-fields.md
================================================
# Body - Campos { #body-fields }
De la misma manera que puedes declarar validaciones adicionales y metadatos en los parámetros de las *path operation function* con `Query`, `Path` y `Body`, puedes declarar validaciones y metadatos dentro de los modelos de Pydantic usando `Field` de Pydantic.
## Importar `Field` { #import-field }
Primero, tienes que importarlo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Advertencia
Fíjate que `Field` se importa directamente desde `pydantic`, no desde `fastapi` como el resto (`Query`, `Path`, `Body`, etc).
///
## Declarar atributos del modelo { #declare-model-attributes }
Después puedes utilizar `Field` con los atributos del modelo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` funciona de la misma manera que `Query`, `Path` y `Body`, tiene todos los mismos parámetros, etc.
/// note | Detalles técnicos
En realidad, `Query`, `Path` y otros que verás a continuación crean objetos de subclases de una clase común `Param`, que es a su vez una subclase de la clase `FieldInfo` de Pydantic.
Y `Field` de Pydantic también regresa una instance de `FieldInfo`.
`Body` también devuelve objetos de una subclase de `FieldInfo` directamente. Y hay otros que verás más adelante que son subclases de la clase `Body`.
Recuerda que cuando importas `Query`, `Path`, y otros desde `fastapi`, en realidad son funciones que devuelven clases especiales.
///
/// tip | Consejo
Observa cómo cada atributo del modelo con un tipo, un valor por defecto y `Field` tiene la misma estructura que un parámetro de una *path operation function*, con `Field` en lugar de `Path`, `Query` y `Body`.
///
## Agregar información extra { #add-extra-information }
Puedes declarar información extra en `Field`, `Query`, `Body`, etc. Y será incluida en el JSON Schema generado.
Aprenderás más sobre cómo agregar información extra más adelante en la documentación, cuando aprendamos a declarar ejemplos.
/// warning | Advertencia
Las claves extra pasadas a `Field` también estarán presentes en el esquema de OpenAPI resultante para tu aplicación.
Como estas claves no necesariamente tienen que ser parte de la especificación de OpenAPI, algunas herramientas de OpenAPI, por ejemplo [el validador de OpenAPI](https://validator.swagger.io/), podrían no funcionar con tu esquema generado.
///
## Resumen { #recap }
Puedes utilizar `Field` de Pydantic para declarar validaciones adicionales y metadatos para los atributos del modelo.
También puedes usar los argumentos de palabra clave extra para pasar metadatos adicionales del JSON Schema.
================================================
FILE: docs/es/docs/tutorial/body-multiple-params.md
================================================
# Cuerpo - Múltiples Parámetros { #body-multiple-parameters }
Ahora que hemos visto cómo usar `Path` y `Query`, veamos usos más avanzados de las declaraciones del request body.
## Mezclar `Path`, `Query` y parámetros del cuerpo { #mix-path-query-and-body-parameters }
Primero, por supuesto, puedes mezclar las declaraciones de parámetros de `Path`, `Query` y del request body libremente y **FastAPI** sabrá qué hacer.
Y también puedes declarar parámetros del cuerpo como opcionales, estableciendo el valor por defecto a `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Nota
Ten en cuenta que, en este caso, el `item` que se tomaría del cuerpo es opcional. Ya que tiene un valor por defecto de `None`.
///
## Múltiples parámetros del cuerpo { #multiple-body-parameters }
En el ejemplo anterior, las *path operations* esperarían un cuerpo JSON con los atributos de un `Item`, como:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Pero también puedes declarar múltiples parámetros del cuerpo, por ejemplo `item` y `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
En este caso, **FastAPI** notará que hay más de un parámetro del cuerpo en la función (hay dos parámetros que son modelos de Pydantic).
Entonces, usará los nombres de los parámetros como claves (nombres de campo) en el cuerpo, y esperará un cuerpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Nota
Ten en cuenta que aunque el `item` se declaró de la misma manera que antes, ahora se espera que esté dentro del cuerpo con una clave `item`.
///
**FastAPI** hará la conversión automática del request, de modo que el parámetro `item` reciba su contenido específico y lo mismo para `user`.
Realizará la validación de los datos compuestos, y los documentará así para el esquema de OpenAPI y la documentación automática.
## Valores singulares en el cuerpo { #singular-values-in-body }
De la misma manera que hay un `Query` y `Path` para definir datos extra para parámetros de query y path, **FastAPI** proporciona un equivalente `Body`.
Por ejemplo, ampliando el modelo anterior, podrías decidir que deseas tener otra clave `importance` en el mismo cuerpo, además de `item` y `user`.
Si lo declaras tal cual, debido a que es un valor singular, **FastAPI** asumirá que es un parámetro de query.
Pero puedes instruir a **FastAPI** para que lo trate como otra clave del cuerpo usando `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
En este caso, **FastAPI** esperará un cuerpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Nuevamente, convertirá los tipos de datos, validará, documentará, etc.
## Múltiples parámetros de cuerpo y query { #multiple-body-params-and-query }
Por supuesto, también puedes declarar parámetros adicionales de query siempre que lo necesites, además de cualquier parámetro del cuerpo.
Como, por defecto, los valores singulares se interpretan como parámetros de query, no tienes que añadir explícitamente un `Query`, solo puedes hacer:
```Python
q: str | None = None
```
Por ejemplo:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Información
`Body` también tiene todos los mismos parámetros de validación y metadatos extras que `Query`, `Path` y otros que verás luego.
///
## Embeber un solo parámetro de cuerpo { #embed-a-single-body-parameter }
Supongamos que solo tienes un único parámetro de cuerpo `item` de un modelo Pydantic `Item`.
Por defecto, **FastAPI** esperará su cuerpo directamente.
Pero si deseas que espere un JSON con una clave `item` y dentro de ella los contenidos del modelo, como lo hace cuando declaras parámetros de cuerpo extra, puedes usar el parámetro especial `Body` `embed`:
```Python
item: Item = Body(embed=True)
```
como en:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
En este caso, **FastAPI** esperará un cuerpo como:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
en lugar de:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Resumen { #recap }
Puedes añadir múltiples parámetros de cuerpo a tu *path operation function*, aunque un request solo puede tener un único cuerpo.
Pero **FastAPI** lo manejará, te dará los datos correctos en tu función, y validará y documentará el esquema correcto en la *path operation*.
También puedes declarar valores singulares para ser recibidos como parte del cuerpo.
Y puedes instruir a **FastAPI** para embeber el cuerpo en una clave incluso cuando solo hay un único parámetro declarado.
================================================
FILE: docs/es/docs/tutorial/body-nested-models.md
================================================
# Cuerpo - Modelos Anidados { #body-nested-models }
Con **FastAPI**, puedes definir, validar, documentar y usar modelos profundamente anidados de manera arbitraria (gracias a Pydantic).
## Campos de lista { #list-fields }
Puedes definir un atributo como un subtipo. Por ejemplo, una `list` en Python:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Esto hará que `tags` sea una lista, aunque no declare el tipo de los elementos de la lista.
## Campos de lista con parámetro de tipo { #list-fields-with-type-parameter }
Pero Python tiene una forma específica de declarar listas con tipos internos, o "parámetros de tipo":
### Declarar una `list` con un parámetro de tipo { #declare-a-list-with-a-type-parameter }
Para declarar tipos que tienen parámetros de tipo (tipos internos), como `list`, `dict`, `tuple`,
pasa el/los tipo(s) interno(s) como "parámetros de tipo" usando corchetes: `[` y `]`
```Python
my_list: list[str]
```
Eso es toda la sintaxis estándar de Python para declaraciones de tipo.
Usa esa misma sintaxis estándar para atributos de modelos con tipos internos.
Así, en nuestro ejemplo, podemos hacer que `tags` sea específicamente una "lista de strings":
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Tipos de conjunto { #set-types }
Pero luego pensamos en ello, y nos damos cuenta de que los tags no deberían repetirse, probablemente serían strings únicos.
Y Python tiene un tipo de datos especial para conjuntos de elementos únicos, el `set`.
Entonces podemos declarar `tags` como un conjunto de strings:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Con esto, incluso si recibes un request con datos duplicados, se convertirá en un conjunto de elementos únicos.
Y siempre que emitas esos datos, incluso si la fuente tenía duplicados, se emitirá como un conjunto de elementos únicos.
Y también se anotará/documentará en consecuencia.
## Modelos Anidados { #nested-models }
Cada atributo de un modelo Pydantic tiene un tipo.
Pero ese tipo puede ser en sí mismo otro modelo Pydantic.
Así que, puedes declarar "objetos" JSON anidados profundamente con nombres de atributos específicos, tipos y validaciones.
Todo eso, de manera arbitraria.
### Definir un submodelo { #define-a-submodel }
Por ejemplo, podemos definir un modelo `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Usar el submodelo como tipo { #use-the-submodel-as-a-type }
Y luego podemos usarlo como el tipo de un atributo:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Esto significaría que **FastAPI** esperaría un cuerpo similar a:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Nuevamente, haciendo solo esa declaración, con **FastAPI** obtienes:
* Soporte de editor (autocompletado, etc.), incluso para modelos anidados
* Conversión de datos
* Validación de datos
* Documentación automática
## Tipos especiales y validación { #special-types-and-validation }
Además de tipos singulares normales como `str`, `int`, `float`, etc., puedes usar tipos singulares más complejos que heredan de `str`.
Para ver todas las opciones que tienes, Revisa [Resumen de tipos de Pydantic](https://docs.pydantic.dev/latest/concepts/types/). Verás algunos ejemplos en el siguiente capítulo.
Por ejemplo, como en el modelo `Image` tenemos un campo `url`, podemos declararlo como una instance de `HttpUrl` de Pydantic en lugar de un `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
El string será verificado para ser una URL válida, y documentado en JSON Schema / OpenAPI como tal.
## Atributos con listas de submodelos { #attributes-with-lists-of-submodels }
También puedes usar modelos Pydantic como subtipos de `list`, `set`, etc.:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Esto esperará (convertirá, validará, documentará, etc.) un cuerpo JSON como:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Información
Nota cómo la clave `images` ahora tiene una lista de objetos de imagen.
///
## Modelos anidados profundamente { #deeply-nested-models }
Puedes definir modelos anidados tan profundamente como desees:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Información
Observa cómo `Offer` tiene una lista de `Item`s, que a su vez tienen una lista opcional de `Image`s
///
## Cuerpos de listas puras { #bodies-of-pure-lists }
Si el valor superior del cuerpo JSON que esperas es un `array` JSON (una `list` en Python), puedes declarar el tipo en el parámetro de la función, al igual que en los modelos Pydantic:
```Python
images: list[Image]
```
como en:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Soporte de editor en todas partes { #editor-support-everywhere }
Y obtienes soporte de editor en todas partes.
Incluso para elementos dentro de listas:
No podrías obtener este tipo de soporte de editor si estuvieras trabajando directamente con `dict` en lugar de modelos Pydantic.
Pero tampoco tienes que preocuparte por ellos, los `dicts` entrantes se convierten automáticamente y tu salida se convierte automáticamente a JSON también.
## Cuerpos de `dict`s arbitrarios { #bodies-of-arbitrary-dicts }
También puedes declarar un cuerpo como un `dict` con claves de algún tipo y valores de algún otro tipo.
De esta manera, no tienes que saber de antemano cuáles son los nombres válidos de campo/atributo (como sería el caso con modelos Pydantic).
Esto sería útil si deseas recibir claves que aún no conoces.
---
Otro caso útil es cuando deseas tener claves de otro tipo (por ejemplo, `int`).
Eso es lo que vamos a ver aquí.
En este caso, aceptarías cualquier `dict` siempre que tenga claves `int` con valores `float`:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Consejo
Ten en cuenta que JSON solo admite `str` como claves.
Pero Pydantic tiene conversión automática de datos.
Esto significa que, aunque tus clientes de API solo pueden enviar strings como claves, mientras esos strings contengan enteros puros, Pydantic los convertirá y validará.
Y el `dict` que recibas como `weights` tendrá realmente claves `int` y valores `float`.
///
## Resumen { #recap }
Con **FastAPI** tienes la máxima flexibilidad proporcionada por los modelos Pydantic, manteniendo tu código simple, corto y elegante.
Pero con todos los beneficios:
* Soporte de editor (¡autocompletado en todas partes!)
* Conversión de datos (también conocido como parsing/serialización)
* Validación de datos
* Documentación del esquema
* Documentación automática
================================================
FILE: docs/es/docs/tutorial/body-updates.md
================================================
# Body - Actualizaciones { #body-updates }
## Actualización reemplazando con `PUT` { #update-replacing-with-put }
Para actualizar un ítem puedes utilizar la operación de [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT).
Puedes usar el `jsonable_encoder` para convertir los datos de entrada en datos que se puedan almacenar como JSON (por ejemplo, con una base de datos NoSQL). Por ejemplo, convirtiendo `datetime` a `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` se usa para recibir datos que deben reemplazar los datos existentes.
### Advertencia sobre el reemplazo { #warning-about-replacing }
Esto significa que si quieres actualizar el ítem `bar` usando `PUT` con un body que contenga:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
debido a que no incluye el atributo ya almacenado `"tax": 20.2`, el modelo de entrada tomaría el valor por defecto de `"tax": 10.5`.
Y los datos se guardarían con ese "nuevo" `tax` de `10.5`.
## Actualizaciones parciales con `PATCH` { #partial-updates-with-patch }
También puedes usar la operación de [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) para actualizar *parcialmente* datos.
Esto significa que puedes enviar solo los datos que deseas actualizar, dejando el resto intacto.
/// note | Nota
`PATCH` es menos usado y conocido que `PUT`.
Y muchos equipos utilizan solo `PUT`, incluso para actualizaciones parciales.
Eres **libre** de usarlos como desees, **FastAPI** no impone ninguna restricción.
Pero esta guía te muestra, más o menos, cómo se pretende que se usen.
///
### Uso del parámetro `exclude_unset` de Pydantic { #using-pydantics-exclude-unset-parameter }
Si quieres recibir actualizaciones parciales, es muy útil usar el parámetro `exclude_unset` en el `.model_dump()` del modelo de Pydantic.
Como `item.model_dump(exclude_unset=True)`.
Eso generaría un `dict` solo con los datos que se establecieron al crear el modelo `item`, excluyendo los valores por defecto.
Luego puedes usar esto para generar un `dict` solo con los datos que se establecieron (enviados en el request), omitiendo los valores por defecto:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Uso del parámetro `update` de Pydantic { #using-pydantics-update-parameter }
Ahora, puedes crear una copia del modelo existente usando `.model_copy()`, y pasar el parámetro `update` con un `dict` que contenga los datos a actualizar.
Como `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Resumen de actualizaciones parciales { #partial-updates-recap }
En resumen, para aplicar actualizaciones parciales deberías:
* (Opcionalmente) usar `PATCH` en lugar de `PUT`.
* Recuperar los datos almacenados.
* Poner esos datos en un modelo de Pydantic.
* Generar un `dict` sin valores por defecto del modelo de entrada (usando `exclude_unset`).
* De esta manera puedes actualizar solo los valores realmente establecidos por el usuario, en lugar de sobrescribir valores ya almacenados con valores por defecto en tu modelo.
* Crear una copia del modelo almacenado, actualizando sus atributos con las actualizaciones parciales recibidas (usando el parámetro `update`).
* Convertir el modelo copiado en algo que pueda almacenarse en tu DB (por ejemplo, usando el `jsonable_encoder`).
* Esto es comparable a usar el método `.model_dump()` del modelo de nuevo, pero asegura (y convierte) los valores a tipos de datos que pueden convertirse a JSON, por ejemplo, `datetime` a `str`.
* Guardar los datos en tu DB.
* Devolver el modelo actualizado.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Consejo
Puedes realmente usar esta misma técnica con una operación HTTP `PUT`.
Pero el ejemplo aquí usa `PATCH` porque fue creado para estos casos de uso.
///
/// note | Nota
Observa que el modelo de entrada sigue siendo validado.
Entonces, si deseas recibir actualizaciones parciales que puedan omitir todos los atributos, necesitas tener un modelo con todos los atributos marcados como opcionales (con valores por defecto o `None`).
Para distinguir entre los modelos con todos los valores opcionales para **actualizaciones** y modelos con valores requeridos para **creación**, puedes utilizar las ideas descritas en [Modelos Extra](extra-models.md).
///
================================================
FILE: docs/es/docs/tutorial/body.md
================================================
# Request Body { #request-body }
Cuando necesitas enviar datos desde un cliente (digamos, un navegador) a tu API, los envías como un **request body**.
Un **request** body es un dato enviado por el cliente a tu API. Un **response** body es el dato que tu API envía al cliente.
Tu API casi siempre tiene que enviar un **response** body. Pero los clientes no necesariamente necesitan enviar **request bodies** todo el tiempo, a veces solo solicitan un path, quizás con algunos parámetros de query, pero no envían un body.
Para declarar un **request** body, usas modelos de [Pydantic](https://docs.pydantic.dev/) con todo su poder y beneficios.
/// info | Información
Para enviar datos, deberías usar uno de estos métodos: `POST` (el más común), `PUT`, `DELETE` o `PATCH`.
Enviar un body con un request `GET` tiene un comportamiento indefinido en las especificaciones, no obstante, es soportado por FastAPI, solo para casos de uso muy complejos/extremos.
Como no se recomienda, la documentación interactiva con Swagger UI no mostrará la documentación para el body cuando se usa `GET`, y los proxies intermedios podrían no soportarlo.
///
## Importar `BaseModel` de Pydantic { #import-pydantics-basemodel }
Primero, necesitas importar `BaseModel` de `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Crea tu modelo de datos { #create-your-data-model }
Luego, declaras tu modelo de datos como una clase que hereda de `BaseModel`.
Usa tipos estándar de Python para todos los atributos:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Al igual que al declarar parámetros de query, cuando un atributo del modelo tiene un valor por defecto, no es obligatorio. De lo contrario, es obligatorio. Usa `None` para hacerlo solo opcional.
Por ejemplo, el modelo anterior declara un “`object`” JSON (o `dict` en Python) como:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...dado que `description` y `tax` son opcionales (con un valor por defecto de `None`), este “`object`” JSON también sería válido:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Decláralo como un parámetro { #declare-it-as-a-parameter }
Para añadirlo a tu *path operation*, decláralo de la misma manera que declaraste parámetros de path y query:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...y declara su tipo como el modelo que creaste, `Item`.
## Resultados { #results }
Con solo esa declaración de tipo en Python, **FastAPI** hará lo siguiente:
* Leer el body del request como JSON.
* Convertir los tipos correspondientes (si es necesario).
* Validar los datos.
* Si los datos son inválidos, devolverá un error claro e indicado, señalando exactamente dónde y qué fue lo incorrecto.
* Proporcionar los datos recibidos en el parámetro `item`.
* Como lo declaraste en la función como de tipo `Item`, también tendrás todo el soporte del editor (autocompletado, etc.) para todos los atributos y sus tipos.
* Generar definiciones de [JSON Schema](https://json-schema.org) para tu modelo, que también puedes usar en cualquier otro lugar si tiene sentido para tu proyecto.
* Esos esquemas serán parte del esquema de OpenAPI generado y usados por las UIs de documentación automática.
## Documentación automática { #automatic-docs }
Los JSON Schemas de tus modelos serán parte del esquema OpenAPI generado y se mostrarán en la documentación API interactiva:
Y también se utilizarán en la documentación API dentro de cada *path operation* que los necesite:
## Soporte del editor { #editor-support }
En tu editor, dentro de tu función, obtendrás anotaciones de tipos y autocompletado en todas partes (esto no sucedería si recibieras un `dict` en lugar de un modelo de Pydantic):
También recibirás chequeos de errores para operaciones de tipo incorrecto:
No es por casualidad, todo el framework fue construido alrededor de ese diseño.
Y fue rigurosamente probado en la fase de diseño, antes de cualquier implementación, para garantizar que funcionaría con todos los editores.
Incluso se hicieron algunos cambios en Pydantic para admitir esto.
Las capturas de pantalla anteriores se tomaron con [Visual Studio Code](https://code.visualstudio.com).
Pero obtendrías el mismo soporte en el editor con [PyCharm](https://www.jetbrains.com/pycharm/) y la mayoría de los otros editores de Python:
/// tip | Consejo
Si usas [PyCharm](https://www.jetbrains.com/pycharm/) como tu editor, puedes usar el [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
Mejora el soporte del editor para modelos de Pydantic, con:
* autocompletado
* chequeo de tipos
* refactorización
* búsqueda
* inspecciones
///
## Usa el modelo { #use-the-model }
Dentro de la función, puedes acceder a todos los atributos del objeto modelo directamente:
{* ../../docs_src/body/tutorial002_py310.py *}
## Request body + parámetros de path { #request-body-path-parameters }
Puedes declarar parámetros de path y request body al mismo tiempo.
**FastAPI** reconocerá que los parámetros de función que coinciden con los parámetros de path deben ser **tomados del path**, y que los parámetros de función que se declaran como modelos de Pydantic deben ser **tomados del request body**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Request body + path + parámetros de query { #request-body-path-query-parameters }
También puedes declarar parámetros de **body**, **path** y **query**, todos al mismo tiempo.
**FastAPI** reconocerá cada uno de ellos y tomará los datos del lugar correcto.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Los parámetros de la función se reconocerán de la siguiente manera:
* Si el parámetro también se declara en el **path**, se utilizará como un parámetro de path.
* Si el parámetro es de un **tipo singular** (como `int`, `float`, `str`, `bool`, etc.), se interpretará como un parámetro de **query**.
* Si el parámetro se declara como del tipo de un **modelo de Pydantic**, se interpretará como un **body** de request.
/// note | Nota
FastAPI sabrá que el valor de `q` no es requerido debido al valor por defecto `= None`.
El `str | None` no es utilizado por FastAPI para determinar que el valor no es requerido, sabrá que no es requerido porque tiene un valor por defecto de `= None`.
Pero agregar las anotaciones de tipos permitirá que tu editor te brinde un mejor soporte y detecte errores.
///
## Sin Pydantic { #without-pydantic }
Si no quieres usar modelos de Pydantic, también puedes usar parámetros **Body**. Consulta la documentación para [Body - Múltiples parámetros: Valores singulares en el body](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/es/docs/tutorial/cookie-param-models.md
================================================
# Modelos de Cookies { #cookie-parameter-models }
Si tienes un grupo de **cookies** que están relacionadas, puedes crear un **modelo de Pydantic** para declararlas. 🍪
Esto te permitirá **reutilizar el modelo** en **múltiples lugares** y también declarar validaciones y metadatos para todos los parámetros a la vez. 😎
/// note | Nota
Esto es compatible desde la versión `0.115.0` de FastAPI. 🤓
///
/// tip | Consejo
Esta misma técnica se aplica a `Query`, `Cookie`, y `Header`. 😎
///
## Cookies con un Modelo de Pydantic { #cookies-with-a-pydantic-model }
Declara los parámetros de **cookie** que necesites en un **modelo de Pydantic**, y luego declara el parámetro como `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** **extraerá** los datos para **cada campo** de las **cookies** recibidas en el request y te entregará el modelo de Pydantic que definiste.
## Revisa la Documentación { #check-the-docs }
Puedes ver las cookies definidas en la UI de la documentación en `/docs`:
/// info | Información
Ten en cuenta que, como los **navegadores manejan las cookies** de maneras especiales y detrás de escenas, **no** permiten fácilmente que **JavaScript** las toque.
Si vas a la **UI de la documentación de la API** en `/docs` podrás ver la **documentación** de las cookies para tus *path operations*.
Pero incluso si **rellenas los datos** y haces clic en "Execute", como la UI de la documentación funciona con **JavaScript**, las cookies no serán enviadas y verás un **mensaje de error** como si no hubieras escrito ningún valor.
///
## Prohibir Cookies Extra { #forbid-extra-cookies }
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** las cookies que deseas recibir.
Tu API ahora tiene el poder de controlar su propio consentimiento de cookies. 🤪🍪
Puedes usar la configuración del modelo de Pydantic para `prohibir` cualquier campo `extra`:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Si un cliente intenta enviar algunas **cookies extra**, recibirán un response de **error**.
Pobres banners de cookies con todo su esfuerzo para obtener tu consentimiento para que la API lo rechace. 🍪
Por ejemplo, si el cliente intenta enviar una cookie `santa_tracker` con un valor de `good-list-please`, el cliente recibirá un response de **error** que le informa que la `santa_tracker` cookie no está permitida:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Resumen { #summary }
Puedes usar **modelos de Pydantic** para declarar **cookies** en **FastAPI**. 😎
================================================
FILE: docs/es/docs/tutorial/cookie-params.md
================================================
# Parámetros de Cookie { #cookie-parameters }
Puedes definir parámetros de Cookie de la misma manera que defines los parámetros `Query` y `Path`.
## Importar `Cookie` { #import-cookie }
Primero importa `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Declarar parámetros de `Cookie` { #declare-cookie-parameters }
Luego declara los parámetros de cookie usando la misma estructura que con `Path` y `Query`.
Puedes definir el valor por defecto así como toda la validación extra o los parámetros de anotación:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalles Técnicos
`Cookie` es una clase "hermana" de `Path` y `Query`. También hereda de la misma clase común `Param`.
Pero recuerda que cuando importas `Query`, `Path`, `Cookie` y otros desde `fastapi`, en realidad son funciones que devuelven clases especiales.
///
/// info | Información
Para declarar cookies, necesitas usar `Cookie`, porque de lo contrario los parámetros serían interpretados como parámetros de query.
///
/// info | Información
Ten en cuenta que, como **los navegadores manejan las cookies** de formas especiales y por detrás, **no** permiten fácilmente que **JavaScript** las toque.
Si vas a la **UI de la documentación de la API** en `/docs` podrás ver la **documentación** de cookies para tus *path operations*.
Pero incluso si **rellenas los datos** y haces clic en "Execute", como la UI de la documentación funciona con **JavaScript**, las cookies no se enviarán y verás un mensaje de **error** como si no hubieras escrito ningún valor.
///
## Resumen { #recap }
Declara cookies con `Cookie`, usando el mismo patrón común que `Query` y `Path`.
================================================
FILE: docs/es/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS o "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) se refiere a situaciones en las que un frontend que se ejecuta en un navegador tiene código JavaScript que se comunica con un backend, y el backend está en un "origen" diferente al frontend.
## Origen { #origin }
Un origen es la combinación de protocolo (`http`, `https`), dominio (`myapp.com`, `localhost`, `localhost.tiangolo.com`) y puerto (`80`, `443`, `8080`).
Así que, todos estos son orígenes diferentes:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Aunque todos están en `localhost`, usan protocolos o puertos diferentes, por lo tanto, son "orígenes" diferentes.
## Pasos { #steps }
Entonces, digamos que tienes un frontend corriendo en tu navegador en `http://localhost:8080`, y su JavaScript está tratando de comunicarse con un backend corriendo en `http://localhost` (porque no especificamos un puerto, el navegador asumirá el puerto por defecto `80`).
Entonces, el navegador enviará un request HTTP `OPTIONS` al backend `:80`, y si el backend envía los headers apropiados autorizando la comunicación desde este origen diferente (`http://localhost:8080`), entonces el navegador `:8080` permitirá que el JavaScript en el frontend envíe su request al backend `:80`.
Para lograr esto, el backend `:80` debe tener una lista de "orígenes permitidos".
En este caso, la lista tendría que incluir `http://localhost:8080` para que el frontend `:8080` funcione correctamente.
## Comodines { #wildcards }
También es posible declarar la lista como `"*"` (un "comodín") para decir que todos están permitidos.
Pero eso solo permitirá ciertos tipos de comunicación, excluyendo todo lo que implique credenciales: Cookies, headers de autorización como los utilizados con Bearer Tokens, etc.
Así que, para que todo funcione correctamente, es mejor especificar explícitamente los orígenes permitidos.
## Usa `CORSMiddleware` { #use-corsmiddleware }
Puedes configurarlo en tu aplicación **FastAPI** usando el `CORSMiddleware`.
* Importa `CORSMiddleware`.
* Crea una lista de orígenes permitidos (como strings).
* Agrégalo como un "middleware" a tu aplicación **FastAPI**.
También puedes especificar si tu backend permite:
* Credenciales (headers de autorización, cookies, etc).
* Métodos HTTP específicos (`POST`, `PUT`) o todos ellos con el comodín `"*"`.
* Headers HTTP específicos o todos ellos con el comodín `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Los parámetros predeterminados utilizados por la implementación de `CORSMiddleware` son restrictivos por defecto, por lo que necesitarás habilitar explícitamente orígenes, métodos o headers particulares para que los navegadores estén permitidos de usarlos en un contexto de Cross-Domain.
Se admiten los siguientes argumentos:
* `allow_origins` - Una lista de orígenes que deberían estar permitidos para hacer requests cross-origin. Por ejemplo, `['https://example.org', 'https://www.example.org']`. Puedes usar `['*']` para permitir cualquier origen.
* `allow_origin_regex` - Una cadena regex para coincidir con orígenes que deberían estar permitidos para hacer requests cross-origin. por ejemplo, `'https://.*\.example\.org'`.
* `allow_methods` - Una lista de métodos HTTP que deberían estar permitidos para requests cross-origin. Por defecto es `['GET']`. Puedes usar `['*']` para permitir todos los métodos estándar.
* `allow_headers` - Una lista de headers de request HTTP que deberían estar soportados para requests cross-origin. Por defecto es `[]`. Puedes usar `['*']` para permitir todos los headers. Los headers `Accept`, `Accept-Language`, `Content-Language` y `Content-Type` siempre están permitidos para [requests CORS simples](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Indica que las cookies deberían estar soportadas para requests cross-origin. Por defecto es `False`.
Ninguno de `allow_origins`, `allow_methods` y `allow_headers` puede establecerse a `['*']` si `allow_credentials` está configurado a `True`. Todos deben ser [especificados explícitamente](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Indica cualquier header de response que debería ser accesible para el navegador. Por defecto es `[]`.
* `max_age` - Establece un tiempo máximo en segundos para que los navegadores almacenen en caché los responses CORS. Por defecto es `600`.
El middleware responde a dos tipos particulares de request HTTP...
### Requests de preflight CORS { #cors-preflight-requests }
Estos son cualquier request `OPTIONS` con headers `Origin` y `Access-Control-Request-Method`.
En este caso, el middleware interceptará el request entrante y responderá con los headers CORS adecuados, y un response `200` o `400` con fines informativos.
### Requests simples { #simple-requests }
Cualquier request con un header `Origin`. En este caso, el middleware pasará el request a través de lo normal, pero incluirá los headers CORS adecuados en el response.
## Más info { #more-info }
Para más información sobre CORS, revisa la [documentación de CORS de Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Detalles Técnicos
También podrías usar `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** proporciona varios middlewares en `fastapi.middleware` como una conveniencia para ti, el desarrollador. Pero la mayoría de los middlewares disponibles provienen directamente de Starlette.
///
================================================
FILE: docs/es/docs/tutorial/debugging.md
================================================
# Depuración { #debugging }
Puedes conectar el depurador en tu editor, por ejemplo con Visual Studio Code o PyCharm.
## Llama a `uvicorn` { #call-uvicorn }
En tu aplicación de FastAPI, importa y ejecuta `uvicorn` directamente:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Acerca de `__name__ == "__main__"` { #about-name-main }
El objetivo principal de `__name__ == "__main__"` es tener algo de código que se ejecute cuando tu archivo es llamado con:
```console
$ python myapp.py
```
pero no es llamado cuando otro archivo lo importa, como en:
```Python
from myapp import app
```
#### Más detalles { #more-details }
Supongamos que tu archivo se llama `myapp.py`.
Si lo ejecutas con:
```console
$ python myapp.py
```
entonces la variable interna `__name__` en tu archivo, creada automáticamente por Python, tendrá como valor el string `"__main__"`.
Así que, la sección:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
se ejecutará.
---
Esto no ocurrirá si importas ese módulo (archivo).
Entonces, si tienes otro archivo `importer.py` con:
```Python
from myapp import app
# Algún código adicional
```
en ese caso, la variable creada automáticamente dentro de `myapp.py` no tendrá la variable `__name__` con un valor de `"__main__"`.
Así que, la línea:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
no se ejecutará.
/// info | Información
Para más información, revisa [la documentación oficial de Python](https://docs.python.org/3/library/__main__.html).
///
## Ejecuta tu código con tu depurador { #run-your-code-with-your-debugger }
Dado que estás ejecutando el servidor Uvicorn directamente desde tu código, puedes llamar a tu programa de Python (tu aplicación FastAPI) directamente desde el depurador.
---
Por ejemplo, en Visual Studio Code, puedes:
* Ir al panel de "Debug".
* "Add configuration...".
* Seleccionar "Python".
* Ejecutar el depurador con la opción "`Python: Current File (Integrated Terminal)`".
Luego, iniciará el servidor con tu código **FastAPI**, deteniéndose en tus puntos de interrupción, etc.
Así es como podría verse:
---
Si usas PyCharm, puedes:
* Abrir el menú "Run".
* Seleccionar la opción "Debug...".
* Luego aparece un menú contextual.
* Selecciona el archivo para depurar (en este caso, `main.py`).
Luego, iniciará el servidor con tu código **FastAPI**, deteniéndose en tus puntos de interrupción, etc.
Así es como podría verse:
================================================
FILE: docs/es/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Clases como dependencias { #classes-as-dependencies }
Antes de profundizar en el sistema de **Inyección de Dependencias**, vamos a mejorar el ejemplo anterior.
## Un `dict` del ejemplo anterior { #a-dict-from-the-previous-example }
En el ejemplo anterior, estábamos devolviendo un `dict` de nuestra dependencia ("dependable"):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Pero luego obtenemos un `dict` en el parámetro `commons` de la *path operation function*.
Y sabemos que los editores no pueden proporcionar mucho soporte (como autocompletado) para `dict`s, porque no pueden conocer sus claves y tipos de valor.
Podemos hacerlo mejor...
## Qué hace a una dependencia { #what-makes-a-dependency }
Hasta ahora has visto dependencias declaradas como funciones.
Pero esa no es la única forma de declarar dependencias (aunque probablemente sea la más común).
El factor clave es que una dependencia debe ser un "callable".
Un "**callable**" en Python es cualquier cosa que Python pueda "llamar" como una función.
Entonces, si tienes un objeto `something` (que podría _no_ ser una función) y puedes "llamarlo" (ejecutarlo) como:
```Python
something()
```
o
```Python
something(some_argument, some_keyword_argument="foo")
```
entonces es un "callable".
## Clases como dependencias { #classes-as-dependencies_1 }
Puedes notar que para crear una instance de una clase en Python, utilizas esa misma sintaxis.
Por ejemplo:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
En este caso, `fluffy` es una instance de la clase `Cat`.
Y para crear `fluffy`, estás "llamando" a `Cat`.
Entonces, una clase en Python también es un **callable**.
Entonces, en **FastAPI**, podrías usar una clase de Python como una dependencia.
Lo que **FastAPI** realmente comprueba es que sea un "callable" (función, clase o cualquier otra cosa) y los parámetros definidos.
Si pasas un "callable" como dependencia en **FastAPI**, analizará los parámetros de ese "callable", y los procesará de la misma manera que los parámetros de una *path operation function*. Incluyendo sub-dependencias.
Eso también se aplica a los callables sin parámetros. Igual que sería para *path operation functions* sin parámetros.
Entonces, podemos cambiar la dependencia "dependable" `common_parameters` de arriba a la clase `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Presta atención al método `__init__` usado para crear la instance de la clase:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...tiene los mismos parámetros que nuestros `common_parameters` anteriores:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Esos parámetros son los que **FastAPI** usará para "resolver" la dependencia.
En ambos casos, tendrá:
* Un parámetro de query `q` opcional que es un `str`.
* Un parámetro de query `skip` que es un `int`, con un valor por defecto de `0`.
* Un parámetro de query `limit` que es un `int`, con un valor por defecto de `100`.
En ambos casos, los datos serán convertidos, validados, documentados en el esquema de OpenAPI, etc.
## Úsalo { #use-it }
Ahora puedes declarar tu dependencia usando esta clase.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** llama a la clase `CommonQueryParams`. Esto crea una "instance" de esa clase y la instance será pasada como el parámetro `commons` a tu función.
## Anotación de tipos vs `Depends` { #type-annotation-vs-depends }
Nota cómo escribimos `CommonQueryParams` dos veces en el código anterior:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
El último `CommonQueryParams`, en:
```Python
... Depends(CommonQueryParams)
```
...es lo que **FastAPI** utilizará realmente para saber cuál es la dependencia.
Es a partir de este que **FastAPI** extraerá los parámetros declarados y es lo que **FastAPI** realmente llamará.
---
En este caso, el primer `CommonQueryParams`, en:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons: CommonQueryParams ...
```
////
...no tiene ningún significado especial para **FastAPI**. **FastAPI** no lo usará para la conversión de datos, validación, etc. (ya que está usando `Depends(CommonQueryParams)` para eso).
De hecho, podrías escribir simplemente:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...como en:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Pero declarar el tipo es recomendable, ya que de esa manera tu editor sabrá lo que se pasará como el parámetro `commons`, y entonces podrá ayudarte con el autocompletado, chequeo de tipos, etc:
## Atajo { #shortcut }
Pero ves que estamos teniendo algo de repetición de código aquí, escribiendo `CommonQueryParams` dos veces:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** proporciona un atajo para estos casos, en donde la dependencia es *específicamente* una clase que **FastAPI** "llamará" para crear una instance de la clase misma.
Para esos casos específicos, puedes hacer lo siguiente:
En lugar de escribir:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...escribes:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ sin `Annotated`
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Declaras la dependencia como el tipo del parámetro, y usas `Depends()` sin ningún parámetro, en lugar de tener que escribir la clase completa *otra vez* dentro de `Depends(CommonQueryParams)`.
El mismo ejemplo se vería entonces así:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...y **FastAPI** sabrá qué hacer.
/// tip | Consejo
Si eso parece más confuso que útil, ignóralo, no lo *necesitas*.
Es solo un atajo. Porque a **FastAPI** le importa ayudarte a minimizar la repetición de código.
///
================================================
FILE: docs/es/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Dependencias en decoradores de *path operation* { #dependencies-in-path-operation-decorators }
En algunos casos realmente no necesitas el valor de retorno de una dependencia dentro de tu *path operation function*.
O la dependencia no devuelve un valor.
Pero aún necesitas que sea ejecutada/resuelta.
Para esos casos, en lugar de declarar un parámetro de *path operation function* con `Depends`, puedes añadir una `list` de `dependencies` al decorador de *path operation*.
## Agregar `dependencies` al decorador de *path operation* { #add-dependencies-to-the-path-operation-decorator }
El decorador de *path operation* recibe un argumento opcional `dependencies`.
Debe ser una `list` de `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Estas dependencias serán ejecutadas/resueltas de la misma manera que las dependencias normales. Pero su valor (si devuelven alguno) no será pasado a tu *path operation function*.
/// tip | Consejo
Algunos editores revisan los parámetros de función no usados y los muestran como errores.
Usando estas `dependencies` en el decorador de *path operation* puedes asegurarte de que se ejecutan mientras evitas errores en editores/herramientas.
También puede ayudar a evitar confusiones para nuevos desarrolladores que vean un parámetro no usado en tu código y puedan pensar que es innecesario.
///
/// info | Información
En este ejemplo usamos headers personalizados inventados `X-Key` y `X-Token`.
Pero en casos reales, al implementar seguridad, obtendrías más beneficios usando las [Utilidades de Seguridad integradas (el próximo capítulo)](../security/index.md).
///
## Errores de dependencias y valores de retorno { #dependencies-errors-and-return-values }
Puedes usar las mismas *funciones* de dependencia que usas normalmente.
### Requisitos de dependencia { #dependency-requirements }
Pueden declarar requisitos de request (como headers) u otras sub-dependencias:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Lanzar excepciones { #raise-exceptions }
Estas dependencias pueden `raise` excepciones, igual que las dependencias normales:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Valores de retorno { #return-values }
Y pueden devolver valores o no, los valores no serán usados.
Así que, puedes reutilizar una dependencia normal (que devuelve un valor) que ya uses en otro lugar, y aunque el valor no se use, la dependencia será ejecutada:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Dependencias para un grupo de *path operations* { #dependencies-for-a-group-of-path-operations }
Más adelante, cuando leas sobre cómo estructurar aplicaciones más grandes ([Aplicaciones Más Grandes - Múltiples Archivos](../../tutorial/bigger-applications.md)), posiblemente con múltiples archivos, aprenderás cómo declarar un único parámetro `dependencies` para un grupo de *path operations*.
## Dependencias Globales { #global-dependencies }
A continuación veremos cómo añadir dependencias a toda la aplicación `FastAPI`, de modo que se apliquen a cada *path operation*.
================================================
FILE: docs/es/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Dependencias con yield { #dependencies-with-yield }
FastAPI admite dependencias que realizan algunos pasos adicionales después de finalizar.
Para hacer esto, usa `yield` en lugar de `return`, y escribe los pasos adicionales (código) después.
/// tip | Consejo
Asegúrate de usar `yield` una sola vez por dependencia.
///
/// note | Detalles técnicos
Cualquier función que sea válida para usar con:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) o
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
sería válida para usar como una dependencia en **FastAPI**.
De hecho, FastAPI usa esos dos decoradores internamente.
///
## Una dependencia de base de datos con `yield` { #a-database-dependency-with-yield }
Por ejemplo, podrías usar esto para crear una sesión de base de datos y cerrarla después de finalizar.
Solo el código anterior e incluyendo el `yield` statement se ejecuta antes de crear un response:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
El valor generado es lo que se inyecta en *path operations* y otras dependencias:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
El código posterior al `yield` statement se ejecuta después del response:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Consejo
Puedes usar funciones `async` o regulares.
**FastAPI** hará lo correcto con cada una, igual que con dependencias normales.
///
## Una dependencia con `yield` y `try` { #a-dependency-with-yield-and-try }
Si usas un bloque `try` en una dependencia con `yield`, recibirás cualquier excepción que se haya lanzado al usar la dependencia.
Por ejemplo, si algún código en algún punto intermedio, en otra dependencia o en una *path operation*, realiza un "rollback" en una transacción de base de datos o crea cualquier otro error, recibirás la excepción en tu dependencia.
Por lo tanto, puedes buscar esa excepción específica dentro de la dependencia con `except SomeException`.
Del mismo modo, puedes usar `finally` para asegurarte de que los pasos de salida se ejecuten, sin importar si hubo una excepción o no.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Sub-dependencias con `yield` { #sub-dependencies-with-yield }
Puedes tener sub-dependencias y "árboles" de sub-dependencias de cualquier tamaño y forma, y cualquiera o todas ellas pueden usar `yield`.
**FastAPI** se asegurará de que el "código de salida" en cada dependencia con `yield` se ejecute en el orden correcto.
Por ejemplo, `dependency_c` puede tener una dependencia de `dependency_b`, y `dependency_b` de `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
Y todas ellas pueden usar `yield`.
En este caso, `dependency_c`, para ejecutar su código de salida, necesita que el valor de `dependency_b` (aquí llamado `dep_b`) todavía esté disponible.
Y, a su vez, `dependency_b` necesita que el valor de `dependency_a` (aquí llamado `dep_a`) esté disponible para su código de salida.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
De la misma manera, podrías tener algunas dependencias con `yield` y otras dependencias con `return`, y hacer que algunas de esas dependan de algunas de las otras.
Y podrías tener una sola dependencia que requiera varias otras dependencias con `yield`, etc.
Puedes tener cualquier combinación de dependencias que quieras.
**FastAPI** se asegurará de que todo se ejecute en el orden correcto.
/// note | Detalles técnicos
Esto funciona gracias a los [Context Managers](https://docs.python.org/3/library/contextlib.html) de Python.
**FastAPI** los utiliza internamente para lograr esto.
///
## Dependencias con `yield` y `HTTPException` { #dependencies-with-yield-and-httpexception }
Viste que puedes usar dependencias con `yield` y tener bloques `try` que intentan ejecutar algo de código y luego ejecutar código de salida después de `finally`.
También puedes usar `except` para capturar la excepción que se lanzó y hacer algo con ella.
Por ejemplo, puedes lanzar una excepción diferente, como `HTTPException`.
/// tip | Consejo
Esta es una técnica algo avanzada, y en la mayoría de los casos realmente no la necesitarás, ya que puedes lanzar excepciones (incluyendo `HTTPException`) desde dentro del resto del código de tu aplicación, por ejemplo, en la *path operation function*.
Pero está ahí para ti si la necesitas. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Si quieres capturar excepciones y crear un response personalizado en base a eso, crea un [Manejador de Excepciones Personalizado](../handling-errors.md#install-custom-exception-handlers).
## Dependencias con `yield` y `except` { #dependencies-with-yield-and-except }
Si capturas una excepción usando `except` en una dependencia con `yield` y no la lanzas nuevamente (o lanzas una nueva excepción), FastAPI no podrá notar que hubo una excepción, al igual que sucedería con Python normal:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
En este caso, el cliente verá un response *HTTP 500 Internal Server Error* como debería, dado que no estamos lanzando una `HTTPException` o similar, pero el servidor **no tendrá ningún registro** ni ninguna otra indicación de cuál fue el error. 😱
### Siempre `raise` en Dependencias con `yield` y `except` { #always-raise-in-dependencies-with-yield-and-except }
Si capturas una excepción en una dependencia con `yield`, a menos que estés lanzando otra `HTTPException` o similar, **deberías volver a lanzar la excepción original**.
Puedes volver a lanzar la misma excepción usando `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Ahora el cliente obtendrá el mismo response *HTTP 500 Internal Server Error*, pero el servidor tendrá nuestro `InternalError` personalizado en los registros. 😎
## Ejecución de dependencias con `yield` { #execution-of-dependencies-with-yield }
La secuencia de ejecución es más o menos como este diagrama. El tiempo fluye de arriba a abajo. Y cada columna es una de las partes que interactúa o ejecuta código.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Puede lanzar excepciones, incluyendo HTTPException
client ->> dep: Iniciar request
Note over dep: Ejecutar código hasta yield
opt raise Exception
dep -->> handler: Lanzar Exception
handler -->> client: Response HTTP de error
end
dep ->> operation: Ejecutar dependencia, por ejemplo, sesión de BD
opt raise
operation -->> dep: Lanzar Exception (por ejemplo, HTTPException)
opt handle
dep -->> dep: Puede capturar excepción, lanzar una nueva HTTPException, lanzar otra excepción
end
handler -->> client: Response HTTP de error
end
operation ->> client: Devolver response al cliente
Note over client,operation: El response ya fue enviado, no se puede cambiar
opt Tasks
operation -->> tasks: Enviar tareas en background
end
opt Lanzar otra excepción
tasks -->> tasks: Manejar excepciones en el código de la tarea en background
end
```
/// info | Información
Solo **un response** será enviado al cliente. Podría ser uno de los responses de error o será el response de la *path operation*.
Después de que se envíe uno de esos responses, no se podrá enviar ningún otro response.
///
/// tip | Consejo
Si lanzas cualquier excepción en el código de la *path operation function*, se pasará a las dependencias con `yield`, incluyendo `HTTPException`. En la mayoría de los casos querrás volver a lanzar esa misma excepción o una nueva desde la dependencia con `yield` para asegurarte de que se maneje correctamente.
///
## Salida temprana y `scope` { #early-exit-and-scope }
Normalmente, el código de salida de las dependencias con `yield` se ejecuta **después de que el response** se envía al cliente.
Pero si sabes que no necesitarás usar la dependencia después de regresar de la *path operation function*, puedes usar `Depends(scope="function")` para decirle a FastAPI que debe cerrar la dependencia después de que la *path operation function* regrese, pero **antes** de que se envíe el **response**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` recibe un parámetro `scope` que puede ser:
* `"function"`: iniciar la dependencia antes de la *path operation function* que maneja el request, terminar la dependencia después de que termine la *path operation function*, pero **antes** de que el response se envíe de vuelta al cliente. Entonces, la función de dependencia se ejecutará **alrededor** de la *path operation **function***.
* `"request"`: iniciar la dependencia antes de la *path operation function* que maneja el request (similar a cuando se usa `"function"`), pero terminar **después** de que el response se envíe de vuelta al cliente. Entonces, la función de dependencia se ejecutará **alrededor** del **request** y del ciclo del response.
Si no se especifica y la dependencia tiene `yield`, tendrá un `scope` de `"request"` por defecto.
### `scope` para sub-dependencias { #scope-for-sub-dependencies }
Cuando declaras una dependencia con `scope="request"` (el valor por defecto), cualquier sub-dependencia también necesita tener un `scope` de `"request"`.
Pero una dependencia con `scope` de `"function"` puede tener dependencias con `scope` de `"function"` y `scope` de `"request"`.
Esto es porque cualquier dependencia necesita poder ejecutar su código de salida antes que las sub-dependencias, ya que podría necesitar seguir usándolas durante su código de salida.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en Background { #dependencies-with-yield-httpexception-except-and-background-tasks }
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y corregir algunos problemas.
Si quieres ver qué ha cambiado en diferentes versiones de FastAPI, puedes leer más al respecto en la guía avanzada, en [Dependencias avanzadas - Dependencias con `yield`, `HTTPException`, `except` y Tareas en Background](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Context Managers { #context-managers }
### Qué son los "Context Managers" { #what-are-context-managers }
Los "Context Managers" son aquellos objetos de Python que puedes usar en un `with` statement.
Por ejemplo, [puedes usar `with` para leer un archivo](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Internamente, `open("./somefile.txt")` crea un objeto llamado "Context Manager".
Cuando el bloque `with` termina, se asegura de cerrar el archivo, incluso si hubo excepciones.
Cuando creas una dependencia con `yield`, **FastAPI** creará internamente un context manager para ella y lo combinará con algunas otras herramientas relacionadas.
### Usando context managers en dependencias con `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Advertencia
Esto es, más o menos, una idea "avanzada".
Si apenas estás comenzando con **FastAPI**, podrías querer omitirlo por ahora.
///
En Python, puedes crear Context Managers [creando una clase con dos métodos: `__enter__()` y `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
También puedes usarlos dentro de las dependencias de **FastAPI** con `yield` usando
`with` o `async with` en la función de dependencia:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Consejo
Otra manera de crear un context manager es con:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) o
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
usándolos para decorar una función con un solo `yield`.
Eso es lo que **FastAPI** usa internamente para dependencias con `yield`.
Pero no tienes que usar los decoradores para las dependencias de FastAPI (y no deberías).
FastAPI lo hará por ti internamente.
///
================================================
FILE: docs/es/docs/tutorial/dependencies/global-dependencies.md
================================================
# Dependencias Globales { #global-dependencies }
Para algunos tipos de aplicaciones, podrías querer agregar dependencias a toda la aplicación.
Similar a como puedes [agregar `dependencies` a los *path operation decorators*](dependencies-in-path-operation-decorators.md), puedes agregarlos a la aplicación de `FastAPI`.
En ese caso, se aplicarán a todas las *path operations* en la aplicación:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Y todas las ideas en la sección sobre [agregar `dependencies` a los *path operation decorators*](dependencies-in-path-operation-decorators.md) siguen aplicándose, pero en este caso, a todas las *path operations* en la app.
## Dependencias para grupos de *path operations* { #dependencies-for-groups-of-path-operations }
Más adelante, al leer sobre cómo estructurar aplicaciones más grandes ([Aplicaciones Más Grandes - Múltiples Archivos](../../tutorial/bigger-applications.md)), posiblemente con múltiples archivos, aprenderás cómo declarar un solo parámetro de `dependencies` para un grupo de *path operations*.
================================================
FILE: docs/es/docs/tutorial/dependencies/index.md
================================================
# Dependencias { #dependencies }
**FastAPI** tiene un sistema de **Inyección de Dependencias** muy poderoso pero intuitivo.
Está diseñado para ser muy simple de usar, y para hacer que cualquier desarrollador integre otros componentes con **FastAPI** de forma muy sencilla.
## Qué es la "Inyección de Dependencias" { #what-is-dependency-injection }
**"Inyección de Dependencias"** significa, en programación, que hay una manera para que tu código (en este caso, tus *path operation functions*) declare las cosas que necesita para funcionar y utilizar: "dependencias".
Y luego, ese sistema (en este caso **FastAPI**) se encargará de hacer lo que sea necesario para proporcionar a tu código esas dependencias necesarias ("inyectar" las dependencias).
Esto es muy útil cuando necesitas:
* Tener lógica compartida (la misma lógica de código una y otra vez).
* Compartir conexiones a bases de datos.
* Imponer seguridad, autenticación, requisitos de roles, etc.
* Y muchas otras cosas...
Todo esto, mientras minimizas la repetición de código.
## Primeros Pasos { #first-steps }
Veamos un ejemplo muy simple. Será tan simple que no es muy útil, por ahora.
Pero de esta manera podemos enfocarnos en cómo funciona el sistema de **Inyección de Dependencias**.
### Crear una dependencia, o "dependable" { #create-a-dependency-or-dependable }
Primero enfoquémonos en la dependencia.
Es solo una función que puede tomar todos los mismos parámetros que una *path operation function* puede tomar:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Eso es todo.
**2 líneas**.
Y tiene la misma forma y estructura que todas tus *path operation functions*.
Puedes pensar en ella como una *path operation function* sin el "decorador" (sin el `@app.get("/some-path")`).
Y puede devolver lo que quieras.
En este caso, esta dependencia espera:
* Un parámetro de query opcional `q` que es un `str`.
* Un parámetro de query opcional `skip` que es un `int`, y por defecto es `0`.
* Un parámetro de query opcional `limit` que es un `int`, y por defecto es `100`.
Y luego solo devuelve un `dict` que contiene esos valores.
/// info | Información
FastAPI agregó soporte para `Annotated` (y comenzó a recomendarlo) en la versión 0.95.0.
Si tienes una versión anterior, obtendrás errores al intentar usar `Annotated`.
Asegúrate de [Actualizar la versión de FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) al menos a la 0.95.1 antes de usar `Annotated`.
///
### Importar `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Declarar la dependencia, en el "dependant" { #declare-the-dependency-in-the-dependant }
De la misma forma en que usas `Body`, `Query`, etc. con los parámetros de tu *path operation function*, usa `Depends` con un nuevo parámetro:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Aunque usas `Depends` en los parámetros de tu función de la misma manera que usas `Body`, `Query`, etc., `Depends` funciona un poco diferente.
Le das a `Depends` un solo parámetro.
Este parámetro debe ser algo como una función.
**No la llames** directamente (no agregues los paréntesis al final), solo pásala como un parámetro a `Depends()`.
Y esa función toma parámetros de la misma manera que las *path operation functions*.
/// tip | Consejo
Verás qué otras "cosas", además de funciones, pueden usarse como dependencias en el próximo capítulo.
///
Cada vez que llega un nuevo request, **FastAPI** se encargará de:
* Llamar a tu función de dependencia ("dependable") con los parámetros correctos.
* Obtener el resultado de tu función.
* Asignar ese resultado al parámetro en tu *path operation function*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
De esta manera escribes código compartido una vez y **FastAPI** se encarga de llamarlo para tus *path operations*.
/// check | Revisa
Nota que no tienes que crear una clase especial y pasarla en algún lugar a **FastAPI** para "registrarla" o algo similar.
Solo la pasas a `Depends` y **FastAPI** sabe cómo hacer el resto.
///
## Compartir dependencias `Annotated` { #share-annotated-dependencies }
En los ejemplos anteriores, ves que hay un poquito de **duplicación de código**.
Cuando necesitas usar la dependencia `common_parameters()`, tienes que escribir todo el parámetro con la anotación de tipo y `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Pero como estamos usando `Annotated`, podemos almacenar ese valor `Annotated` en una variable y usarlo en múltiples lugares:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Consejo
Esto es solo Python estándar, se llama un "alias de tipo", en realidad no es específico de **FastAPI**.
Pero porque **FastAPI** está basado en los estándares de Python, incluido `Annotated`, puedes usar este truco en tu código. 😎
///
Las dependencias seguirán funcionando como se esperaba, y la **mejor parte** es que la **información de tipo se preservará**, lo que significa que tu editor podrá seguir proporcionándote **autocompletado**, **errores en línea**, etc. Lo mismo para otras herramientas como `mypy`.
Esto será especialmente útil cuando lo uses en una **gran code base** donde uses **las mismas dependencias** una y otra vez en **muchas *path operations***.
## Usar `async` o no usar `async` { #to-async-or-not-to-async }
Como las dependencias también serán llamadas por **FastAPI** (lo mismo que tus *path operation functions*), las mismas reglas aplican al definir tus funciones.
Puedes usar `async def` o `def` normal.
Y puedes declarar dependencias con `async def` dentro de *path operation functions* normales `def`, o dependencias `def` dentro de *path operation functions* `async def`, etc.
No importa. **FastAPI** sabrá qué hacer.
/// note | Nota
Si no lo sabes, revisa la sección [Async: *"¿Con prisa?"*](../../async.md#in-a-hurry) sobre `async` y `await` en la documentación.
///
## Integración con OpenAPI { #integrated-with-openapi }
Todas las declaraciones de request, validaciones y requisitos de tus dependencias (y sub-dependencias) se integrarán en el mismo esquema de OpenAPI.
Así, la documentación interactiva tendrá toda la información de estas dependencias también:
## Uso simple { #simple-usage }
Si lo ves, las *path operation functions* se declaran para ser usadas siempre que un *path* y una *operación* coincidan, y luego **FastAPI** se encarga de llamar la función con los parámetros correctos, extrayendo los datos del request.
En realidad, todos (o la mayoría) de los frameworks web funcionan de esta misma manera.
Nunca llamas directamente a esas funciones. Son llamadas por tu framework (en este caso, **FastAPI**).
Con el sistema de Inyección de Dependencias, también puedes decirle a **FastAPI** que tu *path operation function* también "depende" de algo más que debe ejecutarse antes que tu *path operation function*, y **FastAPI** se encargará de ejecutarlo e "inyectar" los resultados.
Otros términos comunes para esta misma idea de "inyección de dependencias" son:
* recursos
* proveedores
* servicios
* inyectables
* componentes
## Plug-ins de **FastAPI** { #fastapi-plug-ins }
Las integraciones y "plug-ins" pueden construirse usando el sistema de **Inyección de Dependencias**. Pero, de hecho, en realidad **no hay necesidad de crear "plug-ins"**, ya que al usar dependencias es posible declarar una cantidad infinita de integraciones e interacciones que se vuelven disponibles para tus *path operation functions*.
Y las dependencias se pueden crear de una manera muy simple e intuitiva que te permite simplemente importar los paquetes de Python que necesitas, e integrarlos con tus funciones de API en un par de líneas de código, *literalmente*.
Verás ejemplos de esto en los próximos capítulos, sobre bases de datos relacionales y NoSQL, seguridad, etc.
## Compatibilidad de **FastAPI** { #fastapi-compatibility }
La simplicidad del sistema de inyección de dependencias hace que **FastAPI** sea compatible con:
* todas las bases de datos relacionales
* bases de datos NoSQL
* paquetes externos
* APIs externas
* sistemas de autenticación y autorización
* sistemas de monitoreo de uso de la API
* sistemas de inyección de datos de response
* etc.
## Simple y Poderoso { #simple-and-powerful }
Aunque el sistema de inyección de dependencias jerárquico es muy simple de definir y usar, sigue siendo muy poderoso.
Puedes definir dependencias que a su vez pueden definir dependencias ellas mismas.
Al final, se construye un árbol jerárquico de dependencias, y el sistema de **Inyección de Dependencias** se encarga de resolver todas estas dependencias por ti (y sus sub-dependencias) y proporcionar (inyectar) los resultados en cada paso.
Por ejemplo, digamos que tienes 4 endpoints de API (*path operations*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
entonces podrías agregar diferentes requisitos de permiso para cada uno de ellos solo con dependencias y sub-dependencias:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integrado con **OpenAPI** { #integrated-with-openapi_1 }
Todas estas dependencias, al declarar sus requisitos, también añaden parámetros, validaciones, etc. a tus *path operations*.
**FastAPI** se encargará de agregar todo al esquema de OpenAPI, para que se muestre en los sistemas de documentación interactiva.
================================================
FILE: docs/es/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Sub-dependencias { #sub-dependencies }
Puedes crear dependencias que tengan **sub-dependencias**.
Pueden ser tan **profundas** como necesites.
**FastAPI** se encargará de resolverlas.
## Primera dependencia "dependable" { #first-dependency-dependable }
Podrías crear una primera dependencia ("dependable") así:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Declara un parámetro de query opcional `q` como un `str`, y luego simplemente lo devuelve.
Esto es bastante simple (no muy útil), pero nos ayudará a centrarnos en cómo funcionan las sub-dependencias.
## Segunda dependencia, "dependable" y "dependant" { #second-dependency-dependable-and-dependant }
Luego puedes crear otra función de dependencia (un "dependable") que al mismo tiempo declare una dependencia propia (por lo que también es un "dependant"):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Centrémonos en los parámetros declarados:
* Aunque esta función es una dependencia ("dependable") en sí misma, también declara otra dependencia (depende de algo más).
* Depende del `query_extractor`, y asigna el valor que devuelve al parámetro `q`.
* También declara una `last_query` cookie opcional, como un `str`.
* Si el usuario no proporcionó ningún query `q`, usamos el último query utilizado, que guardamos previamente en una cookie.
## Usa la dependencia { #use-the-dependency }
Entonces podemos usar la dependencia con:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Información
Fíjate que solo estamos declarando una dependencia en la *path operation function*, `query_or_cookie_extractor`.
Pero **FastAPI** sabrá que tiene que resolver `query_extractor` primero, para pasar los resultados de eso a `query_or_cookie_extractor` al llamarlo.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Usando la misma dependencia múltiples veces { #using-the-same-dependency-multiple-times }
Si una de tus dependencias se declara varias veces para la misma *path operation*, por ejemplo, múltiples dependencias tienen una sub-dependencia común, **FastAPI** sabrá llamar a esa sub-dependencia solo una vez por request.
Y guardará el valor devuelto en un "caché" y lo pasará a todos los "dependants" que lo necesiten en ese request específico, en lugar de llamar a la dependencia varias veces para el mismo request.
En un escenario avanzado donde sabes que necesitas que la dependencia se llame en cada paso (posiblemente varias veces) en el mismo request en lugar de usar el valor "en caché", puedes establecer el parámetro `use_cache=False` al usar `Depends`:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ sin Anotaciones
/// tip | Consejo
Prefiere usar la versión `Annotated` si es posible.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Resumen { #recap }
Aparte de todas las palabras rimbombantes usadas aquí, el sistema de **Inyección de Dependencias** es bastante simple.
Solo son funciones que se ven igual que las *path operation functions*.
Pero aun así, es muy potente y te permite declarar "grafos" de dependencia anidados arbitrariamente profundos (árboles).
/// tip | Consejo
Todo esto podría no parecer tan útil con estos ejemplos simples.
Pero verás lo útil que es en los capítulos sobre **seguridad**.
Y también verás la cantidad de código que te ahorrará.
///
================================================
FILE: docs/es/docs/tutorial/encoder.md
================================================
# Codificador compatible con JSON { #json-compatible-encoder }
Hay algunos casos en los que podrías necesitar convertir un tipo de dato (como un modelo de Pydantic) a algo compatible con JSON (como un `dict`, `list`, etc).
Por ejemplo, si necesitas almacenarlo en una base de datos.
Para eso, **FastAPI** proporciona una función `jsonable_encoder()`.
## Usando el `jsonable_encoder` { #using-the-jsonable-encoder }
Imaginemos que tienes una base de datos `fake_db` que solo recibe datos compatibles con JSON.
Por ejemplo, no recibe objetos `datetime`, ya que no son compatibles con JSON.
Entonces, un objeto `datetime` tendría que ser convertido a un `str` que contenga los datos en [formato ISO](https://en.wikipedia.org/wiki/ISO_8601).
De la misma manera, esta base de datos no recibiría un modelo de Pydantic (un objeto con atributos), solo un `dict`.
Puedes usar `jsonable_encoder` para eso.
Recibe un objeto, como un modelo de Pydantic, y devuelve una versión compatible con JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
En este ejemplo, convertiría el modelo de Pydantic a un `dict`, y el `datetime` a un `str`.
El resultado de llamarlo es algo que puede ser codificado con la función estándar de Python [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
No devuelve un gran `str` que contenga los datos en formato JSON (como un string). Devuelve una estructura de datos estándar de Python (por ejemplo, un `dict`) con valores y sub-valores que son todos compatibles con JSON.
/// note | Nota
`jsonable_encoder` es utilizado internamente por **FastAPI** para convertir datos. Pero es útil en muchos otros escenarios.
///
================================================
FILE: docs/es/docs/tutorial/extra-data-types.md
================================================
# Tipos de Datos Extra { #extra-data-types }
Hasta ahora, has estado usando tipos de datos comunes, como:
* `int`
* `float`
* `str`
* `bool`
Pero también puedes usar tipos de datos más complejos.
Y seguirás teniendo las mismas funcionalidades como hasta ahora:
* Gran soporte de editor.
* Conversión de datos de requests entrantes.
* Conversión de datos para datos de response.
* Validación de datos.
* Anotación y documentación automática.
## Otros tipos de datos { #other-data-types }
Aquí hay algunos de los tipos de datos adicionales que puedes usar:
* `UUID`:
* Un "Identificador Universalmente Único" estándar, común como un ID en muchas bases de datos y sistemas.
* En requests y responses se representará como un `str`.
* `datetime.datetime`:
* Un `datetime.datetime` de Python.
* En requests y responses se representará como un `str` en formato ISO 8601, como: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* `datetime.date` de Python.
* En requests y responses se representará como un `str` en formato ISO 8601, como: `2008-09-15`.
* `datetime.time`:
* Un `datetime.time` de Python.
* En requests y responses se representará como un `str` en formato ISO 8601, como: `14:23:55.003`.
* `datetime.timedelta`:
* Un `datetime.timedelta` de Python.
* En requests y responses se representará como un `float` de segundos totales.
* Pydantic también permite representarlo como una "codificación de diferencia horaria ISO 8601", [consulta la documentación para más información](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* En requests y responses, tratado igual que un `set`:
* En requests, se leerá una list, eliminando duplicados y convirtiéndola en un `set`.
* En responses, el `set` se convertirá en una `list`.
* El esquema generado especificará que los valores del `set` son únicos (usando `uniqueItems` de JSON Schema).
* `bytes`:
* `bytes` estándar de Python.
* En requests y responses se tratará como `str`.
* El esquema generado especificará que es un `str` con `binary` como "format".
* `Decimal`:
* `Decimal` estándar de Python.
* En requests y responses, manejado igual que un `float`.
* Puedes revisar todos los tipos de datos válidos de Pydantic aquí: [Tipos de datos de Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
## Ejemplo { #example }
Aquí tienes un ejemplo de una *path operation* con parámetros usando algunos de los tipos anteriores.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Nota que los parámetros dentro de la función tienen su tipo de dato natural, y puedes, por ejemplo, realizar manipulaciones de fechas normales, como:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/es/docs/tutorial/extra-models.md
================================================
# Modelos Extra { #extra-models }
Continuando con el ejemplo anterior, será común tener más de un modelo relacionado.
Esto es especialmente el caso para los modelos de usuario, porque:
* El **modelo de entrada** necesita poder tener una contraseña.
* El **modelo de salida** no debería tener una contraseña.
* El **modelo de base de datos** probablemente necesitaría tener una contraseña hasheada.
/// danger | Peligro
Nunca almacenes contraseñas de usuarios en texto plano. Siempre almacena un "hash seguro" que puedas verificar luego.
Si no lo sabes, aprenderás qué es un "hash de contraseña" en los [capítulos de seguridad](security/simple-oauth2.md#password-hashing).
///
## Múltiples modelos { #multiple-models }
Aquí tienes una idea general de cómo podrían ser los modelos con sus campos de contraseña y los lugares donde se utilizan:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Acerca de `**user_in.model_dump()` { #about-user-in-model-dump }
#### `.model_dump()` de Pydantic { #pydantics-model-dump }
`user_in` es un modelo Pydantic de la clase `UserIn`.
Los modelos Pydantic tienen un método `.model_dump()` que devuelve un `dict` con los datos del modelo.
Así que, si creamos un objeto Pydantic `user_in` como:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
y luego llamamos a:
```Python
user_dict = user_in.model_dump()
```
ahora tenemos un `dict` con los datos en la variable `user_dict` (es un `dict` en lugar de un objeto modelo Pydantic).
Y si llamamos a:
```Python
print(user_dict)
```
obtendríamos un `dict` de Python con:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Desempaquetando un `dict` { #unpacking-a-dict }
Si tomamos un `dict` como `user_dict` y lo pasamos a una función (o clase) con `**user_dict`, Python lo "desempaquetará". Pasará las claves y valores del `user_dict` directamente como argumentos clave-valor.
Así que, continuando con el `user_dict` anterior, escribir:
```Python
UserInDB(**user_dict)
```
sería equivalente a algo como:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
O más exactamente, usando `user_dict` directamente, con cualquier contenido que pueda tener en el futuro:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Un modelo Pydantic a partir del contenido de otro { #a-pydantic-model-from-the-contents-of-another }
Como en el ejemplo anterior obtuvimos `user_dict` de `user_in.model_dump()`, este código:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
sería equivalente a:
```Python
UserInDB(**user_in.model_dump())
```
...porque `user_in.model_dump()` es un `dict`, y luego hacemos que Python lo "desempaquete" al pasarlo a `UserInDB` con el prefijo `**`.
Así, obtenemos un modelo Pydantic a partir de los datos en otro modelo Pydantic.
#### Desempaquetando un `dict` y palabras clave adicionales { #unpacking-a-dict-and-extra-keywords }
Y luego agregando el argumento de palabra clave adicional `hashed_password=hashed_password`, como en:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...termina siendo como:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Advertencia
Las funciones adicionales de soporte `fake_password_hasher` y `fake_save_user` son solo para demostrar un posible flujo de datos, pero por supuesto no proporcionan ninguna seguridad real.
///
## Reducir duplicación { #reduce-duplication }
Reducir la duplicación de código es una de las ideas centrales en **FastAPI**.
Ya que la duplicación de código incrementa las posibilidades de bugs, problemas de seguridad, problemas de desincronización de código (cuando actualizas en un lugar pero no en los otros), etc.
Y estos modelos están compartiendo muchos de los datos y duplicando nombres y tipos de atributos.
Podríamos hacerlo mejor.
Podemos declarar un modelo `UserBase` que sirva como base para nuestros otros modelos. Y luego podemos hacer subclases de ese modelo que heredan sus atributos (declaraciones de tipos, validación, etc).
Toda la conversión de datos, validación, documentación, etc. seguirá funcionando normalmente.
De esa manera, podemos declarar solo las diferencias entre los modelos (con `password` en texto plano, con `hashed_password` y sin contraseña):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` o `anyOf` { #union-or-anyof }
Puedes declarar un response que sea la `Union` de dos o más tipos, eso significa que el response sería cualquiera de ellos.
Se definirá en OpenAPI con `anyOf`.
Para hacerlo, usa la anotación de tipos estándar de Python [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note | Nota
Al definir una [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions), incluye el tipo más específico primero, seguido por el tipo menos específico. En el ejemplo a continuación, el más específico `PlaneItem` viene antes de `CarItem` en `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` en Python 3.10 { #union-in-python-3-10 }
En este ejemplo pasamos `Union[PlaneItem, CarItem]` como el valor del argumento `response_model`.
Porque lo estamos pasando como un **valor a un argumento** en lugar de ponerlo en una **anotación de tipos**, tenemos que usar `Union` incluso en Python 3.10.
Si estuviera en una anotación de tipos podríamos haber usado la barra vertical, como:
```Python
some_variable: PlaneItem | CarItem
```
Pero si ponemos eso en la asignación `response_model=PlaneItem | CarItem` obtendríamos un error, porque Python intentaría realizar una **operación inválida** entre `PlaneItem` y `CarItem` en lugar de interpretar eso como una anotación de tipos.
## Lista de modelos { #list-of-models }
De la misma manera, puedes declarar responses de listas de objetos.
Para eso, usa la `list` estándar de Python:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Response con `dict` arbitrario { #response-with-arbitrary-dict }
También puedes declarar un response usando un `dict` arbitrario plano, declarando solo el tipo de las claves y valores, sin usar un modelo Pydantic.
Esto es útil si no conoces los nombres de los campos/atributos válidos (que serían necesarios para un modelo Pydantic) de antemano.
En este caso, puedes usar `dict`:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Recapitulación { #recap }
Usa múltiples modelos Pydantic y hereda libremente para cada caso.
No necesitas tener un solo modelo de datos por entidad si esa entidad debe poder tener diferentes "estados". Como el caso con la "entidad" usuario con un estado que incluye `password`, `password_hash` y sin contraseña.
================================================
FILE: docs/es/docs/tutorial/first-steps.md
================================================
# Primeros Pasos { #first-steps }
El archivo FastAPI más simple podría verse así:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copia eso en un archivo `main.py`.
Ejecuta el servidor en vivo:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
En el resultado, hay una línea con algo como:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Esa línea muestra la URL donde tu aplicación está siendo servida, en tu máquina local.
### Revisa { #check-it }
Abre tu navegador en [http://127.0.0.1:8000](http://127.0.0.1:8000).
Verás el response JSON como:
```JSON
{"message": "Hello World"}
```
### Documentación interactiva de la API { #interactive-api-docs }
Ahora ve a [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás la documentación interactiva automática de la API (proporcionada por [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Documentación alternativa de la API { #alternative-api-docs }
Y ahora, ve a [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Verás la documentación alternativa automática (proporcionada por [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
**FastAPI** genera un "esquema" con toda tu API utilizando el estándar **OpenAPI** para definir APIs.
#### "Esquema" { #schema }
Un "esquema" es una definición o descripción de algo. No el código que lo implementa, sino solo una descripción abstracta.
#### Esquema de la API { #api-schema }
En este caso, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) es una especificación que dicta cómo definir un esquema de tu API.
Esta definición de esquema incluye los paths de tu API, los posibles parámetros que toman, etc.
#### Esquema de Datos { #data-schema }
El término "esquema" también podría referirse a la forma de algunos datos, como el contenido JSON.
En ese caso, significaría los atributos del JSON, los tipos de datos que tienen, etc.
#### OpenAPI y JSON Schema { #openapi-and-json-schema }
OpenAPI define un esquema de API para tu API. Y ese esquema incluye definiciones (o "esquemas") de los datos enviados y recibidos por tu API utilizando **JSON Schema**, el estándar para esquemas de datos JSON.
#### Revisa el `openapi.json` { #check-the-openapi-json }
Si tienes curiosidad por cómo se ve el esquema OpenAPI en bruto, FastAPI automáticamente genera un JSON (esquema) con las descripciones de toda tu API.
Puedes verlo directamente en: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Mostrará un JSON que empieza con algo como:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Para qué sirve OpenAPI { #what-is-openapi-for }
El esquema OpenAPI es lo que impulsa los dos sistemas de documentación interactiva incluidos.
Y hay docenas de alternativas, todas basadas en OpenAPI. Podrías añadir fácilmente cualquiera de esas alternativas a tu aplicación construida con **FastAPI**.
También podrías usarlo para generar código automáticamente, para clientes que se comuniquen con tu API. Por ejemplo, aplicaciones frontend, móviles o IoT.
### Configura el `entrypoint` de la app en `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Puedes configurar dónde está tu app en un archivo `pyproject.toml` así:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Ese `entrypoint` le dirá al comando `fastapi` que debe hacer el import de la app así:
```python
from main import app
```
Si tu código estuviera estructurado así:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Entonces pondrías el `entrypoint` como:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
lo cual sería equivalente a:
```python
from backend.main import app
```
### `fastapi dev` con path { #fastapi-dev-with-path }
También puedes pasar el path del archivo al comando `fastapi dev`, y adivinará el objeto app de FastAPI que debe usar:
```console
$ fastapi dev main.py
```
Pero tendrías que recordar pasar el path correcto cada vez que llames al comando `fastapi`.
Además, otras herramientas podrían no ser capaces de encontrarlo, por ejemplo la [Extensión de VS Code](../editor-support.md) o [FastAPI Cloud](https://fastapicloud.com), así que se recomienda usar el `entrypoint` en `pyproject.toml`.
### Despliega tu app (opcional) { #deploy-your-app-optional }
Opcionalmente puedes desplegar tu app de FastAPI en [FastAPI Cloud](https://fastapicloud.com), ve y únete a la lista de espera si aún no lo has hecho. 🚀
Si ya tienes una cuenta de **FastAPI Cloud** (te invitamos desde la lista de espera 😉), puedes desplegar tu aplicación con un solo comando.
Antes de desplegar, asegúrate de haber iniciado sesión:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Luego despliega tu app:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
¡Eso es todo! Ahora puedes acceder a tu app en esa URL. ✨
## Recapitulación, paso a paso { #recap-step-by-step }
### Paso 1: importa `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` es una clase de Python que proporciona toda la funcionalidad para tu API.
/// note | Detalles técnicos
`FastAPI` es una clase que hereda directamente de `Starlette`.
Puedes usar toda la funcionalidad de [Starlette](https://www.starlette.dev/) con `FastAPI` también.
///
### Paso 2: crea una "instance" de `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Aquí la variable `app` será una "instance" de la clase `FastAPI`.
Este será el punto principal de interacción para crear toda tu API.
### Paso 3: crea una *path operation* { #step-3-create-a-path-operation }
#### Path { #path }
"Path" aquí se refiere a la última parte de la URL empezando desde la primera `/`.
Así que, en una URL como:
```
https://example.com/items/foo
```
...el path sería:
```
/items/foo
```
/// info | Información
Un "path" también es comúnmente llamado "endpoint" o "ruta".
///
Mientras construyes una API, el "path" es la forma principal de separar "concerns" y "resources".
#### Operación { #operation }
"Operación" aquí se refiere a uno de los "métodos" HTTP.
Uno de:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...y los más exóticos:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
En el protocolo HTTP, puedes comunicarte con cada path usando uno (o más) de estos "métodos".
---
Al construir APIs, normalmente usas estos métodos HTTP específicos para realizar una acción específica.
Normalmente usas:
* `POST`: para crear datos.
* `GET`: para leer datos.
* `PUT`: para actualizar datos.
* `DELETE`: para eliminar datos.
Así que, en OpenAPI, cada uno de los métodos HTTP se llama una "operation".
Vamos a llamarlas "**operaciones**" también.
#### Define un *path operation decorator* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
El `@app.get("/")` le dice a **FastAPI** que la función justo debajo se encarga de manejar requests que vayan a:
* el path `/`
* usando una get operación
/// info | Información sobre `@decorator`
Esa sintaxis `@algo` en Python se llama un "decorador".
Lo pones encima de una función. Como un bonito sombrero decorativo (supongo que de ahí viene el término).
Un "decorador" toma la función de abajo y hace algo con ella.
En nuestro caso, este decorador le dice a **FastAPI** que la función de abajo corresponde al **path** `/` con una **operation** `get`.
Es el "**path operation decorator**".
///
También puedes usar las otras operaciones:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Y los más exóticos:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Consejo
Eres libre de usar cada operación (método HTTP) como quieras.
**FastAPI** no fuerza ningún significado específico.
La información aquí se presenta como una guía, no un requisito.
Por ejemplo, cuando usas GraphQL normalmente realizas todas las acciones usando solo operaciones `POST`.
///
### Paso 4: define la **path operation function** { #step-4-define-the-path-operation-function }
Esta es nuestra "**path operation function**":
* **path**: es `/`.
* **operation**: es `get`.
* **function**: es la función debajo del "decorador" (debajo de `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Esta es una función de Python.
Será llamada por **FastAPI** cuando reciba un request en la URL "`/`" usando una operación `GET`.
En este caso, es una función `async`.
---
También podrías definirla como una función normal en lugar de `async def`:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Nota
Si no sabes la diferencia, Revisa la sección [Async: *"¿Tienes prisa?"*](../async.md#in-a-hurry).
///
### Paso 5: retorna el contenido { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Puedes retornar un `dict`, `list`, valores singulares como `str`, `int`, etc.
También puedes retornar modelos de Pydantic (verás más sobre eso más adelante).
Hay muchos otros objetos y modelos que serán automáticamente convertidos a JSON (incluyendo ORMs, etc). Intenta usar tus favoritos, es altamente probable que ya sean compatibles.
### Paso 6: Despliégalo { #step-6-deploy-it }
Despliega tu app en **[FastAPI Cloud](https://fastapicloud.com)** con un solo comando: `fastapi deploy`. 🎉
#### Sobre FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** está construido por el mismo autor y equipo detrás de **FastAPI**.
Agiliza el proceso de **construir**, **desplegar** y **acceder** a una API con el mínimo esfuerzo.
Trae la misma **experiencia de desarrollador** de construir apps con FastAPI a **desplegarlas** en la nube. 🎉
FastAPI Cloud es el sponsor principal y proveedor de financiación para los proyectos open source de *FastAPI and friends*. ✨
#### Despliega en otros proveedores cloud { #deploy-to-other-cloud-providers }
FastAPI es open source y basado en estándares. Puedes desplegar apps de FastAPI en cualquier proveedor cloud que elijas.
Sigue las guías de tu proveedor cloud para desplegar apps de FastAPI con ellos. 🤓
## Recapitulación { #recap }
* Importa `FastAPI`.
* Crea una instance `app`.
* Escribe un **path operation decorator** usando decoradores como `@app.get("/")`.
* Define una **path operation function**; por ejemplo, `def root(): ...`.
* Ejecuta el servidor de desarrollo usando el comando `fastapi dev`.
* Opcionalmente, despliega tu app con `fastapi deploy`.
================================================
FILE: docs/es/docs/tutorial/handling-errors.md
================================================
# Manejo de Errores { #handling-errors }
Existen muchas situaciones en las que necesitas notificar un error a un cliente que está usando tu API.
Este cliente podría ser un navegador con un frontend, un código de otra persona, un dispositivo IoT, etc.
Podrías necesitar decirle al cliente que:
* El cliente no tiene suficientes privilegios para esa operación.
* El cliente no tiene acceso a ese recurso.
* El ítem al que el cliente intentaba acceder no existe.
* etc.
En estos casos, normalmente devolverías un **código de estado HTTP** en el rango de **400** (de 400 a 499).
Esto es similar a los códigos de estado HTTP 200 (de 200 a 299). Esos códigos de estado "200" significan que de alguna manera hubo un "éxito" en el request.
Los códigos de estado en el rango de 400 significan que hubo un error por parte del cliente.
¿Recuerdas todos esos errores de **"404 Not Found"** (y chistes)?
## Usa `HTTPException` { #use-httpexception }
Para devolver responses HTTP con errores al cliente, usa `HTTPException`.
### Importa `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Lanza un `HTTPException` en tu código { #raise-an-httpexception-in-your-code }
`HTTPException` es una excepción de Python normal con datos adicionales relevantes para APIs.
Debido a que es una excepción de Python, no la `return`, sino que la `raise`.
Esto también significa que si estás dentro de una función de utilidad que estás llamando dentro de tu *path operation function*, y lanzas el `HTTPException` desde dentro de esa función de utilidad, no se ejecutará el resto del código en la *path operation function*, terminará ese request de inmediato y enviará el error HTTP del `HTTPException` al cliente.
El beneficio de lanzar una excepción en lugar de `return`ar un valor será más evidente en la sección sobre Dependencias y Seguridad.
En este ejemplo, cuando el cliente solicita un ítem por un ID que no existe, lanza una excepción con un código de estado de `404`:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### El response resultante { #the-resulting-response }
Si el cliente solicita `http://example.com/items/foo` (un `item_id` `"foo"`), ese cliente recibirá un código de estado HTTP de 200, y un response JSON de:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Pero si el cliente solicita `http://example.com/items/bar` (un `item_id` inexistente `"bar"`), ese cliente recibirá un código de estado HTTP de 404 (el error "no encontrado"), y un response JSON de:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Consejo
Cuando lanzas un `HTTPException`, puedes pasar cualquier valor que pueda convertirse a JSON como el parámetro `detail`, no solo `str`.
Podrías pasar un `dict`, un `list`, etc.
Son manejados automáticamente por **FastAPI** y convertidos a JSON.
///
## Agrega headers personalizados { #add-custom-headers }
Existen algunas situaciones en las que es útil poder agregar headers personalizados al error HTTP. Por ejemplo, para algunos tipos de seguridad.
Probablemente no necesitarás usarlos directamente en tu código.
Pero en caso de que los necesites para un escenario avanzado, puedes agregar headers personalizados:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Instalar manejadores de excepciones personalizados { #install-custom-exception-handlers }
Puedes agregar manejadores de excepciones personalizados con [las mismas utilidades de excepciones de Starlette](https://www.starlette.dev/exceptions/).
Supongamos que tienes una excepción personalizada `UnicornException` que tú (o un paquete que usas) podrías lanzar.
Y quieres manejar esta excepción globalmente con FastAPI.
Podrías agregar un manejador de excepciones personalizado con `@app.exception_handler()`:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Aquí, si solicitas `/unicorns/yolo`, la *path operation* lanzará un `UnicornException`.
Pero será manejado por el `unicorn_exception_handler`.
Así que recibirás un error limpio, con un código de estado HTTP de `418` y un contenido JSON de:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Nota Técnica
También podrías usar `from starlette.requests import Request` y `from starlette.responses import JSONResponse`.
**FastAPI** ofrece las mismas `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette. Lo mismo con `Request`.
///
## Sobrescribir los manejadores de excepciones predeterminados { #override-the-default-exception-handlers }
**FastAPI** tiene algunos manejadores de excepciones predeterminados.
Estos manejadores se encargan de devolver los responses JSON predeterminadas cuando lanzas un `HTTPException` y cuando el request tiene datos inválidos.
Puedes sobrescribir estos manejadores de excepciones con los tuyos propios.
### Sobrescribir excepciones de validación de request { #override-request-validation-exceptions }
Cuando un request contiene datos inválidos, **FastAPI** lanza internamente un `RequestValidationError`.
Y también incluye un manejador de excepciones predeterminado para ello.
Para sobrescribirlo, importa el `RequestValidationError` y úsalo con `@app.exception_handler(RequestValidationError)` para decorar el manejador de excepciones.
El manejador de excepciones recibirá un `Request` y la excepción.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Ahora, si vas a `/items/foo`, en lugar de obtener el error JSON por defecto con:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
obtendrás una versión en texto, con:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Sobrescribir el manejador de errores de `HTTPException` { #override-the-httpexception-error-handler }
De la misma manera, puedes sobrescribir el manejador de `HTTPException`.
Por ejemplo, podrías querer devolver un response de texto plano en lugar de JSON para estos errores:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Nota Técnica
También podrías usar `from starlette.responses import PlainTextResponse`.
**FastAPI** ofrece las mismas `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette.
///
/// warning | Advertencia
Ten en cuenta que `RequestValidationError` contiene la información del nombre de archivo y la línea donde ocurre el error de validación, para que puedas mostrarla en tus logs con la información relevante si quieres.
Pero eso significa que si simplemente lo conviertes a un string y devuelves esa información directamente, podrías estar filtrando un poquito de información sobre tu sistema, por eso aquí el código extrae y muestra cada error de forma independiente.
///
### Usar el body de `RequestValidationError` { #use-the-requestvalidationerror-body }
El `RequestValidationError` contiene el `body` que recibió con datos inválidos.
Podrías usarlo mientras desarrollas tu aplicación para registrar el body y depurarlo, devolverlo al usuario, etc.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Ahora intenta enviar un ítem inválido como:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Recibirás un response que te dirá que los datos son inválidos conteniendo el body recibido:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### `HTTPException` de FastAPI vs `HTTPException` de Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** tiene su propio `HTTPException`.
Y la clase de error `HTTPException` de **FastAPI** hereda de la clase de error `HTTPException` de Starlette.
La única diferencia es que el `HTTPException` de **FastAPI** acepta cualquier dato JSON-able para el campo `detail`, mientras que el `HTTPException` de Starlette solo acepta strings para ello.
Así que puedes seguir lanzando un `HTTPException` de **FastAPI** como de costumbre en tu código.
Pero cuando registras un manejador de excepciones, deberías registrarlo para el `HTTPException` de Starlette.
De esta manera, si alguna parte del código interno de Starlette, o una extensión o plug-in de Starlette, lanza un `HTTPException` de Starlette, tu manejador podrá capturarlo y manejarlo.
En este ejemplo, para poder tener ambos `HTTPException` en el mismo código, las excepciones de Starlette son renombradas a `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Reutilizar los manejadores de excepciones de **FastAPI** { #reuse-fastapis-exception-handlers }
Si quieres usar la excepción junto con los mismos manejadores de excepciones predeterminados de **FastAPI**, puedes importar y reutilizar los manejadores de excepciones predeterminados de `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
En este ejemplo solo estás `print`eando el error con un mensaje muy expresivo, pero te haces una idea. Puedes usar la excepción y luego simplemente reutilizar los manejadores de excepciones predeterminados.
================================================
FILE: docs/es/docs/tutorial/header-param-models.md
================================================
# Modelos de Parámetros de Header { #header-parameter-models }
Si tienes un grupo de **parámetros de header** relacionados, puedes crear un **modelo Pydantic** para declararlos.
Esto te permitirá **reutilizar el modelo** en **múltiples lugares** y también declarar validaciones y metadatos para todos los parámetros al mismo tiempo. 😎
/// note | Nota
Esto es compatible desde la versión `0.115.0` de FastAPI. 🤓
///
## Parámetros de Header con un Modelo Pydantic { #header-parameters-with-a-pydantic-model }
Declara los **parámetros de header** que necesitas en un **modelo Pydantic**, y luego declara el parámetro como `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** **extraerá** los datos para **cada campo** de los **headers** en el request y te dará el modelo Pydantic que definiste.
## Revisa la Documentación { #check-the-docs }
Puedes ver los headers requeridos en la interfaz de documentación en `/docs`:
## Prohibir Headers Extra { #forbid-extra-headers }
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** los headers que deseas recibir.
Puedes usar la configuración del modelo de Pydantic para `prohibir` cualquier campo `extra`:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Si un cliente intenta enviar algunos **headers extra**, recibirán un response de **error**.
Por ejemplo, si el cliente intenta enviar un header `tool` con un valor de `plumbus`, recibirán un response de **error** indicando que el parámetro de header `tool` no está permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Desactivar la conversión de guiones bajos { #disable-convert-underscores }
De la misma forma que con los parámetros de header normales, cuando tienes caracteres de guion bajo en los nombres de los parámetros, se **convierten automáticamente en guiones**.
Por ejemplo, si tienes un parámetro de header `save_data` en el código, el header HTTP esperado será `save-data`, y aparecerá así en la documentación.
Si por alguna razón necesitas desactivar esta conversión automática, también puedes hacerlo para los modelos Pydantic de parámetros de header.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Advertencia
Antes de establecer `convert_underscores` a `False`, ten en cuenta que algunos proxies y servidores HTTP no permiten el uso de headers con guiones bajos.
///
## Resumen { #summary }
Puedes usar **modelos Pydantic** para declarar **headers** en **FastAPI**. 😎
================================================
FILE: docs/es/docs/tutorial/header-params.md
================================================
# Parámetros de Header { #header-parameters }
Puedes definir los parámetros de Header de la misma manera que defines los parámetros de `Query`, `Path` y `Cookie`.
## Importar `Header` { #import-header }
Primero importa `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Declarar parámetros de `Header` { #declare-header-parameters }
Luego declara los parámetros de header usando la misma estructura que con `Path`, `Query` y `Cookie`.
Puedes definir el valor por defecto así como toda la validación extra o los parámetros de anotaciones:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalles Técnicos
`Header` es una clase "hermana" de `Path`, `Query` y `Cookie`. También hereda de la misma clase común `Param`.
Pero recuerda que cuando importas `Query`, `Path`, `Header`, y otros de `fastapi`, en realidad son funciones que retornan clases especiales.
///
/// info | Información
Para declarar headers, necesitas usar `Header`, porque de otra forma los parámetros serían interpretados como parámetros de query.
///
## Conversión automática { #automatic-conversion }
`Header` tiene un poquito de funcionalidad extra además de lo que proporcionan `Path`, `Query` y `Cookie`.
La mayoría de los headers estándar están separados por un carácter "guion", también conocido como el "símbolo menos" (`-`).
Pero una variable como `user-agent` es inválida en Python.
Así que, por defecto, `Header` convertirá los caracteres de los nombres de los parámetros de guion bajo (`_`) a guion (`-`) para extraer y documentar los headers.
Además, los headers HTTP no diferencian entre mayúsculas y minúsculas, por lo que los puedes declarar con el estilo estándar de Python (también conocido como "snake_case").
Así que, puedes usar `user_agent` como normalmente lo harías en código Python, en lugar de necesitar capitalizar las primeras letras como `User_Agent` o algo similar.
Si por alguna razón necesitas desactivar la conversión automática de guiones bajos a guiones, establece el parámetro `convert_underscores` de `Header` a `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Advertencia
Antes de establecer `convert_underscores` a `False`, ten en cuenta que algunos proxies y servidores HTTP no permiten el uso de headers con guiones bajos.
///
## Headers duplicados { #duplicate-headers }
Es posible recibir headers duplicados. Eso significa, el mismo header con múltiples valores.
Puedes definir esos casos usando una lista en la declaración del tipo.
Recibirás todos los valores del header duplicado como una `list` de Python.
Por ejemplo, para declarar un header de `X-Token` que puede aparecer más de una vez, puedes escribir:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Si te comunicas con esa *path operation* enviando dos headers HTTP como:
```
X-Token: foo
X-Token: bar
```
El response sería como:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Recapitulación { #recap }
Declara headers con `Header`, usando el mismo patrón común que `Query`, `Path` y `Cookie`.
Y no te preocupes por los guiones bajos en tus variables, **FastAPI** se encargará de convertirlos.
================================================
FILE: docs/es/docs/tutorial/index.md
================================================
# Tutorial - Guía del Usuario { #tutorial-user-guide }
Este tutorial te muestra cómo usar **FastAPI** con la mayoría de sus funcionalidades, paso a paso.
Cada sección se basa gradualmente en las anteriores, pero está estructurada para separar temas, de manera que puedas ir directamente a cualquier sección específica para resolver tus necesidades específicas de API.
También está diseñado para funcionar como una referencia futura para que puedas volver y ver exactamente lo que necesitas.
## Ejecuta el código { #run-the-code }
Todos los bloques de código pueden ser copiados y usados directamente (de hecho, son archivos Python probados).
Para ejecutar cualquiera de los ejemplos, copia el código a un archivo `main.py`, y comienza `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Es **ALTAMENTE recomendable** que escribas o copies el código, lo edites y lo ejecutes localmente.
Usarlo en tu editor es lo que realmente te muestra los beneficios de FastAPI, al ver cuán poco código tienes que escribir, todos los chequeos de tipos, autocompletado, etc.
---
## Instalar FastAPI { #install-fastapi }
El primer paso es instalar FastAPI.
Asegúrate de crear un [entorno virtual](../virtual-environments.md), actívalo, y luego **instala FastAPI**:
/// note | Nota
Cuando instalas con `pip install "fastapi[standard]"` viene con algunas dependencias opcionales estándar por defecto, incluyendo `fastapi-cloud-cli`, que te permite hacer deploy a [FastAPI Cloud](https://fastapicloud.com).
Si no quieres tener esas dependencias opcionales, en su lugar puedes instalar `pip install fastapi`.
Si quieres instalar las dependencias estándar pero sin `fastapi-cloud-cli`, puedes instalar con `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip | Consejo
FastAPI tiene una [extensión oficial para VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (y Cursor), que ofrece muchas funcionalidades, incluyendo un explorador de path operation, búsqueda de path operation, navegación de CodeLens en tests (saltar a la definición desde tests), y deploy y logs de FastAPI Cloud, todo desde tu editor.
///
## Guía Avanzada del Usuario { #advanced-user-guide }
También hay una **Guía Avanzada del Usuario** que puedes leer después de esta **Tutorial - Guía del Usuario**.
La **Guía Avanzada del Usuario** se basa en esta, utiliza los mismos conceptos y te enseña algunas funcionalidades adicionales.
Pero primero deberías leer la **Tutorial - Guía del Usuario** (lo que estás leyendo ahora mismo).
Está diseñada para que puedas construir una aplicación completa solo con la **Tutorial - Guía del Usuario**, y luego extenderla de diferentes maneras, dependiendo de tus necesidades, utilizando algunas de las ideas adicionales de la **Guía Avanzada del Usuario**.
================================================
FILE: docs/es/docs/tutorial/metadata.md
================================================
# Metadata y URLs de Docs { #metadata-and-docs-urls }
Puedes personalizar varias configuraciones de metadata en tu aplicación **FastAPI**.
## Metadata para la API { #metadata-for-api }
Puedes establecer los siguientes campos que se usan en la especificación OpenAPI y en las interfaces automáticas de documentación de la API:
| Parámetro | Tipo | Descripción |
|------------|------|-------------|
| `title` | `str` | El título de la API. |
| `summary` | `str` | Un resumen corto de la API. Disponible desde OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Una breve descripción de la API. Puede usar Markdown. |
| `version` | `string` | La versión de la API. Esta es la versión de tu propia aplicación, no de OpenAPI. Por ejemplo, `2.5.0`. |
| `terms_of_service` | `str` | Una URL a los Términos de Servicio para la API. Si se proporciona, debe ser una URL. |
| `contact` | `dict` | La información de contacto para la API expuesta. Puede contener varios campos. contact fields
Parámetro
Tipo
Descripción
name
str
El nombre identificativo de la persona/organización de contacto.
url
str
La URL que apunta a la información de contacto. DEBE tener el formato de una URL.
email
str
La dirección de correo electrónico de la persona/organización de contacto. DEBE tener el formato de una dirección de correo.
|
| `license_info` | `dict` | La información de la licencia para la API expuesta. Puede contener varios campos. license_info fields
Parámetro
Tipo
Descripción
name
str
REQUERIDO (si se establece un license_info). El nombre de la licencia utilizada para la API.
identifier
str
Una expresión de licencia [SPDX](https://spdx.org/licenses/) para la API. El campo identifier es mutuamente excluyente del campo url. Disponible desde OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
Una URL a la licencia utilizada para la API. DEBE tener el formato de una URL.
|
Puedes configurarlos de la siguiente manera:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Consejo
Puedes escribir Markdown en el campo `description` y se mostrará en el resultado.
///
Con esta configuración, la documentación automática de la API se vería así:
## Identificador de licencia { #license-identifier }
Desde OpenAPI 3.1.0 y FastAPI 0.99.0, también puedes establecer la `license_info` con un `identifier` en lugar de una `url`.
Por ejemplo:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Metadata para etiquetas { #metadata-for-tags }
También puedes agregar metadata adicional para las diferentes etiquetas usadas para agrupar tus path operations con el parámetro `openapi_tags`.
Este toma una list que contiene un diccionario para cada etiqueta.
Cada diccionario puede contener:
* `name` (**requerido**): un `str` con el mismo nombre de etiqueta que usas en el parámetro `tags` en tus *path operations* y `APIRouter`s.
* `description`: un `str` con una breve descripción de la etiqueta. Puede tener Markdown y se mostrará en la interfaz de documentación.
* `externalDocs`: un `dict` que describe documentación externa con:
* `description`: un `str` con una breve descripción para la documentación externa.
* `url` (**requerido**): un `str` con la URL para la documentación externa.
### Crear metadata para etiquetas { #create-metadata-for-tags }
Probemos eso en un ejemplo con etiquetas para `users` y `items`.
Crea metadata para tus etiquetas y pásala al parámetro `openapi_tags`:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Nota que puedes utilizar Markdown dentro de las descripciones, por ejemplo "login" se mostrará en negrita (**login**) y "fancy" se mostrará en cursiva (_fancy_).
/// tip | Consejo
No tienes que agregar metadata para todas las etiquetas que uses.
///
### Usar tus etiquetas { #use-your-tags }
Usa el parámetro `tags` con tus *path operations* (y `APIRouter`s) para asignarlas a diferentes etiquetas:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Información
Lee más sobre etiquetas en [Configuración de Path Operation](path-operation-configuration.md#tags).
///
### Revisa la documentación { #check-the-docs }
Ahora, si revisas la documentación, mostrará toda la metadata adicional:
### Orden de las etiquetas { #order-of-tags }
El orden de cada diccionario de metadata de etiqueta también define el orden mostrado en la interfaz de documentación.
Por ejemplo, aunque `users` iría después de `items` en orden alfabético, se muestra antes porque agregamos su metadata como el primer diccionario en la list.
## URL de OpenAPI { #openapi-url }
Por defecto, el esquema OpenAPI se sirve en `/openapi.json`.
Pero puedes configurarlo con el parámetro `openapi_url`.
Por ejemplo, para configurarlo para que se sirva en `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Si quieres deshabilitar el esquema OpenAPI completamente, puedes establecer `openapi_url=None`, eso también deshabilitará las interfaces de usuario de documentación que lo usan.
## URLs de Docs { #docs-urls }
Puedes configurar las dos interfaces de usuario de documentación incluidas:
* **Swagger UI**: servida en `/docs`.
* Puedes establecer su URL con el parámetro `docs_url`.
* Puedes deshabilitarla estableciendo `docs_url=None`.
* **ReDoc**: servida en `/redoc`.
* Puedes establecer su URL con el parámetro `redoc_url`.
* Puedes deshabilitarla estableciendo `redoc_url=None`.
Por ejemplo, para configurar Swagger UI para que se sirva en `/documentation` y deshabilitar ReDoc:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/es/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
Puedes añadir middleware a las aplicaciones de **FastAPI**.
Un "middleware" es una función que trabaja con cada **request** antes de que sea procesada por cualquier *path operation* específica. Y también con cada **response** antes de devolverla.
* Toma cada **request** que llega a tu aplicación.
* Puede entonces hacer algo a esa **request** o ejecutar cualquier código necesario.
* Luego pasa la **request** para que sea procesada por el resto de la aplicación (por alguna *path operation*).
* Después toma la **response** generada por la aplicación (por alguna *path operation*).
* Puede hacer algo a esa **response** o ejecutar cualquier código necesario.
* Luego devuelve la **response**.
/// note | Detalles Técnicos
Si tienes dependencias con `yield`, el código de salida se ejecutará *después* del middleware.
Si hubiera tareas en segundo plano (cubiertas en la sección [Tareas en segundo plano](background-tasks.md), lo verás más adelante), se ejecutarán *después* de todo el middleware.
///
## Crear un middleware { #create-a-middleware }
Para crear un middleware usas el decorador `@app.middleware("http")` encima de una función.
La función middleware recibe:
* La `request`.
* Una función `call_next` que recibirá la `request` como parámetro.
* Esta función pasará la `request` a la correspondiente *path operation*.
* Luego devuelve la `response` generada por la correspondiente *path operation*.
* Puedes entonces modificar aún más la `response` antes de devolverla.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Consejo
Ten en cuenta que los custom proprietary headers se pueden añadir [usando el prefijo `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Pero si tienes custom headers que deseas que un cliente en un navegador pueda ver, necesitas añadirlos a tus configuraciones de CORS ([CORS (Cross-Origin Resource Sharing)](cors.md)) usando el parámetro `expose_headers` documentado en [la documentación de CORS de Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Detalles Técnicos
También podrías usar `from starlette.requests import Request`.
**FastAPI** lo proporciona como una conveniencia para ti, el desarrollador. Pero viene directamente de Starlette.
///
### Antes y después de la `response` { #before-and-after-the-response }
Puedes añadir código que se ejecute con la `request`, antes de que cualquier *path operation* la reciba.
Y también después de que se genere la `response`, antes de devolverla.
Por ejemplo, podrías añadir un custom header `X-Process-Time` que contenga el tiempo en segundos que tomó procesar la request y generar una response:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Consejo
Aquí usamos [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) en lugar de `time.time()` porque puede ser más preciso para estos casos de uso. 🤓
///
## Orden de ejecución con múltiples middlewares { #multiple-middleware-execution-order }
Cuando añades múltiples middlewares usando ya sea el decorador `@app.middleware()` o el método `app.add_middleware()`, cada nuevo middleware envuelve la aplicación, formando un stack. El último middleware añadido es el más externo, y el primero es el más interno.
En el camino de la request, el middleware más externo se ejecuta primero.
En el camino de la response, se ejecuta al final.
Por ejemplo:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Esto da como resultado el siguiente orden de ejecución:
* **Request**: MiddlewareB → MiddlewareA → ruta
* **Response**: ruta → MiddlewareA → MiddlewareB
Este comportamiento de apilamiento asegura que los middlewares se ejecuten en un orden predecible y controlable.
## Otros middlewares { #other-middlewares }
Más adelante puedes leer sobre otros middlewares en la [Guía del Usuario Avanzado: Middleware Avanzado](../advanced/middleware.md).
Leerás sobre cómo manejar CORS con un middleware en la siguiente sección.
================================================
FILE: docs/es/docs/tutorial/path-operation-configuration.md
================================================
# Configuración de Path Operation { #path-operation-configuration }
Hay varios parámetros que puedes pasar a tu *path operation decorator* para configurarlo.
/// warning | Advertencia
Ten en cuenta que estos parámetros se pasan directamente al *path operation decorator*, no a tu *path operation function*.
///
## Código de Estado del Response { #response-status-code }
Puedes definir el `status_code` (HTTP) que se utilizará en el response de tu *path operation*.
Puedes pasar directamente el código `int`, como `404`.
Pero si no recuerdas para qué es cada código numérico, puedes usar las constantes atajo en `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Ese código de estado se usará en el response y se añadirá al esquema de OpenAPI.
/// note | Detalles Técnicos
También podrías usar `from starlette import status`.
**FastAPI** ofrece el mismo `starlette.status` como `fastapi.status` solo por conveniencia para ti, el desarrollador. Pero viene directamente de Starlette.
///
## Tags { #tags }
Puedes añadir tags a tu *path operation*, pasando el parámetro `tags` con un `list` de `str` (comúnmente solo una `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Serán añadidas al esquema de OpenAPI y usadas por las interfaces de documentación automática:
### Tags con Enums { #tags-with-enums }
Si tienes una gran aplicación, podrías terminar acumulando **varias tags**, y querrías asegurarte de que siempre uses la **misma tag** para *path operations* relacionadas.
En estos casos, podría tener sentido almacenar las tags en un `Enum`.
**FastAPI** soporta eso de la misma manera que con strings normales:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Resumen y Descripción { #summary-and-description }
Puedes añadir un `summary` y `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Descripción desde docstring { #description-from-docstring }
Como las descripciones tienden a ser largas y cubrir múltiples líneas, puedes declarar la descripción de la *path operation* en la docstring de la función y **FastAPI** la leerá desde allí.
Puedes escribir [Markdown](https://en.wikipedia.org/wiki/Markdown) en el docstring, se interpretará y mostrará correctamente (teniendo en cuenta la indentación del docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Será usado en la documentación interactiva:
## Descripción del Response { #response-description }
Puedes especificar la descripción del response con el parámetro `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Información
Ten en cuenta que `response_description` se refiere específicamente al response, mientras que `description` se refiere a la *path operation* en general.
///
/// check | Revisa
OpenAPI especifica que cada *path operation* requiere una descripción de response.
Entonces, si no proporcionas una, **FastAPI** generará automáticamente una de "Response exitoso".
///
## Deprecar una *path operation* { #deprecate-a-path-operation }
Si necesitas marcar una *path operation* como deprecated, pero sin eliminarla, pasa el parámetro `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Se marcará claramente como deprecado en la documentación interactiva:
Revisa cómo lucen las *path operations* deprecadas y no deprecadas:
## Resumen { #recap }
Puedes configurar y añadir metadatos a tus *path operations* fácilmente pasando parámetros a los *path operation decorators*.
================================================
FILE: docs/es/docs/tutorial/path-params-numeric-validations.md
================================================
# Parámetros de Path y Validaciones Numéricas { #path-parameters-and-numeric-validations }
De la misma manera que puedes declarar más validaciones y metadatos para los parámetros de query con `Query`, puedes declarar el mismo tipo de validaciones y metadatos para los parámetros de path con `Path`.
## Importar `Path` { #import-path }
Primero, importa `Path` de `fastapi`, e importa `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Información
FastAPI agregó soporte para `Annotated` (y comenzó a recomendar su uso) en la versión 0.95.0.
Si tienes una versión anterior, obtendrás errores al intentar usar `Annotated`.
Asegúrate de [Actualizar la versión de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) a al menos la 0.95.1 antes de usar `Annotated`.
///
## Declarar metadatos { #declare-metadata }
Puedes declarar todos los mismos parámetros que para `Query`.
Por ejemplo, para declarar un valor de metadato `title` para el parámetro de path `item_id` puedes escribir:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Nota
Un parámetro de path siempre es requerido ya que tiene que formar parte del path. Incluso si lo declaras con `None` o le asignas un valor por defecto, no afectará en nada, siempre será requerido.
///
## Ordena los parámetros como necesites { #order-the-parameters-as-you-need }
/// tip | Consejo
Esto probablemente no es tan importante o necesario si usas `Annotated`.
///
Supongamos que quieres declarar el parámetro de query `q` como un `str` requerido.
Y no necesitas declarar nada más para ese parámetro, así que realmente no necesitas usar `Query`.
Pero aún necesitas usar `Path` para el parámetro de path `item_id`. Y no quieres usar `Annotated` por alguna razón.
Python se quejará si pones un valor con "por defecto" antes de un valor que no tenga "por defecto".
Pero puedes reordenarlos y poner el valor sin un valor por defecto (el parámetro de query `q`) primero.
No importa para **FastAPI**. Detectará los parámetros por sus nombres, tipos y declaraciones por defecto (`Query`, `Path`, etc), no le importa el orden.
Así que puedes declarar tu función como:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Pero ten en cuenta que si usas `Annotated`, no tendrás este problema, no importará ya que no estás usando los valores por defecto de los parámetros de la función para `Query()` o `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Ordena los parámetros como necesites, trucos { #order-the-parameters-as-you-need-tricks }
/// tip | Consejo
Esto probablemente no es tan importante o necesario si usas `Annotated`.
///
Aquí hay un **pequeño truco** que puede ser útil, pero no lo necesitarás a menudo.
Si quieres:
* declarar el parámetro de query `q` sin un `Query` ni ningún valor por defecto
* declarar el parámetro de path `item_id` usando `Path`
* tenerlos en un orden diferente
* no usar `Annotated`
...Python tiene una sintaxis especial para eso.
Pasa `*`, como el primer parámetro de la función.
Python no hará nada con ese `*`, pero sabrá que todos los parámetros siguientes deben ser llamados como argumentos de palabras clave (parejas key-value), también conocidos como kwargs. Incluso si no tienen un valor por defecto.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Mejor con `Annotated` { #better-with-annotated }
Ten en cuenta que si usas `Annotated`, como no estás usando valores por defecto de los parámetros de la función, no tendrás este problema y probablemente no necesitarás usar `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Validaciones numéricas: mayor o igual { #number-validations-greater-than-or-equal }
Con `Query` y `Path` (y otros que verás más adelante) puedes declarar restricciones numéricas.
Aquí, con `ge=1`, `item_id` necesitará ser un número entero "`g`reater than or `e`qual" a `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Validaciones numéricas: mayor que y menor o igual { #number-validations-greater-than-and-less-than-or-equal }
Lo mismo aplica para:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Validaciones numéricas: flotantes, mayor y menor { #number-validations-floats-greater-than-and-less-than }
Las validaciones numéricas también funcionan para valores `float`.
Aquí es donde se convierte en importante poder declarar gt y no solo ge. Ya que con esto puedes requerir, por ejemplo, que un valor sea mayor que `0`, incluso si es menor que `1`.
Así, `0.5` sería un valor válido. Pero `0.0` o `0` no lo serían.
Y lo mismo para lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Resumen { #recap }
Con `Query`, `Path` (y otros que aún no has visto) puedes declarar metadatos y validaciones de string de las mismas maneras que con [Parámetros de Query y Validaciones de String](query-params-str-validations.md).
Y también puedes declarar validaciones numéricas:
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info | Información
`Query`, `Path` y otras clases que verás más adelante son subclases de una clase común `Param`.
Todas ellas comparten los mismos parámetros para validación adicional y metadatos que has visto.
///
/// note | Detalles técnicos
Cuando importas `Query`, `Path` y otros de `fastapi`, en realidad son funciones.
Que cuando se llaman, retornan instances de clases con el mismo nombre.
Así que importas `Query`, que es una función. Y cuando la llamas, retorna una instance de una clase también llamada `Query`.
Estas funciones están allí (en lugar de usar simplemente las clases directamente) para que tu editor no marque errores sobre sus tipos.
De esa forma puedes usar tu editor y herramientas de programación normales sin tener que agregar configuraciones personalizadas para omitir esos errores.
///
================================================
FILE: docs/es/docs/tutorial/path-params.md
================================================
# Parámetros de Path { #path-parameters }
Puedes declarar "parámetros" o "variables" de path con la misma sintaxis que se usa en los format strings de Python:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
El valor del parámetro de path `item_id` se pasará a tu función como el argumento `item_id`.
Así que, si ejecutas este ejemplo y vas a [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), verás un response de:
```JSON
{"item_id":"foo"}
```
## Parámetros de path con tipos { #path-parameters-with-types }
Puedes declarar el tipo de un parámetro de path en la función, usando anotaciones de tipos estándar de Python:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
En este caso, `item_id` se declara como un `int`.
/// check | Revisa
Esto te dará soporte del editor dentro de tu función, con chequeo de errores, autocompletado, etc.
///
## Conversión de datos { #data-conversion }
Si ejecutas este ejemplo y abres tu navegador en [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), verás un response de:
```JSON
{"item_id":3}
```
/// check | Revisa
Nota que el valor que tu función recibió (y devolvió) es `3`, como un `int` de Python, no un string `"3"`.
Entonces, con esa declaración de tipo, **FastAPI** te ofrece "parsing" automático de request.
///
## Validación de datos { #data-validation }
Pero si vas al navegador en [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), verás un bonito error HTTP de:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
porque el parámetro de path `item_id` tenía un valor de `"foo"`, que no es un `int`.
El mismo error aparecería si proporcionaras un `float` en lugar de un `int`, como en: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Revisa
Entonces, con la misma declaración de tipo de Python, **FastAPI** te ofrece validación de datos.
Nota que el error también indica claramente el punto exacto donde la validación falló.
Esto es increíblemente útil mientras desarrollas y depuras código que interactúa con tu API.
///
## Documentación { #documentation }
Y cuando abras tu navegador en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), verás una documentación de API automática e interactiva como:
/// check | Revisa
Nuevamente, solo con esa misma declaración de tipo de Python, **FastAPI** te ofrece documentación automática e interactiva (integrando Swagger UI).
Nota que el parámetro de path está declarado como un entero.
///
## Beneficios basados en estándares, documentación alternativa { #standards-based-benefits-alternative-documentation }
Y porque el esquema generado es del estándar [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md), hay muchas herramientas compatibles.
Debido a esto, el propio **FastAPI** proporciona una documentación de API alternativa (usando ReDoc), a la cual puedes acceder en [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc):
De la misma manera, hay muchas herramientas compatibles. Incluyendo herramientas de generación de código para muchos lenguajes.
## Pydantic { #pydantic }
Toda la validación de datos se realiza internamente con [Pydantic](https://docs.pydantic.dev/), así que obtienes todos los beneficios de esta. Y sabes que estás en buenas manos.
Puedes usar las mismas declaraciones de tipo con `str`, `float`, `bool` y muchos otros tipos de datos complejos.
Varios de estos se exploran en los siguientes capítulos del tutorial.
## El orden importa { #order-matters }
Al crear *path operations*, puedes encontrarte en situaciones donde tienes un path fijo.
Como `/users/me`, imaginemos que es para obtener datos sobre el usuario actual.
Y luego también puedes tener un path `/users/{user_id}` para obtener datos sobre un usuario específico por algún ID de usuario.
Debido a que las *path operations* se evalúan en orden, necesitas asegurarte de que el path para `/users/me` se declara antes que el de `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
De lo contrario, el path para `/users/{user_id}` también coincidiría para `/users/me`, "pensando" que está recibiendo un parámetro `user_id` con un valor de `"me"`.
De manera similar, no puedes redefinir una path operation:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
La primera siempre será utilizada ya que el path coincide primero.
## Valores predefinidos { #predefined-values }
Si tienes una *path operation* que recibe un *path parameter*, pero quieres que los valores posibles válidos del *path parameter* estén predefinidos, puedes usar un `Enum` estándar de Python.
### Crear una clase `Enum` { #create-an-enum-class }
Importa `Enum` y crea una subclase que herede de `str` y de `Enum`.
Al heredar de `str`, la documentación de la API podrá saber que los valores deben ser de tipo `string` y podrá representarlos correctamente.
Luego crea atributos de clase con valores fijos, que serán los valores válidos disponibles:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Consejo
Si te estás preguntando, "AlexNet", "ResNet" y "LeNet" son solo nombres de modelos de Machine Learning.
///
### Declarar un *path parameter* { #declare-a-path-parameter }
Luego crea un *path parameter* con una anotación de tipo usando la clase enum que creaste (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Revisa la documentación { #check-the-docs }
Como los valores disponibles para el *path parameter* están predefinidos, la documentación interactiva puede mostrarlos de manera ordenada:
### Trabajando con *enumeraciones* de Python { #working-with-python-enumerations }
El valor del *path parameter* será un *miembro* de enumeración.
#### Comparar *miembros* de enumeraciones { #compare-enumeration-members }
Puedes compararlo con el *miembro* de enumeración en tu enum creada `ModelName`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Obtener el valor de *enumeración* { #get-the-enumeration-value }
Puedes obtener el valor actual (un `str` en este caso) usando `model_name.value`, o en general, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Consejo
También podrías acceder al valor `"lenet"` con `ModelName.lenet.value`.
///
#### Devolver *miembros* de enumeración { #return-enumeration-members }
Puedes devolver *miembros de enum* desde tu *path operation*, incluso anidados en un cuerpo JSON (por ejemplo, un `dict`).
Serán convertidos a sus valores correspondientes (cadenas en este caso) antes de devolverlos al cliente:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
En tu cliente recibirás un response JSON como:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parámetros de path conteniendo paths { #path-parameters-containing-paths }
Imaginemos que tienes una *path operation* con un path `/files/{file_path}`.
Pero necesitas que `file_path` en sí mismo contenga un *path*, como `home/johndoe/myfile.txt`.
Entonces, la URL para ese archivo sería algo como: `/files/home/johndoe/myfile.txt`.
### Soporte de OpenAPI { #openapi-support }
OpenAPI no soporta una manera de declarar un *path parameter* para que contenga un *path* dentro, ya que eso podría llevar a escenarios que son difíciles de probar y definir.
Sin embargo, todavía puedes hacerlo en **FastAPI**, usando una de las herramientas internas de Starlette.
Y la documentación seguiría funcionando, aunque no agregue ninguna documentación indicando que el parámetro debe contener un path.
### Convertidor de Path { #path-convertor }
Usando una opción directamente de Starlette puedes declarar un *path parameter* conteniendo un *path* usando una URL como:
```
/files/{file_path:path}
```
En este caso, el nombre del parámetro es `file_path`, y la última parte, `:path`, indica que el parámetro debería coincidir con cualquier *path*.
Así que, puedes usarlo con:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Consejo
Podrías necesitar que el parámetro contenga `/home/johndoe/myfile.txt`, con una barra inclinada (`/`) inicial.
En ese caso, la URL sería: `/files//home/johndoe/myfile.txt`, con una doble barra inclinada (`//`) entre `files` y `home`.
///
## Resumen { #recap }
Con **FastAPI**, al usar declaraciones de tipo estándar de Python, cortas e intuitivas, obtienes:
* Soporte del editor: chequeo de errores, autocompletado, etc.
* " parsing " de datos
* Validación de datos
* Anotación de API y documentación automática
Y solo tienes que declararlos una vez.
Probablemente esa sea la principal ventaja visible de **FastAPI** en comparación con otros frameworks alternativos (aparte del rendimiento bruto).
================================================
FILE: docs/es/docs/tutorial/query-param-models.md
================================================
# Modelos de Parámetros Query { #query-parameter-models }
Si tienes un grupo de **parámetros query** que están relacionados, puedes crear un **modelo de Pydantic** para declararlos.
Esto te permitiría **reutilizar el modelo** en **múltiples lugares** y también declarar validaciones y metadatos para todos los parámetros de una vez. 😎
/// note | Nota
Esto es compatible desde la versión `0.115.0` de FastAPI. 🤓
///
## Parámetros Query con un Modelo Pydantic { #query-parameters-with-a-pydantic-model }
Declara los **parámetros query** que necesitas en un **modelo de Pydantic**, y luego declara el parámetro como `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI** **extraerá** los datos para **cada campo** de los **parámetros query** en el request y te proporcionará el modelo de Pydantic que definiste.
## Revisa la Documentación { #check-the-docs }
Puedes ver los parámetros query en la UI de documentación en `/docs`:
## Prohibir Parámetros Query Extras { #forbid-extra-query-parameters }
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** los parámetros query que deseas recibir.
Puedes usar la configuración del modelo de Pydantic para `forbid` cualquier campo `extra`:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Si un cliente intenta enviar algunos datos **extra** en los **parámetros query**, recibirán un response de **error**.
Por ejemplo, si el cliente intenta enviar un parámetro query `tool` con un valor de `plumbus`, como:
```http
https://example.com/items/?limit=10&tool=plumbus
```
Recibirán un response de **error** que les indica que el parámetro query `tool` no está permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Resumen { #summary }
Puedes usar **modelos de Pydantic** para declarar **parámetros query** en **FastAPI**. 😎
/// tip | Consejo
Alerta de spoiler: también puedes usar modelos de Pydantic para declarar cookies y headers, pero leerás sobre eso más adelante en el tutorial. 🤫
///
================================================
FILE: docs/es/docs/tutorial/query-params-str-validations.md
================================================
# Parámetros de Query y Validaciones de String { #query-parameters-and-string-validations }
**FastAPI** te permite declarar información adicional y validación para tus parámetros.
Tomemos esta aplicación como ejemplo:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
El parámetro de query `q` es de tipo `str | None`, lo que significa que es de tipo `str` pero también podría ser `None`, y de hecho, el valor por defecto es `None`, así que FastAPI sabrá que no es requerido.
/// note | Nota
FastAPI sabrá que el valor de `q` no es requerido por el valor por defecto `= None`.
Tener `str | None` permitirá que tu editor te dé un mejor soporte y detecte errores.
///
## Validaciones adicionales { #additional-validation }
Vamos a hacer que, aunque `q` sea opcional, siempre que se proporcione, su longitud no exceda los 50 caracteres.
### Importar `Query` y `Annotated` { #import-query-and-annotated }
Para lograr eso, primero importa:
* `Query` desde `fastapi`
* `Annotated` desde `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Información
FastAPI añadió soporte para `Annotated` (y empezó a recomendarlo) en la versión 0.95.0.
Si tienes una versión más antigua, obtendrás errores al intentar usar `Annotated`.
Asegúrate de [Actualizar la versión de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) a al menos 0.95.1 antes de usar `Annotated`.
///
## Usar `Annotated` en el tipo del parámetro `q` { #use-annotated-in-the-type-for-the-q-parameter }
¿Recuerdas que te dije antes que `Annotated` puede usarse para agregar metadatos a tus parámetros en la [Introducción a Tipos de Python](../python-types.md#type-hints-with-metadata-annotations)?
Ahora es el momento de usarlo con FastAPI. 🚀
Teníamos esta anotación de tipo:
```Python
q: str | None = None
```
Lo que haremos es envolver eso con `Annotated`, para que se convierta en:
```Python
q: Annotated[str | None] = None
```
Ambas versiones significan lo mismo, `q` es un parámetro que puede ser un `str` o `None`, y por defecto, es `None`.
Ahora vamos a lo divertido. 🎉
## Agregar `Query` a `Annotated` en el parámetro `q` { #add-query-to-annotated-in-the-q-parameter }
Ahora que tenemos este `Annotated` donde podemos poner más información (en este caso algunas validaciones adicionales), agrega `Query` dentro de `Annotated`, y establece el parámetro `max_length` a `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Nota que el valor por defecto sigue siendo `None`, por lo que el parámetro sigue siendo opcional.
Pero ahora, al tener `Query(max_length=50)` dentro de `Annotated`, le estamos diciendo a FastAPI que queremos que tenga validación adicional para este valor, queremos que tenga un máximo de 50 caracteres. 😎
/// tip | Consejo
Aquí estamos usando `Query()` porque este es un **parámetro de query**. Más adelante veremos otros como `Path()`, `Body()`, `Header()`, y `Cookie()`, que también aceptan los mismos argumentos que `Query()`.
///
FastAPI ahora:
* Validará los datos asegurándose de que la longitud máxima sea de 50 caracteres
* Mostrará un error claro para el cliente cuando los datos no sean válidos
* Documentará el parámetro en el OpenAPI esquema *path operation* (así aparecerá en la UI de documentación automática)
## Alternativa (antigua): `Query` como valor por defecto { #alternative-old-query-as-the-default-value }
Versiones anteriores de FastAPI (antes de 0.95.0) requerían que usaras `Query` como el valor por defecto de tu parámetro, en lugar de ponerlo en `Annotated`, hay una alta probabilidad de que veas código usándolo alrededor, así que te lo explicaré.
/// tip | Consejo
Para nuevo código y siempre que sea posible, usa `Annotated` como se explicó arriba. Hay múltiples ventajas (explicadas a continuación) y no hay desventajas. 🍰
///
Así es como usarías `Query()` como el valor por defecto de tu parámetro de función, estableciendo el parámetro `max_length` a 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Como en este caso (sin usar `Annotated`) debemos reemplazar el valor por defecto `None` en la función con `Query()`, ahora necesitamos establecer el valor por defecto con el parámetro `Query(default=None)`, esto sirve al mismo propósito de definir ese valor por defecto (al menos para FastAPI).
Entonces:
```Python
q: str | None = Query(default=None)
```
...hace que el parámetro sea opcional, con un valor por defecto de `None`, lo mismo que:
```Python
q: str | None = None
```
Pero la versión con `Query` lo declara explícitamente como un parámetro de query.
Luego, podemos pasar más parámetros a `Query`. En este caso, el parámetro `max_length` que se aplica a los strings:
```Python
q: str | None = Query(default=None, max_length=50)
```
Esto validará los datos, mostrará un error claro cuando los datos no sean válidos, y documentará el parámetro en el esquema del *path operation* de OpenAPI.
### `Query` como valor por defecto o en `Annotated` { #query-as-the-default-value-or-in-annotated }
Ten en cuenta que cuando uses `Query` dentro de `Annotated` no puedes usar el parámetro `default` para `Query`.
En su lugar utiliza el valor por defecto real del parámetro de la función. De lo contrario, sería inconsistente.
Por ejemplo, esto no está permitido:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...porque no está claro si el valor por defecto debería ser `"rick"` o `"morty"`.
Así que utilizarías (preferentemente):
```Python
q: Annotated[str, Query()] = "rick"
```
...o en code bases más antiguas encontrarás:
```Python
q: str = Query(default="rick")
```
### Ventajas de `Annotated` { #advantages-of-annotated }
Usar `Annotated` es recomendado en lugar del valor por defecto en los parámetros de función, es mejor por múltiples razones. 🤓
El valor por defecto del parámetro de función es el valor real por defecto, eso es más intuitivo con Python en general. 😌
Podrías llamar a esa misma función en otros lugares sin FastAPI, y funcionaría como se espera. Si hay un parámetro requerido (sin un valor por defecto), tu editor te avisará con un error, Python también se quejará si lo ejecutas sin pasar el parámetro requerido.
Cuando no usas `Annotated` y en su lugar usas el estilo de valor por defecto (antiguo), si llamas a esa función sin FastAPI en otros lugares, tienes que recordar pasar los argumentos a la función para que funcione correctamente, de lo contrario, los valores serán diferentes de lo que esperas (por ejemplo, `QueryInfo` o algo similar en lugar de `str`). Y tu editor no se quejará, y Python no se quejará al ejecutar esa función, solo cuando los errores dentro de las operaciones hagan que funcione incorrectamente.
Dado que `Annotated` puede tener más de una anotación de metadato, ahora podrías incluso usar la misma función con otras herramientas, como [Typer](https://typer.tiangolo.com/). 🚀
## Agregar más validaciones { #add-more-validations }
También puedes agregar un parámetro `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Agregar expresiones regulares { #add-regular-expressions }
Puedes definir una expresión regular `pattern` que el parámetro debe coincidir:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Este patrón específico de expresión regular comprueba que el valor recibido del parámetro:
* `^`: comienza con los siguientes caracteres, no tiene caracteres antes.
* `fixedquery`: tiene el valor exacto `fixedquery`.
* `$`: termina allí, no tiene más caracteres después de `fixedquery`.
Si te sientes perdido con todas estas ideas de "expresión regular", no te preocupes. Son un tema difícil para muchas personas. Aún puedes hacer muchas cosas sin necesitar expresiones regulares todavía.
Ahora sabes que cuando las necesites puedes usarlas en **FastAPI**.
## Valores por defecto { #default-values }
Puedes, por supuesto, usar valores por defecto diferentes de `None`.
Digamos que quieres declarar el parámetro de query `q` para que tenga un `min_length` de `3`, y para que tenga un valor por defecto de `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Nota
Tener un valor por defecto de cualquier tipo, incluyendo `None`, hace que el parámetro sea opcional (no requerido).
///
## Parámetros requeridos { #required-parameters }
Cuando no necesitamos declarar más validaciones o metadatos, podemos hacer que el parámetro de query `q` sea requerido simplemente no declarando un valor por defecto, como:
```Python
q: str
```
en lugar de:
```Python
q: str | None = None
```
Pero ahora lo estamos declarando con `Query`, por ejemplo, como:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Así que, cuando necesites declarar un valor como requerido mientras usas `Query`, simplemente puedes no declarar un valor por defecto:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Requerido, puede ser `None` { #required-can-be-none }
Puedes declarar que un parámetro puede aceptar `None`, pero que aún así es requerido. Esto obligaría a los clientes a enviar un valor, incluso si el valor es `None`.
Para hacer eso, puedes declarar que `None` es un tipo válido pero simplemente no declarar un valor por defecto:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Lista de parámetros de Query / múltiples valores { #query-parameter-list-multiple-values }
Cuando defines un parámetro de query explícitamente con `Query` también puedes declararlo para recibir una lista de valores, o dicho de otra manera, para recibir múltiples valores.
Por ejemplo, para declarar un parámetro de query `q` que puede aparecer varias veces en la URL, puedes escribir:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Entonces, con una URL como:
```
http://localhost:8000/items/?q=foo&q=bar
```
recibirías los múltiples valores de los *query parameters* `q` (`foo` y `bar`) en una `list` de Python dentro de tu *path operation function*, en el *parámetro de función* `q`.
Entonces, el response a esa URL sería:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Consejo
Para declarar un parámetro de query con un tipo de `list`, como en el ejemplo anterior, necesitas usar explícitamente `Query`, de lo contrario sería interpretado como un request body.
///
La documentación interactiva de API se actualizará en consecuencia, para permitir múltiples valores:
### Lista de parámetros de Query / múltiples valores con valores por defecto { #query-parameter-list-multiple-values-with-defaults }
También puedes definir un valor por defecto `list` de valores si no se proporciona ninguno:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Si vas a:
```
http://localhost:8000/items/
```
el valor por defecto de `q` será: `["foo", "bar"]` y tu response será:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Usando solo `list` { #using-just-list }
También puedes usar `list` directamente en lugar de `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Nota
Ten en cuenta que en este caso, FastAPI no comprobará el contenido de la list.
Por ejemplo, `list[int]` comprobaría (y documentaría) que el contenido de la list son enteros. Pero `list` sola no lo haría.
///
## Declarar más metadatos { #declare-more-metadata }
Puedes agregar más información sobre el parámetro.
Esa información se incluirá en el OpenAPI generado y será utilizada por las interfaces de usuario de documentación y herramientas externas.
/// note | Nota
Ten en cuenta que diferentes herramientas podrían tener diferentes niveles de soporte de OpenAPI.
Algunas de ellas podrían no mostrar toda la información extra declarada todavía, aunque en la mayoría de los casos, la funcionalidad faltante ya está planificada para desarrollo.
///
Puedes agregar un `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
Y una `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Alias para parámetros { #alias-parameters }
Imagina que quieres que el parámetro sea `item-query`.
Como en:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Pero `item-query` no es un nombre de variable válido en Python.
Lo más cercano sería `item_query`.
Pero aún necesitas que sea exactamente `item-query`...
Entonces puedes declarar un `alias`, y ese alias será usado para encontrar el valor del parámetro:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Declarar parámetros obsoletos { #deprecating-parameters }
Ahora digamos que ya no te gusta este parámetro.
Tienes que dejarlo allí por un tiempo porque hay clientes usándolo, pero quieres que la documentación lo muestre claramente como obsoleto.
Luego pasa el parámetro `deprecated=True` a `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
La documentación lo mostrará así:
## Excluir parámetros de OpenAPI { #exclude-parameters-from-openapi }
Para excluir un parámetro de query del esquema de OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), establece el parámetro `include_in_schema` de `Query` a `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Validación personalizada { #custom-validation }
Podría haber casos donde necesites hacer alguna validación personalizada que no puede hacerse con los parámetros mostrados arriba.
En esos casos, puedes usar una función validadora personalizada que se aplique después de la validación normal (por ejemplo, después de validar que el valor es un `str`).
Puedes lograr eso usando [`AfterValidator` de Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) dentro de `Annotated`.
/// tip | Consejo
Pydantic también tiene [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) y otros. 🤓
///
Por ejemplo, este validador personalizado comprueba que el ID del ítem empiece con `isbn-` para un número de libro ISBN o con `imdb-` para un ID de URL de película de IMDB:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Información
Esto está disponible con Pydantic versión 2 o superior. 😎
///
/// tip | Consejo
Si necesitas hacer cualquier tipo de validación que requiera comunicarte con algún componente externo, como una base de datos u otra API, deberías usar Dependencias de FastAPI, las aprenderás más adelante.
Estos validadores personalizados son para cosas que pueden comprobarse solo con los mismos datos provistos en el request.
///
### Entiende ese código { #understand-that-code }
El punto importante es solo usar `AfterValidator` con una función dentro de `Annotated`. Si quieres, sáltate esta parte. 🤸
---
Pero si te da curiosidad este ejemplo de código específico y sigues entretenido, aquí tienes algunos detalles extra.
#### String con `value.startswith()` { #string-with-value-startswith }
¿Lo notaste? un string usando `value.startswith()` puede recibir una tupla, y comprobará cada valor en la tupla:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Un ítem aleatorio { #a-random-item }
Con `data.items()` obtenemos un objeto iterable con tuplas que contienen la clave y el valor para cada elemento del diccionario.
Convertimos este objeto iterable en una `list` propiamente dicha con `list(data.items())`.
Luego con `random.choice()` podemos obtener un valor aleatorio de la lista, así que obtenemos una tupla con `(id, name)`. Será algo como `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Luego asignamos esos dos valores de la tupla a las variables `id` y `name`.
Así, si el usuario no proporcionó un ID de ítem, aún recibirá una sugerencia aleatoria.
...hacemos todo esto en una sola línea simple. 🤯 ¿No te encanta Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Recapitulación { #recap }
Puedes declarar validaciones y metadatos adicionales para tus parámetros.
Validaciones genéricas y metadatos:
* `alias`
* `title`
* `description`
* `deprecated`
Validaciones específicas para strings:
* `min_length`
* `max_length`
* `pattern`
Validaciones personalizadas usando `AfterValidator`.
En estos ejemplos viste cómo declarar validaciones para valores de tipo `str`.
Mira los siguientes capítulos para aprender cómo declarar validaciones para otros tipos, como números.
================================================
FILE: docs/es/docs/tutorial/query-params.md
================================================
# Parámetros de Query { #query-parameters }
Cuando declaras otros parámetros de función que no son parte de los parámetros de path, son automáticamente interpretados como parámetros de "query".
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
La query es el conjunto de pares clave-valor que van después del `?` en una URL, separados por caracteres `&`.
Por ejemplo, en la URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...los parámetros de query son:
* `skip`: con un valor de `0`
* `limit`: con un valor de `10`
Como son parte de la URL, son "naturalmente" strings.
Pero cuando los declaras con tipos de Python (en el ejemplo anterior, como `int`), son convertidos a ese tipo y validados respecto a él.
Todo el mismo proceso que se aplica para los parámetros de path también se aplica para los parámetros de query:
* Soporte del editor (obviamente)
* "parsing" de datos
* Validación de datos
* Documentación automática
## Valores por defecto { #defaults }
Como los parámetros de query no son una parte fija de un path, pueden ser opcionales y pueden tener valores por defecto.
En el ejemplo anterior, tienen valores por defecto de `skip=0` y `limit=10`.
Entonces, ir a la URL:
```
http://127.0.0.1:8000/items/
```
sería lo mismo que ir a:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Pero si vas a, por ejemplo:
```
http://127.0.0.1:8000/items/?skip=20
```
Los valores de los parámetros en tu función serán:
* `skip=20`: porque lo configuraste en la URL
* `limit=10`: porque ese era el valor por defecto
## Parámetros opcionales { #optional-parameters }
De la misma manera, puedes declarar parámetros de query opcionales, estableciendo su valor por defecto en `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
En este caso, el parámetro de función `q` será opcional y será `None` por defecto.
/// check | Revisa
Además, nota que **FastAPI** es lo suficientemente inteligente para notar que el parámetro de path `item_id` es un parámetro de path y `q` no lo es, por lo tanto, es un parámetro de query.
///
## Conversión de tipos en parámetros de query { #query-parameter-type-conversion }
También puedes declarar tipos `bool`, y serán convertidos:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
En este caso, si vas a:
```
http://127.0.0.1:8000/items/foo?short=1
```
o
```
http://127.0.0.1:8000/items/foo?short=True
```
o
```
http://127.0.0.1:8000/items/foo?short=true
```
o
```
http://127.0.0.1:8000/items/foo?short=on
```
o
```
http://127.0.0.1:8000/items/foo?short=yes
```
o cualquier otra variación (mayúsculas, primera letra en mayúscula, etc.), tu función verá el parámetro `short` con un valor `bool` de `True`. De lo contrario, será `False`.
## Múltiples parámetros de path y de query { #multiple-path-and-query-parameters }
Puedes declarar múltiples parámetros de path y de query al mismo tiempo, **FastAPI** sabe cuál es cuál.
Y no tienes que declararlos en un orden específico.
Serán detectados por nombre:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Parámetros de query requeridos { #required-query-parameters }
Cuando declaras un valor por defecto para parámetros que no son de path (por ahora, solo hemos visto parámetros de query), entonces no es requerido.
Si no quieres agregar un valor específico pero solo hacer que sea opcional, establece el valor por defecto como `None`.
Pero cuando quieres hacer un parámetro de query requerido, simplemente no declares ningún valor por defecto:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Aquí el parámetro de query `needy` es un parámetro de query requerido de tipo `str`.
Si abres en tu navegador una URL como:
```
http://127.0.0.1:8000/items/foo-item
```
...sin agregar el parámetro requerido `needy`, verás un error como:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Como `needy` es un parámetro requerido, necesitarías establecerlo en la URL:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...esto funcionaría:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
Y por supuesto, puedes definir algunos parámetros como requeridos, algunos con un valor por defecto, y algunos enteramente opcionales:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
En este caso, hay 3 parámetros de query:
* `needy`, un `str` requerido.
* `skip`, un `int` con un valor por defecto de `0`.
* `limit`, un `int` opcional.
/// tip | Consejo
También podrías usar `Enum`s de la misma manera que con [Parámetros de Path](path-params.md#predefined-values).
///
================================================
FILE: docs/es/docs/tutorial/request-files.md
================================================
# Archivos de Request { #request-files }
Puedes definir archivos que serán subidos por el cliente utilizando `File`.
/// info | Información
Para recibir archivos subidos, primero instala [`python-multipart`](https://github.com/Kludex/python-multipart).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo y luego instalarlo, por ejemplo:
```console
$ pip install python-multipart
```
Esto es porque los archivos subidos se envían como "form data".
///
## Importar `File` { #import-file }
Importa `File` y `UploadFile` desde `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Definir Parámetros `File` { #define-file-parameters }
Crea parámetros de archivo de la misma manera que lo harías para `Body` o `Form`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Información
`File` es una clase que hereda directamente de `Form`.
Pero recuerda que cuando importas `Query`, `Path`, `File` y otros desde `fastapi`, esos son en realidad funciones que devuelven clases especiales.
///
/// tip | Consejo
Para declarar cuerpos de File, necesitas usar `File`, porque de otra manera los parámetros serían interpretados como parámetros query o parámetros de cuerpo (JSON).
///
Los archivos se subirán como "form data".
Si declaras el tipo de tu parámetro de *path operation function* como `bytes`, **FastAPI** leerá el archivo por ti y recibirás el contenido como `bytes`.
Ten en cuenta que esto significa que todo el contenido se almacenará en memoria. Esto funcionará bien para archivos pequeños.
Pero hay varios casos en los que podrías beneficiarte de usar `UploadFile`.
## Parámetros de Archivo con `UploadFile` { #file-parameters-with-uploadfile }
Define un parámetro de archivo con un tipo de `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Usar `UploadFile` tiene varias ventajas sobre `bytes`:
* No tienes que usar `File()` en el valor por defecto del parámetro.
* Usa un archivo "spooled":
* Un archivo almacenado en memoria hasta un límite de tamaño máximo, y después de superar este límite, se almacenará en el disco.
* Esto significa que funcionará bien para archivos grandes como imágenes, videos, binarios grandes, etc. sin consumir toda la memoria.
* Puedes obtener metadatos del archivo subido.
* Tiene una interfaz `async` [parecida a un archivo](https://docs.python.org/3/glossary.html#term-file-like-object).
* Expone un objeto Python real [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) que puedes pasar directamente a otros paquetes que esperan un objeto parecido a un archivo.
### `UploadFile` { #uploadfile }
`UploadFile` tiene los siguientes atributos:
* `filename`: Un `str` con el nombre original del archivo que fue subido (por ejemplo, `myimage.jpg`).
* `content_type`: Un `str` con el tipo de contenido (MIME type / media type) (por ejemplo, `image/jpeg`).
* `file`: Un [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (un objeto [parecido a un archivo](https://docs.python.org/3/glossary.html#term-file-like-object)). Este es el objeto de archivo Python real que puedes pasar directamente a otras funciones o paquetes que esperan un objeto "parecido a un archivo".
`UploadFile` tiene los siguientes métodos `async`. Todos ellos llaman a los métodos correspondientes del archivo por debajo (usando el `SpooledTemporaryFile` interno).
* `write(data)`: Escribe `data` (`str` o `bytes`) en el archivo.
* `read(size)`: Lee `size` (`int`) bytes/caracteres del archivo.
* `seek(offset)`: Va a la posición de bytes `offset` (`int`) en el archivo.
* Por ejemplo, `await myfile.seek(0)` iría al inicio del archivo.
* Esto es especialmente útil si ejecutas `await myfile.read()` una vez y luego necesitas leer el contenido nuevamente.
* `close()`: Cierra el archivo.
Como todos estos métodos son métodos `async`, necesitas "await" para ellos.
Por ejemplo, dentro de una *path operation function* `async` puedes obtener los contenidos con:
```Python
contents = await myfile.read()
```
Si estás dentro de una *path operation function* normal `def`, puedes acceder al `UploadFile.file` directamente, por ejemplo:
```Python
contents = myfile.file.read()
```
/// note | Detalles Técnicos de `async`
Cuando usas los métodos `async`, **FastAPI** ejecuta los métodos del archivo en un threadpool y los espera.
///
/// note | Detalles Técnicos de Starlette
El `UploadFile` de **FastAPI** hereda directamente del `UploadFile` de **Starlette**, pero añade algunas partes necesarias para hacerlo compatible con **Pydantic** y las otras partes de FastAPI.
///
## Qué es "Form Data" { #what-is-form-data }
La manera en que los formularios de HTML (``) envían los datos al servidor normalmente utiliza una codificación "especial" para esos datos, es diferente de JSON.
**FastAPI** se asegurará de leer esos datos del lugar correcto en lugar de JSON.
/// note | Detalles Técnicos
Los datos de los forms normalmente se codifican usando el "media type" `application/x-www-form-urlencoded` cuando no incluyen archivos.
Pero cuando el formulario incluye archivos, se codifica como `multipart/form-data`. Si usas `File`, **FastAPI** sabrá que tiene que obtener los archivos de la parte correcta del cuerpo.
Si deseas leer más sobre estas codificaciones y campos de formularios, dirígete a la [MDN web docs para `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Advertencia
Puedes declarar múltiples parámetros `File` y `Form` en una *path operation*, pero no puedes declarar campos `Body` que esperas recibir como JSON, ya que el request tendrá el cuerpo codificado usando `multipart/form-data` en lugar de `application/json`.
Esto no es una limitación de **FastAPI**, es parte del protocolo HTTP.
///
## Subida de Archivos Opcional { #optional-file-upload }
Puedes hacer un archivo opcional utilizando anotaciones de tipos estándar y estableciendo un valor por defecto de `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` con Metadatos Adicionales { #uploadfile-with-additional-metadata }
También puedes usar `File()` con `UploadFile`, por ejemplo, para establecer metadatos adicionales:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Subidas de Múltiples Archivos { #multiple-file-uploads }
Es posible subir varios archivos al mismo tiempo.
Estarían asociados al mismo "campo de formulario" enviado usando "form data".
Para usar eso, declara una `list` de `bytes` o `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Recibirás, como se declaró, una `list` de `bytes` o `UploadFile`s.
/// note | Detalles Técnicos
También podrías usar `from starlette.responses import HTMLResponse`.
**FastAPI** proporciona las mismas `starlette.responses` como `fastapi.responses` solo como una conveniencia para ti, el desarrollador. Pero la mayoría de los responses disponibles vienen directamente de Starlette.
///
### Subidas de Múltiples Archivos con Metadatos Adicionales { #multiple-file-uploads-with-additional-metadata }
Y de la misma manera que antes, puedes usar `File()` para establecer parámetros adicionales, incluso para `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Recapitulación { #recap }
Usa `File`, `bytes` y `UploadFile` para declarar archivos que se subirán en el request, enviados como form data.
================================================
FILE: docs/es/docs/tutorial/request-form-models.md
================================================
# Modelos de Formulario { #form-models }
Puedes usar **modelos de Pydantic** para declarar **campos de formulario** en FastAPI.
/// info | Información
Para usar formularios, primero instala [`python-multipart`](https://github.com/Kludex/python-multipart).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo, y luego instalarlo, por ejemplo:
```console
$ pip install python-multipart
```
///
/// note | Nota
Esto es compatible desde la versión `0.113.0` de FastAPI. 🤓
///
## Modelos de Pydantic para Formularios { #pydantic-models-for-forms }
Solo necesitas declarar un **modelo de Pydantic** con los campos que quieres recibir como **campos de formulario**, y luego declarar el parámetro como `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** **extraerá** los datos de **cada campo** de los **form data** en el request y te dará el modelo de Pydantic que definiste.
## Revisa la Documentación { #check-the-docs }
Puedes verificarlo en la interfaz de documentación en `/docs`:
## Prohibir Campos de Formulario Extra { #forbid-extra-form-fields }
En algunos casos de uso especiales (probablemente no muy comunes), podrías querer **restringir** los campos de formulario a solo aquellos declarados en el modelo de Pydantic. Y **prohibir** cualquier campo **extra**.
/// note | Nota
Esto es compatible desde la versión `0.114.0` de FastAPI. 🤓
///
Puedes usar la configuración del modelo de Pydantic para `forbid` cualquier campo `extra`:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Si un cliente intenta enviar datos extra, recibirá un response de **error**.
Por ejemplo, si el cliente intenta enviar los campos de formulario:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Recibirá un response de error indicando que el campo `extra` no está permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Resumen { #summary }
Puedes usar modelos de Pydantic para declarar campos de formulario en FastAPI. 😎
================================================
FILE: docs/es/docs/tutorial/request-forms-and-files.md
================================================
# Formularios y archivos del request { #request-forms-and-files }
Puedes definir archivos y campos de formulario al mismo tiempo usando `File` y `Form`.
/// info | Información
Para recibir archivos subidos y/o form data, primero instala [`python-multipart`](https://github.com/Kludex/python-multipart).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), actívalo y luego instálalo, por ejemplo:
```console
$ pip install python-multipart
```
///
## Importa `File` y `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Define parámetros `File` y `Form` { #define-file-and-form-parameters }
Crea parámetros de archivo y formulario de la misma manera que lo harías para `Body` o `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Los archivos y campos de formulario se subirán como form data y recibirás los archivos y campos de formulario.
Y puedes declarar algunos de los archivos como `bytes` y algunos como `UploadFile`.
/// warning | Advertencia
Puedes declarar múltiples parámetros `File` y `Form` en una *path operation*, pero no puedes también declarar campos `Body` que esperas recibir como JSON, ya que el request tendrá el body codificado usando `multipart/form-data` en lugar de `application/json`.
Esto no es una limitación de **FastAPI**, es parte del protocolo HTTP.
///
## Resumen { #recap }
Usa `File` y `Form` juntos cuando necesites recibir datos y archivos en el mismo request.
================================================
FILE: docs/es/docs/tutorial/request-forms.md
================================================
# Datos de formulario { #form-data }
Cuando necesitas recibir campos de formulario en lugar de JSON, puedes usar `Form`.
/// info | Información
Para usar formularios, primero instala [`python-multipart`](https://github.com/Kludex/python-multipart).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo, y luego instalarlo, por ejemplo:
```console
$ pip install python-multipart
```
///
## Importar `Form` { #import-form }
Importar `Form` desde `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Definir parámetros de `Form` { #define-form-parameters }
Crea parámetros de formulario de la misma manera que lo harías para `Body` o `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Por ejemplo, en una de las formas en las que se puede usar la especificación OAuth2 (llamada "password flow") se requiere enviar un `username` y `password` como campos de formulario.
La especificación requiere que los campos se llamen exactamente `username` y `password`, y que se envíen como campos de formulario, no JSON.
Con `Form` puedes declarar las mismas configuraciones que con `Body` (y `Query`, `Path`, `Cookie`), incluyendo validación, ejemplos, un alias (por ejemplo, `user-name` en lugar de `username`), etc.
/// info | Información
`Form` es una clase que hereda directamente de `Body`.
///
/// tip | Consejo
Para declarar bodies de formularios, necesitas usar `Form` explícitamente, porque sin él, los parámetros se interpretarían como parámetros de query o como parámetros de body (JSON).
///
## Sobre "Campos de formulario" { #about-form-fields }
La manera en que los formularios HTML (``) envían los datos al servidor normalmente usa una codificación "especial" para esos datos, es diferente de JSON.
**FastAPI** se encargará de leer esos datos del lugar correcto en lugar de JSON.
/// note | Detalles técnicos
Los datos de formularios normalmente se codifican usando el "media type" `application/x-www-form-urlencoded`.
Pero cuando el formulario incluye archivos, se codifica como `multipart/form-data`. Leerás sobre la gestión de archivos en el próximo capítulo.
Si quieres leer más sobre estas codificaciones y campos de formulario, dirígete a las [MDN web docs para `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Advertencia
Puedes declarar múltiples parámetros `Form` en una *path operation*, pero no puedes también declarar campos `Body` que esperas recibir como JSON, ya que el request tendrá el body codificado usando `application/x-www-form-urlencoded` en lugar de `application/json`.
Esto no es una limitación de **FastAPI**, es parte del protocolo HTTP.
///
## Recapitulación { #recap }
Usa `Form` para declarar parámetros de entrada de datos de formulario.
================================================
FILE: docs/es/docs/tutorial/response-model.md
================================================
# Modelo de Response - Tipo de Retorno { #response-model-return-type }
Puedes declarar el tipo utilizado para el response anotando el **tipo de retorno** de la *path operation function*.
Puedes utilizar **anotaciones de tipos** de la misma manera que lo harías para datos de entrada en **parámetros** de función, puedes utilizar modelos de Pydantic, lists, diccionarios, valores escalares como enteros, booleanos, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI usará este tipo de retorno para:
* **Validar** los datos devueltos.
* Si los datos son inválidos (por ejemplo, falta un campo), significa que el código de *tu* aplicación está defectuoso, no devolviendo lo que debería, y retornará un error del servidor en lugar de devolver datos incorrectos. De esta manera, tú y tus clientes pueden estar seguros de que recibirán los datos y la forma de los datos esperada.
* Agregar un **JSON Schema** para el response, en la *path operation* de OpenAPI.
* Esto será utilizado por la **documentación automática**.
* También será utilizado por herramientas de generación automática de código de cliente.
* **Serializar** los datos devueltos a JSON usando Pydantic, que está escrito en **Rust**, por lo que será **mucho más rápido**.
Pero lo más importante:
* **Limitará y filtrará** los datos de salida a lo que se define en el tipo de retorno.
* Esto es particularmente importante para la **seguridad**, veremos más sobre eso a continuación.
## Parámetro `response_model` { #response-model-parameter }
Hay algunos casos en los que necesitas o quieres devolver algunos datos que no son exactamente lo que declara el tipo.
Por ejemplo, podrías querer **devolver un diccionario** u objeto de base de datos, pero **declararlo como un modelo de Pydantic**. De esta manera el modelo de Pydantic haría toda la documentación de datos, validación, etc. para el objeto que devolviste (por ejemplo, un diccionario u objeto de base de datos).
Si añadiste la anotación del tipo de retorno, las herramientas y editores se quejarían con un error (correcto) diciéndote que tu función está devolviendo un tipo (por ejemplo, un dict) que es diferente de lo que declaraste (por ejemplo, un modelo de Pydantic).
En esos casos, puedes usar el parámetro del *decorador de path operation* `response_model` en lugar del tipo de retorno.
Puedes usar el parámetro `response_model` en cualquiera de las *path operations*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Nota
Observa que `response_model` es un parámetro del método "decorador" (`get`, `post`, etc). No de tu *path operation function*, como todos los parámetros y el cuerpo.
///
`response_model` recibe el mismo tipo que declararías para un campo de modelo Pydantic, por lo que puede ser un modelo de Pydantic, pero también puede ser, por ejemplo, un `list` de modelos de Pydantic, como `List[Item]`.
FastAPI usará este `response_model` para hacer toda la documentación de datos, validación, etc. y también para **convertir y filtrar los datos de salida** a su declaración de tipo.
/// tip | Consejo
Si tienes chequeo de tipos estricto en tu editor, mypy, etc., puedes declarar el tipo de retorno de la función como `Any`.
De esa manera le dices al editor que intencionalmente estás devolviendo cualquier cosa. Pero FastAPI todavía hará la documentación de datos, validación, filtrado, etc. con `response_model`.
///
### Prioridad del `response_model` { #response-model-priority }
Si declaras tanto un tipo de retorno como un `response_model`, el `response_model` tomará prioridad y será utilizado por FastAPI.
De esta manera puedes añadir anotaciones de tipos correctas a tus funciones incluso cuando estás devolviendo un tipo diferente al modelo de response, para ser utilizado por el editor y herramientas como mypy. Y aún así puedes hacer que FastAPI realice la validación de datos, documentación, etc. usando el `response_model`.
También puedes usar `response_model=None` para desactivar la creación de un modelo de response para esa *path operation*, podrías necesitar hacerlo si estás añadiendo anotaciones de tipos para cosas que no son campos válidos de Pydantic, verás un ejemplo de eso en una de las secciones a continuación.
## Devolver los mismos datos de entrada { #return-the-same-input-data }
Aquí estamos declarando un modelo `UserIn`, contendrá una contraseña en texto plano:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Información
Para usar `EmailStr`, primero instala [`email-validator`](https://github.com/JoshData/python-email-validator).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo, y luego instalarlo, por ejemplo:
```console
$ pip install email-validator
```
o con:
```console
$ pip install "pydantic[email]"
```
///
Y estamos usando este modelo para declarar nuestra entrada y el mismo modelo para declarar nuestra salida:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Ahora, cada vez que un navegador esté creando un usuario con una contraseña, la API devolverá la misma contraseña en el response.
En este caso, podría no ser un problema, porque es el mismo usuario que envía la contraseña.
Pero si usamos el mismo modelo para otra *path operation*, podríamos estar enviando las contraseñas de nuestros usuarios a cada cliente.
/// danger | Peligro
Nunca almacenes la contraseña en texto plano de un usuario ni la envíes en un response como esta, a menos que conozcas todas las advertencias y sepas lo que estás haciendo.
///
## Añadir un modelo de salida { #add-an-output-model }
Podemos en cambio crear un modelo de entrada con la contraseña en texto plano y un modelo de salida sin ella:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Aquí, aunque nuestra *path operation function* está devolviendo el mismo usuario de entrada que contiene la contraseña:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...hemos declarado el `response_model` para ser nuestro modelo `UserOut`, que no incluye la contraseña:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Entonces, **FastAPI** se encargará de filtrar todos los datos que no estén declarados en el modelo de salida (usando Pydantic).
### `response_model` o Tipo de Retorno { #response-model-or-return-type }
En este caso, como los dos modelos son diferentes, si anotáramos el tipo de retorno de la función como `UserOut`, el editor y las herramientas se quejarían de que estamos devolviendo un tipo inválido, ya que son clases diferentes.
Por eso en este ejemplo tenemos que declararlo en el parámetro `response_model`.
...pero sigue leyendo abajo para ver cómo superar eso.
## Tipo de Retorno y Filtrado de Datos { #return-type-and-data-filtering }
Continuemos con el ejemplo anterior. Queríamos **anotar la función con un tipo**, pero queríamos poder devolver desde la función algo que en realidad incluya **más datos**.
Queremos que FastAPI continúe **filtrando** los datos usando el modelo de response. Para que, incluso cuando la función devuelva más datos, el response solo incluya los campos declarados en el modelo de response.
En el ejemplo anterior, debido a que las clases eran diferentes, tuvimos que usar el parámetro `response_model`. Pero eso también significa que no obtenemos el soporte del editor y las herramientas verificando el tipo de retorno de la función.
Pero en la mayoría de los casos en los que necesitamos hacer algo como esto, queremos que el modelo solo **filtre/elimine** algunos de los datos como en este ejemplo.
Y en esos casos, podemos usar clases y herencia para aprovechar las **anotaciones de tipos** de funciones para obtener mejor soporte en el editor y herramientas, y aún así obtener el **filtrado de datos** de FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Con esto, obtenemos soporte de las herramientas, de los editores y mypy ya que este código es correcto en términos de tipos, pero también obtenemos el filtrado de datos de FastAPI.
¿Cómo funciona esto? Vamos a echarle un vistazo. 🤓
### Anotaciones de Tipos y Herramientas { #type-annotations-and-tooling }
Primero vamos a ver cómo los editores, mypy y otras herramientas verían esto.
`BaseUser` tiene los campos base. Luego `UserIn` hereda de `BaseUser` y añade el campo `password`, por lo que incluirá todos los campos de ambos modelos.
Anotamos el tipo de retorno de la función como `BaseUser`, pero en realidad estamos devolviendo un `UserIn` instance.
El editor, mypy y otras herramientas no se quejarán de esto porque, en términos de tipificación, `UserIn` es una subclase de `BaseUser`, lo que significa que es un tipo *válido* cuando se espera algo que es un `BaseUser`.
### Filtrado de Datos en FastAPI { #fastapi-data-filtering }
Ahora, para FastAPI, verá el tipo de retorno y se asegurará de que lo que devuelves incluya **solo** los campos que están declarados en el tipo.
FastAPI realiza varias cosas internamente con Pydantic para asegurarse de que esas mismas reglas de herencia de clases no se utilicen para el filtrado de datos devueltos, de lo contrario, podrías terminar devolviendo muchos más datos de los que esperabas.
De esta manera, puedes obtener lo mejor de ambos mundos: anotaciones de tipos con **soporte de herramientas** y **filtrado de datos**.
## Verlo en la documentación { #see-it-in-the-docs }
Cuando veas la documentación automática, puedes verificar que el modelo de entrada y el modelo de salida tendrán cada uno su propio JSON Schema:
Y ambos modelos se utilizarán para la documentación interactiva de la API:
## Otras Anotaciones de Tipos de Retorno { #other-return-type-annotations }
Podría haber casos en los que devuelvas algo que no es un campo válido de Pydantic y lo anotes en la función, solo para obtener el soporte proporcionado por las herramientas (el editor, mypy, etc).
### Devolver un Response Directamente { #return-a-response-directly }
El caso más común sería [devolver un Response directamente como se explica más adelante en la documentación avanzada](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Este caso simple es manejado automáticamente por FastAPI porque la anotación del tipo de retorno es la clase (o una subclase de) `Response`.
Y las herramientas también estarán felices porque tanto `RedirectResponse` como `JSONResponse` son subclases de `Response`, por lo que la anotación del tipo es correcta.
### Anotar una Subclase de Response { #annotate-a-response-subclass }
También puedes usar una subclase de `Response` en la anotación del tipo:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Esto también funcionará porque `RedirectResponse` es una subclase de `Response`, y FastAPI manejará automáticamente este caso simple.
### Anotaciones de Tipos de Retorno Inválidas { #invalid-return-type-annotations }
Pero cuando devuelves algún otro objeto arbitrario que no es un tipo válido de Pydantic (por ejemplo, un objeto de base de datos) y lo anotas así en la función, FastAPI intentará crear un modelo de response de Pydantic a partir de esa anotación de tipo, y fallará.
Lo mismo sucedería si tuvieras algo como una unión entre diferentes tipos donde uno o más de ellos no son tipos válidos de Pydantic, por ejemplo esto fallaría 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...esto falla porque la anotación de tipo no es un tipo de Pydantic y no es solo una sola clase `Response` o subclase, es una unión (cualquiera de los dos) entre una `Response` y un `dict`.
### Desactivar el Modelo de Response { #disable-response-model }
Continuando con el ejemplo anterior, puede que no quieras tener la validación de datos por defecto, documentación, filtrado, etc. que realiza FastAPI.
Pero puedes querer mantener la anotación del tipo de retorno en la función para obtener el soporte de herramientas como editores y verificadores de tipos (por ejemplo, mypy).
En este caso, puedes desactivar la generación del modelo de response configurando `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Esto hará que FastAPI omita la generación del modelo de response y de esa manera puedes tener cualquier anotación de tipo de retorno que necesites sin que afecte a tu aplicación FastAPI. 🤓
## Parámetros de codificación del Modelo de Response { #response-model-encoding-parameters }
Tu modelo de response podría tener valores por defecto, como:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (o `str | None = None` en Python 3.10) tiene un valor por defecto de `None`.
* `tax: float = 10.5` tiene un valor por defecto de `10.5`.
* `tags: List[str] = []` tiene un valor por defecto de una list vacía: `[]`.
pero podrías querer omitirlos del resultado si no fueron en realidad almacenados.
Por ejemplo, si tienes modelos con muchos atributos opcionales en una base de datos NoSQL, pero no quieres enviar responses JSON muy largos llenos de valores por defecto.
### Usa el parámetro `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Puedes configurar el parámetro del decorador de path operation `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
y esos valores por defecto no serán incluidos en el response, solo los valores realmente establecidos.
Entonces, si envías un request a esa *path operation* para el ítem con ID `foo`, el response (no incluyendo valores por defecto) será:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Información
También puedes usar:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
como se describe en [la documentación de Pydantic](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) para `exclude_defaults` y `exclude_none`.
///
#### Datos con valores para campos con valores por defecto { #data-with-values-for-fields-with-defaults }
Pero si tus datos tienen valores para los campos del modelo con valores por defecto, como el artículo con ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
serán incluidos en el response.
#### Datos con los mismos valores que los valores por defecto { #data-with-the-same-values-as-the-defaults }
Si los datos tienen los mismos valores que los valores por defecto, como el artículo con ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI es lo suficientemente inteligente (de hecho, Pydantic es lo suficientemente inteligente) para darse cuenta de que, a pesar de que `description`, `tax` y `tags` tienen los mismos valores que los valores por defecto, fueron establecidos explícitamente (en lugar de tomados de los valores por defecto).
Por lo tanto, se incluirán en el response JSON.
/// tip | Consejo
Ten en cuenta que los valores por defecto pueden ser cualquier cosa, no solo `None`.
Pueden ser una list (`[]`), un `float` de `10.5`, etc.
///
### `response_model_include` y `response_model_exclude` { #response-model-include-and-response-model-exclude }
También puedes usar los parámetros del decorador de path operation `response_model_include` y `response_model_exclude`.
Aceptan un `set` de `str` con el nombre de los atributos a incluir (omitiendo el resto) o excluir (incluyendo el resto).
Esto se puede usar como un atajo rápido si solo tienes un modelo de Pydantic y quieres eliminar algunos datos de la salida.
/// tip | Consejo
Pero todavía se recomienda usar las ideas anteriores, usando múltiples clases, en lugar de estos parámetros.
Esto se debe a que el JSON Schema generado en el OpenAPI de tu aplicación (y la documentación) aún será el del modelo completo, incluso si usas `response_model_include` o `response_model_exclude` para omitir algunos atributos.
Esto también se aplica a `response_model_by_alias` que funciona de manera similar.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Consejo
La sintaxis `{"name", "description"}` crea un `set` con esos dos valores.
Es equivalente a `set(["name", "description"])`.
///
#### Usar `list`s en lugar de `set`s { #using-lists-instead-of-sets }
Si olvidas usar un `set` y usas un `list` o `tuple` en su lugar, FastAPI todavía lo convertirá a un `set` y funcionará correctamente:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Resumen { #recap }
Usa el parámetro `response_model` del *decorador de path operation* para definir modelos de response y especialmente para asegurarte de que los datos privados sean filtrados.
Usa `response_model_exclude_unset` para devolver solo los valores establecidos explícitamente.
================================================
FILE: docs/es/docs/tutorial/response-status-code.md
================================================
# Código de Estado del Response { #response-status-code }
De la misma manera que puedes especificar un modelo de response, también puedes declarar el código de estado HTTP usado para el response con el parámetro `status_code` en cualquiera de las *path operations*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Nota
Observa que `status_code` es un parámetro del método "decorador" (`get`, `post`, etc). No de tu *path operation function*, como todos los parámetros y body.
///
El parámetro `status_code` recibe un número con el código de estado HTTP.
/// info | Información
`status_code` también puede recibir un `IntEnum`, como por ejemplo el [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) de Python.
///
Esto hará:
* Devolver ese código de estado en el response.
* Documentarlo como tal en el esquema de OpenAPI (y por lo tanto, en las interfaces de usuario):
/// note | Nota
Algunos códigos de response (ver la siguiente sección) indican que el response no tiene un body.
FastAPI sabe esto, y producirá documentación OpenAPI que establece que no hay un response body.
///
## Acerca de los códigos de estado HTTP { #about-http-status-codes }
/// note | Nota
Si ya sabes qué son los códigos de estado HTTP, salta a la siguiente sección.
///
En HTTP, envías un código de estado numérico de 3 dígitos como parte del response.
Estos códigos de estado tienen un nombre asociado para reconocerlos, pero la parte importante es el número.
En breve:
* `100 - 199` son para "Información". Rara vez los usas directamente. Los responses con estos códigos de estado no pueden tener un body.
* **`200 - 299`** son para responses "Exitosos". Estos son los que usarías más.
* `200` es el código de estado por defecto, lo que significa que todo estaba "OK".
* Otro ejemplo sería `201`, "Created". Comúnmente se usa después de crear un nuevo registro en la base de datos.
* Un caso especial es `204`, "No Content". Este response se usa cuando no hay contenido para devolver al cliente, por lo tanto, el response no debe tener un body.
* **`300 - 399`** son para "Redirección". Los responses con estos códigos de estado pueden o no tener un body, excepto `304`, "Not Modified", que no debe tener uno.
* **`400 - 499`** son para responses de "Error del Cliente". Este es el segundo tipo que probablemente más usarías.
* Un ejemplo es `404`, para un response "Not Found".
* Para errores genéricos del cliente, puedes usar simplemente `400`.
* `500 - 599` son para errores del servidor. Casi nunca los usas directamente. Cuando algo sale mal en alguna parte de tu código de aplicación, o del servidor, automáticamente devolverá uno de estos códigos de estado.
/// tip | Consejo
Para saber más sobre cada código de estado y qué código es para qué, revisa la [documentación de MDN sobre códigos de estado HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Atajo para recordar los nombres { #shortcut-to-remember-the-names }
Veamos de nuevo el ejemplo anterior:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` es el código de estado para "Created".
Pero no tienes que memorizar lo que significa cada uno de estos códigos.
Puedes usar las variables de conveniencia de `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Son solo una conveniencia, mantienen el mismo número, pero de esa manera puedes usar el autocompletado del editor para encontrarlos:
/// note | Detalles técnicos
También podrías usar `from starlette import status`.
**FastAPI** proporciona el mismo `starlette.status` como `fastapi.status` solo como una conveniencia para ti, el desarrollador. Pero proviene directamente de Starlette.
///
## Cambiando el valor por defecto { #changing-the-default }
Más adelante, en la [Guía de Usuario Avanzada](../advanced/response-change-status-code.md), verás cómo devolver un código de estado diferente al valor por defecto que estás declarando aquí.
================================================
FILE: docs/es/docs/tutorial/schema-extra-example.md
================================================
# Declarar Datos de Ejemplo de Request { #declare-request-example-data }
Puedes declarar ejemplos de los datos que tu aplicación puede recibir.
Aquí tienes varias formas de hacerlo.
## Datos extra de JSON Schema en modelos de Pydantic { #extra-json-schema-data-in-pydantic-models }
Puedes declarar `examples` para un modelo de Pydantic que se añadirá al JSON Schema generado.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Esa información extra se añadirá tal cual al **JSON Schema** resultante para ese modelo, y se usará en la documentación de la API.
Puedes usar el atributo `model_config` que toma un `dict` como se describe en [Documentación de Pydantic: Configuración](https://docs.pydantic.dev/latest/api/config/).
Puedes establecer `"json_schema_extra"` con un `dict` que contenga cualquier dato adicional que te gustaría que aparezca en el JSON Schema generado, incluyendo `examples`.
/// tip | Consejo
Podrías usar la misma técnica para extender el JSON Schema y añadir tu propia información extra personalizada.
Por ejemplo, podrías usarlo para añadir metadatos para una interfaz de usuario frontend, etc.
///
/// info | Información
OpenAPI 3.1.0 (usado desde FastAPI 0.99.0) añadió soporte para `examples`, que es parte del estándar de **JSON Schema**.
Antes de eso, solo soportaba la palabra clave `example` con un solo ejemplo. Eso aún es soportado por OpenAPI 3.1.0, pero está obsoleto y no es parte del estándar de JSON Schema. Así que se te anima a migrar `example` a `examples`. 🤓
Puedes leer más al final de esta página.
///
## Argumentos adicionales en `Field` { #field-additional-arguments }
Cuando usas `Field()` con modelos de Pydantic, también puedes declarar `examples` adicionales:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` en JSON Schema - OpenAPI { #examples-in-json-schema-openapi }
Cuando usas cualquiera de:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
también puedes declarar un grupo de `examples` con información adicional que se añadirá a sus **JSON Schemas** dentro de **OpenAPI**.
### `Body` con `examples` { #body-with-examples }
Aquí pasamos `examples` que contiene un ejemplo de los datos esperados en `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Ejemplo en la interfaz de documentación { #example-in-the-docs-ui }
Con cualquiera de los métodos anteriores se vería así en los `/docs`:
### `Body` con múltiples `examples` { #body-with-multiple-examples }
Por supuesto, también puedes pasar múltiples `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Cuando haces esto, los ejemplos serán parte del **JSON Schema** interno para esos datos del body.
Sin embargo, al momento de escribir esto, Swagger UI, la herramienta encargada de mostrar la interfaz de documentación, no soporta mostrar múltiples ejemplos para los datos en **JSON Schema**. Pero lee más abajo para una solución alternativa.
### `examples` específicos de OpenAPI { #openapi-specific-examples }
Desde antes de que **JSON Schema** soportara `examples`, OpenAPI tenía soporte para un campo diferente también llamado `examples`.
Estos `examples` específicos de **OpenAPI** van en otra sección en la especificación de OpenAPI. Van en los **detalles para cada *path operation***, no dentro de cada JSON Schema.
Y Swagger UI ha soportado este campo particular de `examples` por un tiempo. Así que, puedes usarlo para **mostrar** diferentes **ejemplos en la interfaz de documentación**.
La forma de este campo específico de OpenAPI `examples` es un `dict` con **múltiples ejemplos** (en lugar de una `list`), cada uno con información adicional que también se añadirá a **OpenAPI**.
Esto no va dentro de cada JSON Schema contenido en OpenAPI, esto va afuera, directamente en la *path operation*.
### Usando el Parámetro `openapi_examples` { #using-the-openapi-examples-parameter }
Puedes declarar los `examples` específicos de OpenAPI en FastAPI con el parámetro `openapi_examples` para:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
Las claves del `dict` identifican cada ejemplo, y cada valor es otro `dict`.
Cada `dict` específico del ejemplo en los `examples` puede contener:
* `summary`: Descripción corta del ejemplo.
* `description`: Una descripción larga que puede contener texto Markdown.
* `value`: Este es el ejemplo real mostrado, e.g. un `dict`.
* `externalValue`: alternativa a `value`, una URL que apunta al ejemplo. Aunque esto puede no ser soportado por tantas herramientas como `value`.
Puedes usarlo así:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Ejemplos de OpenAPI en la Interfaz de Documentación { #openapi-examples-in-the-docs-ui }
Con `openapi_examples` añadido a `Body()`, los `/docs` se verían así:
## Detalles Técnicos { #technical-details }
/// tip | Consejo
Si ya estás usando la versión **0.99.0 o superior** de **FastAPI**, probablemente puedes **omitir** estos detalles.
Son más relevantes para versiones más antiguas, antes de que OpenAPI 3.1.0 estuviera disponible.
Puedes considerar esto una breve lección de **historia** de OpenAPI y JSON Schema. 🤓
///
/// warning | Advertencia
Estos son detalles muy técnicos sobre los estándares **JSON Schema** y **OpenAPI**.
Si las ideas anteriores ya funcionan para ti, eso podría ser suficiente, y probablemente no necesites estos detalles, siéntete libre de omitirlos.
///
Antes de OpenAPI 3.1.0, OpenAPI usaba una versión más antigua y modificada de **JSON Schema**.
JSON Schema no tenía `examples`, así que OpenAPI añadió su propio campo `example` a su versión modificada.
OpenAPI también añadió los campos `example` y `examples` a otras partes de la especificación:
* [`Parameter Object` (en la especificación)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object) que era usado por FastAPI:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, en el campo `content`, sobre el `Media Type Object` (en la especificación)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object) que era usado por FastAPI:
* `Body()`
* `File()`
* `Form()`
/// info | Información
Este viejo parámetro `examples` específico de OpenAPI ahora es `openapi_examples` desde FastAPI `0.103.0`.
///
### Campo `examples` de JSON Schema { #json-schemas-examples-field }
Pero luego JSON Schema añadió un [campo `examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) a una nueva versión de la especificación.
Y entonces el nuevo OpenAPI 3.1.0 se basó en la última versión (JSON Schema 2020-12) que incluía este nuevo campo `examples`.
Y ahora este nuevo campo `examples` tiene precedencia sobre el viejo campo único (y personalizado) `example`, que ahora está obsoleto.
Este nuevo campo `examples` en JSON Schema es **solo una `list`** de ejemplos, no un dict con metadatos adicionales como en los otros lugares en OpenAPI (descritos arriba).
/// info | Información
Incluso después de que OpenAPI 3.1.0 fue lanzado con esta nueva integración más sencilla con JSON Schema, por un tiempo, Swagger UI, la herramienta que proporciona la documentación automática, no soportaba OpenAPI 3.1.0 (lo hace desde la versión 5.0.0 🎉).
Debido a eso, las versiones de FastAPI anteriores a 0.99.0 todavía usaban versiones de OpenAPI menores a 3.1.0.
///
### `examples` de Pydantic y FastAPI { #pydantic-and-fastapi-examples }
Cuando añades `examples` dentro de un modelo de Pydantic, usando `schema_extra` o `Field(examples=["something"])`, ese ejemplo se añade al **JSON Schema** para ese modelo de Pydantic.
Y ese **JSON Schema** del modelo de Pydantic se incluye en el **OpenAPI** de tu API, y luego se usa en la interfaz de documentación.
En las versiones de FastAPI antes de 0.99.0 (0.99.0 y superiores usan el nuevo OpenAPI 3.1.0) cuando usabas `example` o `examples` con cualquiera de las otras utilidades (`Query()`, `Body()`, etc.) esos ejemplos no se añadían al JSON Schema que describe esos datos (ni siquiera a la propia versión de JSON Schema de OpenAPI), se añadían directamente a la declaración de la *path operation* en OpenAPI (fuera de las partes de OpenAPI que usan JSON Schema).
Pero ahora que FastAPI 0.99.0 y superiores usa OpenAPI 3.1.0, que usa JSON Schema 2020-12, y Swagger UI 5.0.0 y superiores, todo es más consistente y los ejemplos se incluyen en JSON Schema.
### Swagger UI y `examples` específicos de OpenAPI { #swagger-ui-and-openapi-specific-examples }
Ahora, como Swagger UI no soportaba múltiples ejemplos de JSON Schema (a fecha de 2023-08-26), los usuarios no tenían una forma de mostrar múltiples ejemplos en la documentación.
Para resolver eso, FastAPI `0.103.0` **añadió soporte** para declarar el mismo viejo campo **específico de OpenAPI** `examples` con el nuevo parámetro `openapi_examples`. 🤓
### Resumen { #summary }
Solía decir que no me gustaba mucho la historia... y mírame ahora dando lecciones de "historia tecnológica". 😅
En resumen, **actualiza a FastAPI 0.99.0 o superior**, y las cosas son mucho **más simples, consistentes e intuitivas**, y no necesitas conocer todos estos detalles históricos. 😎
================================================
FILE: docs/es/docs/tutorial/security/first-steps.md
================================================
# Seguridad - Primeros pasos { #security-first-steps }
Imaginemos que tienes tu API de **backend** en algún dominio.
Y tienes un **frontend** en otro dominio o en un path diferente del mismo dominio (o en una aplicación móvil).
Y quieres tener una forma para que el frontend se autentique con el backend, usando un **username** y **password**.
Podemos usar **OAuth2** para construir eso con **FastAPI**.
Pero vamos a ahorrarte el tiempo de leer la larga especificación completa solo para encontrar esos pequeños fragmentos de información que necesitas.
Usemos las herramientas proporcionadas por **FastAPI** para manejar la seguridad.
## Cómo se ve { #how-it-looks }
Primero solo usemos el código y veamos cómo funciona, y luego volveremos para entender qué está sucediendo.
## Crea `main.py` { #create-main-py }
Copia el ejemplo en un archivo `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Ejecútalo { #run-it }
/// info | Información
El paquete [`python-multipart`](https://github.com/Kludex/python-multipart) se instala automáticamente con **FastAPI** cuando ejecutas el comando `pip install "fastapi[standard]"`.
Sin embargo, si usas el comando `pip install fastapi`, el paquete `python-multipart` no se incluye por defecto.
Para instalarlo manualmente, asegúrate de crear un [entorno virtual](../../virtual-environments.md), activarlo, y luego instalarlo con:
```console
$ pip install python-multipart
```
Esto se debe a que **OAuth2** utiliza "form data" para enviar el `username` y `password`.
///
Ejecuta el ejemplo con:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Revisa { #check-it }
Ve a la documentación interactiva en: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás algo así:
/// check | ¡Botón de autorización!
Ya tienes un nuevo y brillante botón de "Authorize".
Y tu *path operation* tiene un pequeño candado en la esquina superior derecha que puedes pulsar.
///
Y si lo haces, tendrás un pequeño formulario de autorización para escribir un `username` y `password` (y otros campos opcionales):
/// note | Nota
No importa lo que escribas en el formulario, aún no funcionará. Pero llegaremos allí.
///
Esto por supuesto no es el frontend para los usuarios finales, pero es una gran herramienta automática para documentar interactivamente toda tu API.
Puede ser utilizada por el equipo de frontend (que también puedes ser tú mismo).
Puede ser utilizada por aplicaciones y sistemas de terceros.
Y también puede ser utilizada por ti mismo, para depurar, revisar y probar la misma aplicación.
## El flujo `password` { #the-password-flow }
Ahora retrocedamos un poco y entendamos qué es todo eso.
El "flujo" `password` es una de las formas ("flujos") definidas en OAuth2, para manejar la seguridad y la autenticación.
OAuth2 fue diseñado para que el backend o la API pudieran ser independientes del servidor que autentica al usuario.
Pero en este caso, la misma aplicación de **FastAPI** manejará la API y la autenticación.
Así que, revisémoslo desde ese punto de vista simplificado:
* El usuario escribe el `username` y `password` en el frontend, y presiona `Enter`.
* El frontend (ejecutándose en el navegador del usuario) envía ese `username` y `password` a una URL específica en nuestra API (declarada con `tokenUrl="token"`).
* La API verifica ese `username` y `password`, y responde con un "token" (no hemos implementado nada de esto aún).
* Un "token" es solo un string con algún contenido que podemos usar luego para verificar a este usuario.
* Normalmente, un token se establece para que expire después de algún tiempo.
* Así que, el usuario tendrá que volver a iniciar sesión más adelante.
* Y si el token es robado, el riesgo es menor. No es como una llave permanente que funcionará para siempre (en la mayoría de los casos).
* El frontend almacena temporalmente ese token en algún lugar.
* El usuario hace clic en el frontend para ir a otra sección de la aplicación web frontend.
* El frontend necesita obtener más datos de la API.
* Pero necesita autenticación para ese endpoint específico.
* Así que, para autenticarse con nuestra API, envía un `header` `Authorization` con un valor de `Bearer ` más el token.
* Si el token contiene `foobar`, el contenido del `header` `Authorization` sería: `Bearer foobar`.
## `OAuth2PasswordBearer` de **FastAPI** { #fastapis-oauth2passwordbearer }
**FastAPI** proporciona varias herramientas, en diferentes niveles de abstracción, para implementar estas funcionalidades de seguridad.
En este ejemplo vamos a usar **OAuth2**, con el flujo **Password**, usando un token **Bearer**. Hacemos eso utilizando la clase `OAuth2PasswordBearer`.
/// info | Información
Un token "bearer" no es la única opción.
Pero es la mejor para nuestro caso de uso.
Y podría ser la mejor para la mayoría de los casos de uso, a menos que seas un experto en OAuth2 y sepas exactamente por qué hay otra opción que se adapta mejor a tus necesidades.
En ese caso, **FastAPI** también te proporciona las herramientas para construirlo.
///
Cuando creamos una instance de la clase `OAuth2PasswordBearer` pasamos el parámetro `tokenUrl`. Este parámetro contiene la URL que el cliente (el frontend corriendo en el navegador del usuario) usará para enviar el `username` y `password` a fin de obtener un token.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Consejo
Aquí `tokenUrl="token"` se refiere a una URL relativa `token` que aún no hemos creado. Como es una URL relativa, es equivalente a `./token`.
Porque estamos usando una URL relativa, si tu API estuviera ubicada en `https://example.com/`, entonces se referiría a `https://example.com/token`. Pero si tu API estuviera ubicada en `https://example.com/api/v1/`, entonces se referiría a `https://example.com/api/v1/token`.
Usar una URL relativa es importante para asegurarse de que tu aplicación siga funcionando incluso en un caso de uso avanzado como [Detrás de un Proxy](../../advanced/behind-a-proxy.md).
///
Este parámetro no crea ese endpoint / *path operation*, pero declara que la URL `/token` será la que el cliente deberá usar para obtener el token. Esa información se usa en OpenAPI, y luego en los sistemas de documentación interactiva del API.
Pronto también crearemos la verdadera *path operation*.
/// info | Información
Si eres un "Pythonista" muy estricto, tal vez no te guste el estilo del nombre del parámetro `tokenUrl` en lugar de `token_url`.
Eso es porque está usando el mismo nombre que en la especificación de OpenAPI. Para que si necesitas investigar más sobre cualquiera de estos esquemas de seguridad, puedas simplemente copiarlo y pegarlo para encontrar más información al respecto.
///
La variable `oauth2_scheme` es una instance de `OAuth2PasswordBearer`, pero también es un "callable".
Podría ser llamada como:
```Python
oauth2_scheme(some, parameters)
```
Así que, puede usarse con `Depends`.
### Úsalo { #use-it }
Ahora puedes pasar ese `oauth2_scheme` en una dependencia con `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Esta dependencia proporcionará un `str` que se asigna al parámetro `token` de la *path operation function*.
**FastAPI** sabrá que puede usar esta dependencia para definir un "security scheme" en el esquema OpenAPI (y en los docs automáticos del API).
/// info | Detalles técnicos
**FastAPI** sabrá que puede usar la clase `OAuth2PasswordBearer` (declarada en una dependencia) para definir el esquema de seguridad en OpenAPI porque hereda de `fastapi.security.oauth2.OAuth2`, que a su vez hereda de `fastapi.security.base.SecurityBase`.
Todas las utilidades de seguridad que se integran con OpenAPI (y los docs automáticos del API) heredan de `SecurityBase`, así es como **FastAPI** puede saber cómo integrarlas en OpenAPI.
///
## Lo que hace { #what-it-does }
Irá y buscará en el request ese header `Authorization`, verificará si el valor es `Bearer ` más algún token, y devolverá el token como un `str`.
Si no ve un header `Authorization`, o el valor no tiene un token `Bearer `, responderá directamente con un error de código de estado 401 (`UNAUTHORIZED`).
Ni siquiera tienes que verificar si el token existe para devolver un error. Puedes estar seguro de que si tu función se ejecuta, tendrá un `str` en ese token.
Puedes probarlo ya en los docs interactivos:
Todavía no estamos verificando la validez del token, pero ya es un comienzo.
## Resumen { #recap }
Así que, en solo 3 o 4 líneas adicionales, ya tienes alguna forma primitiva de seguridad.
================================================
FILE: docs/es/docs/tutorial/security/get-current-user.md
================================================
# Obtener Usuario Actual { #get-current-user }
En el capítulo anterior, el sistema de seguridad (que se basa en el sistema de inyección de dependencias) le estaba dando a la *path operation function* un `token` como un `str`:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Pero eso aún no es tan útil. Vamos a hacer que nos dé el usuario actual.
## Crear un modelo de usuario { #create-a-user-model }
Primero, vamos a crear un modelo de usuario con Pydantic.
De la misma manera que usamos Pydantic para declarar cuerpos, podemos usarlo en cualquier otra parte:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Crear una dependencia `get_current_user` { #create-a-get-current-user-dependency }
Vamos a crear una dependencia `get_current_user`.
¿Recuerdas que las dependencias pueden tener sub-dependencias?
`get_current_user` tendrá una dependencia con el mismo `oauth2_scheme` que creamos antes.
De la misma manera que estábamos haciendo antes en la *path operation* directamente, nuestra nueva dependencia `get_current_user` recibirá un `token` como un `str` de la sub-dependencia `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Obtener el usuario { #get-the-user }
`get_current_user` usará una función de utilidad (falsa) que creamos, que toma un token como un `str` y devuelve nuestro modelo de Pydantic `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Inyectar al usuario actual { #inject-the-current-user }
Entonces ahora podemos usar el mismo `Depends` con nuestro `get_current_user` en la *path operation*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Ten en cuenta que declaramos el tipo de `current_user` como el modelo de Pydantic `User`.
Esto nos ayudará dentro de la función con todo el autocompletado y chequeo de tipos.
/// tip | Consejo
Tal vez recuerdes que los request bodies también se declaran con modelos de Pydantic.
Aquí **FastAPI** no se confundirá porque estás usando `Depends`.
///
/// check | Revisa
El modo en que este sistema de dependencias está diseñado nos permite tener diferentes dependencias (diferentes "dependables") que todas devuelven un modelo `User`.
No estamos restringidos a tener solo una dependencia que pueda devolver ese tipo de datos.
///
## Otros modelos { #other-models }
Ahora puedes obtener el usuario actual directamente en las *path operation functions* y manejar los mecanismos de seguridad a nivel de **Dependency Injection**, usando `Depends`.
Y puedes usar cualquier modelo o datos para los requisitos de seguridad (en este caso, un modelo de Pydantic `User`).
Pero no estás limitado a usar algún modelo de datos, clase o tipo específico.
¿Quieres tener un `id` y `email` y no tener un `username` en tu modelo? Claro. Puedes usar estas mismas herramientas.
¿Quieres solo tener un `str`? ¿O solo un `dict`? ¿O un instance de clase modelo de base de datos directamente? Todo funciona de la misma manera.
¿En realidad no tienes usuarios que inicien sesión en tu aplicación sino robots, bots u otros sistemas, que solo tienen un token de acceso? Una vez más, todo funciona igual.
Usa cualquier tipo de modelo, cualquier tipo de clase, cualquier tipo de base de datos que necesites para tu aplicación. **FastAPI** te cubre con el sistema de inyección de dependencias.
## Tamaño del código { #code-size }
Este ejemplo podría parecer extenso. Ten en cuenta que estamos mezclando seguridad, modelos de datos, funciones de utilidad y *path operations* en el mismo archivo.
Pero aquí está el punto clave.
El tema de seguridad e inyección de dependencias se escribe una vez.
Y puedes hacerlo tan complejo como desees. Y aún así, tenerlo escrito solo una vez, en un solo lugar. Con toda la flexibilidad.
Pero puedes tener miles de endpoints (*path operations*) usando el mismo sistema de seguridad.
Y todos ellos (o cualquier porción de ellos que quieras) pueden aprovechar la reutilización de estas dependencias o cualquier otra dependencia que crees.
Y todas estas miles de *path operations* pueden ser tan pequeñas como 3 líneas:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Resumen { #recap }
Ahora puedes obtener el usuario actual directamente en tu *path operation function*.
Ya estamos a mitad de camino.
Solo necesitamos agregar una *path operation* para que el usuario/cliente envíe realmente el `username` y `password`.
Eso es lo que viene a continuación.
================================================
FILE: docs/es/docs/tutorial/security/index.md
================================================
# Seguridad { #security }
Hay muchas formas de manejar la seguridad, autenticación y autorización.
Y normalmente es un tema complejo y "difícil".
En muchos frameworks y sistemas, solo manejar la seguridad y autenticación requiere una gran cantidad de esfuerzo y código (en muchos casos puede ser el 50% o más de todo el código escrito).
**FastAPI** proporciona varias herramientas para ayudarte a manejar la **Seguridad** de manera fácil, rápida y estándar, sin tener que estudiar y aprender todas las especificaciones de seguridad.
Pero primero, vamos a revisar algunos pequeños conceptos.
## ¿Con prisa? { #in-a-hurry }
Si no te importan ninguno de estos términos y solo necesitas agregar seguridad con autenticación basada en nombre de usuario y contraseña *ahora mismo*, salta a los siguientes capítulos.
## OAuth2 { #oauth2 }
OAuth2 es una especificación que define varias maneras de manejar la autenticación y autorización.
Es una especificación bastante extensa y cubre varios casos de uso complejos.
Incluye formas de autenticarse usando un "tercero".
Eso es lo que todos los sistemas con "iniciar sesión con Facebook, Google, X (Twitter), GitHub" utilizan internamente.
### OAuth 1 { #oauth-1 }
Hubo un OAuth 1, que es muy diferente de OAuth2, y más complejo, ya que incluía especificaciones directas sobre cómo encriptar la comunicación.
No es muy popular o usado hoy en día.
OAuth2 no especifica cómo encriptar la comunicación, espera que tengas tu aplicación servida con HTTPS.
/// tip | Consejo
En la sección sobre **deployment** verás cómo configurar HTTPS de forma gratuita, usando Traefik y Let's Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect es otra especificación, basada en **OAuth2**.
Solo extiende OAuth2 especificando algunas cosas que son relativamente ambiguas en OAuth2, para intentar hacerla más interoperable.
Por ejemplo, el login de Google usa OpenID Connect (que internamente usa OAuth2).
Pero el login de Facebook no soporta OpenID Connect. Tiene su propia versión de OAuth2.
### OpenID (no "OpenID Connect") { #openid-not-openid-connect }
Hubo también una especificación "OpenID". Que intentaba resolver lo mismo que **OpenID Connect**, pero no estaba basada en OAuth2.
Entonces, era un sistema completo adicional.
No es muy popular o usado hoy en día.
## OpenAPI { #openapi }
OpenAPI (anteriormente conocido como Swagger) es la especificación abierta para construir APIs (ahora parte de la Linux Foundation).
**FastAPI** se basa en **OpenAPI**.
Eso es lo que hace posible tener múltiples interfaces de documentación interactiva automática, generación de código, etc.
OpenAPI tiene una forma de definir múltiples "esquemas" de seguridad.
Al usarlos, puedes aprovechar todas estas herramientas basadas en estándares, incluidos estos sistemas de documentación interactiva.
OpenAPI define los siguientes esquemas de seguridad:
* `apiKey`: una clave específica de la aplicación que puede provenir de:
* Un parámetro de query.
* Un header.
* Una cookie.
* `http`: sistemas de autenticación HTTP estándar, incluyendo:
* `bearer`: un header `Authorization` con un valor de `Bearer ` más un token. Esto se hereda de OAuth2.
* Autenticación básica HTTP.
* Digest HTTP, etc.
* `oauth2`: todas las formas de OAuth2 para manejar la seguridad (llamadas "flujos").
* Varios de estos flujos son apropiados para construir un proveedor de autenticación OAuth 2.0 (como Google, Facebook, X (Twitter), GitHub, etc.):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Pero hay un "flujo" específico que puede usarse perfectamente para manejar la autenticación directamente en la misma aplicación:
* `password`: algunos de los próximos capítulos cubrirán ejemplos de esto.
* `openIdConnect`: tiene una forma de definir cómo descubrir automáticamente los datos de autenticación OAuth2.
* Este descubrimiento automático es lo que se define en la especificación de OpenID Connect.
/// tip | Consejo
Integrar otros proveedores de autenticación/autorización como Google, Facebook, X (Twitter), GitHub, etc. también es posible y relativamente fácil.
El problema más complejo es construir un proveedor de autenticación/autorización como esos, pero **FastAPI** te da las herramientas para hacerlo fácilmente, mientras hace el trabajo pesado por ti.
///
## Utilidades de **FastAPI** { #fastapi-utilities }
FastAPI proporciona varias herramientas para cada uno de estos esquemas de seguridad en el módulo `fastapi.security` que simplifican el uso de estos mecanismos de seguridad.
En los siguientes capítulos verás cómo agregar seguridad a tu API usando esas herramientas proporcionadas por **FastAPI**.
Y también verás cómo se integra automáticamente en el sistema de documentación interactiva.
================================================
FILE: docs/es/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 con Password (y hashing), Bearer con tokens JWT { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Ahora que tenemos todo el flujo de seguridad, hagamos que la aplicación sea realmente segura, usando tokens JWT y hashing de contraseñas seguras.
Este código es algo que puedes usar realmente en tu aplicación, guardar los hashes de las contraseñas en tu base de datos, etc.
Vamos a empezar desde donde lo dejamos en el capítulo anterior e incrementarlo.
## Acerca de JWT { #about-jwt }
JWT significa "JSON Web Tokens".
Es un estándar para codificar un objeto JSON en un string largo y denso sin espacios. Se ve así:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
No está encriptado, por lo que cualquiera podría recuperar la información de los contenidos.
Pero está firmado. Así que, cuando recibes un token que has emitido, puedes verificar que realmente lo emitiste.
De esta manera, puedes crear un token con una expiración de, digamos, 1 semana. Y luego, cuando el usuario regresa al día siguiente con el token, sabes que el usuario todavía está registrado en tu sistema.
Después de una semana, el token estará expirado y el usuario no estará autorizado y tendrá que iniciar sesión nuevamente para obtener un nuevo token. Y si el usuario (o un tercero) intenta modificar el token para cambiar la expiración, podrás descubrirlo, porque las firmas no coincidirían.
Si quieres jugar con tokens JWT y ver cómo funcionan, revisa [https://jwt.io](https://jwt.io/).
## Instalar `PyJWT` { #install-pyjwt }
Necesitamos instalar `PyJWT` para generar y verificar los tokens JWT en Python.
Asegúrate de crear un [entorno virtual](../../virtual-environments.md), activarlo y luego instalar `pyjwt`:
```console
$ pip install pyjwt
---> 100%
```
/// info | Información
Si planeas usar algoritmos de firma digital como RSA o ECDSA, deberías instalar la dependencia del paquete de criptografía `pyjwt[crypto]`.
Puedes leer más al respecto en la [documentación de instalación de PyJWT](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Hashing de contraseñas { #password-hashing }
"Hacer hashing" significa convertir algún contenido (una contraseña en este caso) en una secuencia de bytes (solo un string) que parece un galimatías.
Siempre que pases exactamente el mismo contenido (exactamente la misma contraseña) obtienes exactamente el mismo galimatías.
Pero no puedes convertir del galimatías de nuevo a la contraseña.
### Por qué usar hashing de contraseñas { #why-use-password-hashing }
Si tu base de datos es robada, el ladrón no tendrá las contraseñas en texto claro de tus usuarios, solo los hashes.
Por lo tanto, el ladrón no podrá intentar usar esa contraseña en otro sistema (como muchos usuarios usan la misma contraseña en todas partes, esto sería peligroso).
## Instalar `pwdlib` { #install-pwdlib }
pwdlib es un gran paquete de Python para manejar hashes de contraseñas.
Soporta muchos algoritmos de hashing seguros y utilidades para trabajar con ellos.
El algoritmo recomendado es "Argon2".
Asegúrate de crear un [entorno virtual](../../virtual-environments.md), activarlo y luego instalar pwdlib con Argon2:
/// tip | Consejo
Con `pwdlib`, incluso podrías configurarlo para poder leer contraseñas creadas por **Django**, un plug-in de seguridad de **Flask** u otros muchos.
Así, podrías, por ejemplo, compartir los mismos datos de una aplicación de Django en una base de datos con una aplicación de FastAPI. O migrar gradualmente una aplicación de Django usando la misma base de datos.
Y tus usuarios podrían iniciar sesión desde tu aplicación Django o desde tu aplicación **FastAPI**, al mismo tiempo.
///
## Hash y verificación de contraseñas { #hash-and-verify-the-passwords }
Importa las herramientas que necesitamos de `pwdlib`.
Crea un instance PasswordHash con configuraciones recomendadas: se usará para hacer el hash y verificar las contraseñas.
/// tip | Consejo
pwdlib también soporta el algoritmo de hashing bcrypt pero no incluye algoritmos legacy; para trabajar con hashes desactualizados, se recomienda usar el paquete passlib.
Por ejemplo, podrías usarlo para leer y verificar contraseñas generadas por otro sistema (como Django) pero hacer hash de cualquier contraseña nueva con un algoritmo diferente como Argon2 o Bcrypt.
Y ser compatible con todos ellos al mismo tiempo.
///
Crea una función de utilidad para hacer el hash de una contraseña que venga del usuario.
Y otra utilidad para verificar si una contraseña recibida coincide con el hash almacenado.
Y otra más para autenticar y devolver un usuario.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Cuando `authenticate_user` se llama con un nombre de usuario que no existe en la base de datos, aun así ejecutamos `verify_password` contra un hash ficticio.
Esto asegura que el endpoint tarda aproximadamente la misma cantidad de tiempo en responder tanto si el nombre de usuario es válido como si no, previniendo los **timing attacks** que podrían usarse para enumerar nombres de usuario existentes.
/// note | Nota
Si revisas la nueva (falsa) base de datos `fake_users_db`, verás cómo se ve ahora la contraseña con hash: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Manejo de tokens JWT { #handle-jwt-tokens }
Importa los módulos instalados.
Crea una clave secreta aleatoria que se usará para firmar los tokens JWT.
Para generar una clave secreta segura al azar usa el comando:
Y copia el resultado a la variable `SECRET_KEY` (no uses la del ejemplo).
Crea una variable `ALGORITHM` con el algoritmo usado para firmar el token JWT y configúralo a `"HS256"`.
Crea una variable para la expiración del token.
Define un Modelo de Pydantic que se usará en el endpoint de token para el response.
Crea una función de utilidad para generar un nuevo token de acceso.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Actualizar las dependencias { #update-the-dependencies }
Actualiza `get_current_user` para recibir el mismo token que antes, pero esta vez, usando tokens JWT.
Decodifica el token recibido, verifícalo y devuelve el usuario actual.
Si el token es inválido, devuelve un error HTTP de inmediato.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Actualizar la *path operation* `/token` { #update-the-token-path-operation }
Crea un `timedelta` con el tiempo de expiración del token.
Crea un verdadero token de acceso JWT y devuélvelo.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Detalles técnicos sobre el "sujeto" `sub` de JWT { #technical-details-about-the-jwt-subject-sub }
La especificación de JWT dice que hay una clave `sub`, con el sujeto del token.
Es opcional usarlo, pero ahí es donde pondrías la identificación del usuario, por lo que lo estamos usando aquí.
JWT podría ser usado para otras cosas aparte de identificar un usuario y permitirle realizar operaciones directamente en tu API.
Por ejemplo, podrías identificar un "coche" o un "artículo de blog".
Luego, podrías agregar permisos sobre esa entidad, como "conducir" (para el coche) o "editar" (para el blog).
Y luego, podrías darle ese token JWT a un usuario (o bot), y ellos podrían usarlo para realizar esas acciones (conducir el coche, o editar el artículo del blog) sin siquiera necesitar tener una cuenta, solo con el token JWT que tu API generó para eso.
Usando estas ideas, JWT puede ser utilizado para escenarios mucho más sofisticados.
En esos casos, varias de esas entidades podrían tener el mismo ID, digamos `foo` (un usuario `foo`, un coche `foo`, y un artículo del blog `foo`).
Entonces, para evitar colisiones de ID, cuando crees el token JWT para el usuario, podrías prefijar el valor de la clave `sub`, por ejemplo, con `username:`. Así, en este ejemplo, el valor de `sub` podría haber sido: `username:johndoe`.
Lo importante a tener en cuenta es que la clave `sub` debería tener un identificador único a lo largo de toda la aplicación, y debería ser un string.
## Revisa { #check-it }
Ejecuta el servidor y ve a la documentación: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Verás la interfaz de usuario como:
Autoriza la aplicación de la misma manera que antes.
Usando las credenciales:
Usuario: `johndoe`
Contraseña: `secret`
/// check | Revisa
Observa que en ninguna parte del código está la contraseña en texto claro "`secret`", solo tenemos la versión con hash.
///
Llama al endpoint `/users/me/`, obtendrás el response como:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Si abres las herramientas de desarrollador, podrías ver cómo los datos enviados solo incluyen el token, la contraseña solo se envía en la primera request para autenticar al usuario y obtener ese token de acceso, pero no después:
/// note | Nota
Observa el header `Authorization`, con un valor que comienza con `Bearer `.
///
## Uso avanzado con `scopes` { #advanced-usage-with-scopes }
OAuth2 tiene la noción de "scopes".
Puedes usarlos para agregar un conjunto específico de permisos a un token JWT.
Luego, puedes darle este token directamente a un usuario o a un tercero, para interactuar con tu API con un conjunto de restricciones.
Puedes aprender cómo usarlos y cómo están integrados en **FastAPI** más adelante en la **Guía de Usuario Avanzada**.
## Resumen { #recap }
Con lo que has visto hasta ahora, puedes configurar una aplicación **FastAPI** segura usando estándares como OAuth2 y JWT.
En casi cualquier framework el manejo de la seguridad se convierte en un tema bastante complejo rápidamente.
Muchos paquetes que lo simplifican tienen que hacer muchos compromisos con el modelo de datos, la base de datos y las funcionalidades disponibles. Y algunos de estos paquetes que simplifican las cosas demasiado en realidad tienen fallos de seguridad en el fondo.
---
**FastAPI** no hace ningún compromiso con ninguna base de datos, modelo de datos o herramienta.
Te da toda la flexibilidad para elegir aquellas que se ajusten mejor a tu proyecto.
Y puedes usar directamente muchos paquetes bien mantenidos y ampliamente usados como `pwdlib` y `PyJWT`, porque **FastAPI** no requiere mecanismos complejos para integrar paquetes externos.
Pero te proporciona las herramientas para simplificar el proceso tanto como sea posible sin comprometer la flexibilidad, la robustez o la seguridad.
Y puedes usar e implementar protocolos seguros y estándar, como OAuth2 de una manera relativamente simple.
Puedes aprender más en la **Guía de Usuario Avanzada** sobre cómo usar "scopes" de OAuth2, para un sistema de permisos más detallado, siguiendo estos mismos estándares. OAuth2 con scopes es el mecanismo utilizado por muchos grandes proveedores de autenticación, como Facebook, Google, GitHub, Microsoft, X (Twitter), etc. para autorizar aplicaciones de terceros para interactuar con sus APIs en nombre de sus usuarios.
================================================
FILE: docs/es/docs/tutorial/security/simple-oauth2.md
================================================
# Simple OAuth2 con Password y Bearer { #simple-oauth2-with-password-and-bearer }
Ahora vamos a construir a partir del capítulo anterior y agregar las partes faltantes para tener un flujo de seguridad completo.
## Obtener el `username` y `password` { #get-the-username-and-password }
Vamos a usar las utilidades de seguridad de **FastAPI** para obtener el `username` y `password`.
OAuth2 especifica que cuando se utiliza el "password flow" (que estamos usando), el cliente/usuario debe enviar campos `username` y `password` como form data.
Y la especificación dice que los campos deben llamarse así. Por lo que `user-name` o `email` no funcionarían.
Pero no te preocupes, puedes mostrarlo como quieras a tus usuarios finales en el frontend.
Y tus modelos de base de datos pueden usar cualquier otro nombre que desees.
Pero para la *path operation* de inicio de sesión, necesitamos usar estos nombres para ser compatibles con la especificación (y poder, por ejemplo, utilizar el sistema de documentación integrada de la API).
La especificación también establece que el `username` y `password` deben enviarse como form data (por lo que no hay JSON aquí).
### `scope` { #scope }
La especificación también indica que el cliente puede enviar otro campo del formulario llamado "`scope`".
El nombre del campo del formulario es `scope` (en singular), pero en realidad es un string largo con "scopes" separados por espacios.
Cada "scope" es simplemente un string (sin espacios).
Normalmente se utilizan para declarar permisos de seguridad específicos, por ejemplo:
* `users:read` o `users:write` son ejemplos comunes.
* `instagram_basic` es usado por Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` es usado por Google.
/// info | Información
En OAuth2 un "scope" es solo un string que declara un permiso específico requerido.
No importa si tiene otros caracteres como `:` o si es una URL.
Esos detalles son específicos de la implementación.
Para OAuth2 son solo strings.
///
## Código para obtener el `username` y `password` { #code-to-get-the-username-and-password }
Ahora vamos a usar las utilidades proporcionadas por **FastAPI** para manejar esto.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Primero, importa `OAuth2PasswordRequestForm`, y úsalo como una dependencia con `Depends` en la *path operation* para `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` es una dependencia de clase que declara un body de formulario con:
* El `username`.
* El `password`.
* Un campo opcional `scope` como un string grande, compuesto por strings separados por espacios.
* Un `grant_type` opcional.
/// tip | Consejo
La especificación de OAuth2 en realidad *requiere* un campo `grant_type` con un valor fijo de `password`, pero `OAuth2PasswordRequestForm` no lo obliga.
Si necesitas imponerlo, utiliza `OAuth2PasswordRequestFormStrict` en lugar de `OAuth2PasswordRequestForm`.
///
* Un `client_id` opcional (no lo necesitamos para nuestro ejemplo).
* Un `client_secret` opcional (no lo necesitamos para nuestro ejemplo).
/// info | Información
`OAuth2PasswordRequestForm` no es una clase especial para **FastAPI** como lo es `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` hace que **FastAPI** sepa que es un esquema de seguridad. Así que se añade de esa manera a OpenAPI.
Pero `OAuth2PasswordRequestForm` es solo una dependencia de clase que podrías haber escrito tú mismo, o podrías haber declarado parámetros de `Form` directamente.
Pero como es un caso de uso común, se proporciona directamente por **FastAPI**, solo para facilitarlo.
///
### Usa el form data { #use-the-form-data }
/// tip | Consejo
La instance de la clase de dependencia `OAuth2PasswordRequestForm` no tendrá un atributo `scope` con el string largo separado por espacios, en su lugar, tendrá un atributo `scopes` con la lista real de strings para cada scope enviado.
No estamos usando `scopes` en este ejemplo, pero la funcionalidad está ahí si la necesitas.
///
Ahora, obtén los datos del usuario desde la base de datos (falsa), usando el `username` del campo del form.
Si no existe tal usuario, devolvemos un error diciendo "Incorrect username or password".
Para el error, usamos la excepción `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Revisa el password { #check-the-password }
En este punto tenemos los datos del usuario de nuestra base de datos, pero no hemos revisado el password.
Primero pongamos esos datos en el modelo `UserInDB` de Pydantic.
Nunca deberías guardar passwords en texto plano, así que, usaremos el sistema de hash de passwords (falso).
Si los passwords no coinciden, devolvemos el mismo error.
#### Hashing de passwords { #password-hashing }
"Hacer hash" significa: convertir algún contenido (un password en este caso) en una secuencia de bytes (solo un string) que parece un galimatías.
Siempre que pases exactamente el mismo contenido (exactamente el mismo password) obtienes exactamente el mismo galimatías.
Pero no puedes convertir del galimatías al password.
##### Por qué usar hashing de passwords { #why-use-password-hashing }
Si tu base de datos es robada, el ladrón no tendrá los passwords en texto plano de tus usuarios, solo los hashes.
Entonces, el ladrón no podrá intentar usar esos mismos passwords en otro sistema (como muchos usuarios usan el mismo password en todas partes, esto sería peligroso).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Sobre `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` significa:
*Pasa las claves y valores de `user_dict` directamente como argumentos clave-valor, equivalente a:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Información
Para una explicación más completa de `**user_dict` revisa en [la documentación para **Extra Models**](../extra-models.md#about-user-in-dict).
///
## Devolver el token { #return-the-token }
El response del endpoint `token` debe ser un objeto JSON.
Debe tener un `token_type`. En nuestro caso, como estamos usando tokens "Bearer", el tipo de token debe ser "`bearer`".
Y debe tener un `access_token`, con un string que contenga nuestro token de acceso.
Para este ejemplo simple, vamos a ser completamente inseguros y devolver el mismo `username` como el token.
/// tip | Consejo
En el próximo capítulo, verás una implementación segura real, con hashing de passwords y tokens JWT.
Pero por ahora, enfoquémonos en los detalles específicos que necesitamos.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Consejo
De acuerdo con la especificación, deberías devolver un JSON con un `access_token` y un `token_type`, igual que en este ejemplo.
Esto es algo que tienes que hacer tú mismo en tu código, y asegurarte de usar esas claves JSON.
Es casi lo único que tienes que recordar hacer correctamente tú mismo, para ser compatible con las especificaciones.
Para el resto, **FastAPI** lo maneja por ti.
///
## Actualizar las dependencias { #update-the-dependencies }
Ahora vamos a actualizar nuestras dependencias.
Queremos obtener el `current_user` *solo* si este usuario está activo.
Entonces, creamos una dependencia adicional `get_current_active_user` que a su vez utiliza `get_current_user` como dependencia.
Ambas dependencias solo devolverán un error HTTP si el usuario no existe, o si está inactivo.
Así que, en nuestro endpoint, solo obtendremos un usuario si el usuario existe, fue autenticado correctamente, y está activo:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Información
El header adicional `WWW-Authenticate` con el valor `Bearer` que estamos devolviendo aquí también es parte de la especificación.
Cualquier código de estado HTTP (error) 401 "UNAUTHORIZED" se supone que también debe devolver un header `WWW-Authenticate`.
En el caso de tokens bearer (nuestro caso), el valor de ese header debe ser `Bearer`.
De hecho, puedes omitir ese header extra y aún funcionaría.
Pero se proporciona aquí para cumplir con las especificaciones.
Además, podría haber herramientas que lo esperen y lo usen (ahora o en el futuro) y eso podría ser útil para ti o tus usuarios, ahora o en el futuro.
Ese es el beneficio de los estándares...
///
## Verlo en acción { #see-it-in-action }
Abre la documentación interactiva: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Autenticar { #authenticate }
Haz clic en el botón "Authorize".
Usa las credenciales:
Usuario: `johndoe`
Contraseña: `secret`
Después de autenticarte en el sistema, lo verás así:
### Obtener tus propios datos de usuario { #get-your-own-user-data }
Ahora usa la operación `GET` con la path `/users/me`.
Obtendrás los datos de tu usuario, como:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Si haces clic en el icono de candado y cierras sesión, y luego intentas la misma operación nuevamente, obtendrás un error HTTP 401 de:
```JSON
{
"detail": "Not authenticated"
}
```
### Usuario inactivo { #inactive-user }
Ahora prueba con un usuario inactivo, autentícate con:
Usuario: `alice`
Contraseña: `secret2`
Y trata de usar la operación `GET` con la path `/users/me`.
Obtendrás un error de "Usuario inactivo", como:
```JSON
{
"detail": "Inactive user"
}
```
## Recapitulación { #recap }
Ahora tienes las herramientas para implementar un sistema de seguridad completo basado en `username` y `password` para tu API.
Usando estas herramientas, puedes hacer que el sistema de seguridad sea compatible con cualquier base de datos y con cualquier modelo de usuario o de datos.
El único detalle que falta es que en realidad no es "seguro" aún.
En el próximo capítulo verás cómo usar un paquete de hashing de passwords seguro y tokens JWT.
================================================
FILE: docs/es/docs/tutorial/server-sent-events.md
================================================
# Server-Sent Events (SSE) { #server-sent-events-sse }
Puedes enviar datos en streaming al cliente usando **Server-Sent Events** (SSE).
Esto es similar a [Stream JSON Lines](stream-json-lines.md), pero usa el formato `text/event-stream`, que los navegadores soportan de forma nativa con la [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info | Información
Añadido en FastAPI 0.135.0.
///
## ¿Qué son los Server-Sent Events? { #what-are-server-sent-events }
SSE es un estándar para hacer streaming de datos desde el servidor al cliente sobre HTTP.
Cada evento es un pequeño bloque de texto con “campos” como `data`, `event`, `id` y `retry`, separados por líneas en blanco.
Se ve así:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE se usa comúnmente para streaming de chat de IA, notificaciones en vivo, logs y observabilidad, y otros casos donde el servidor envía actualizaciones al cliente.
/// tip | Consejo
Si quieres hacer streaming de datos binarios, por ejemplo video o audio, Revisa la guía avanzada: [Stream Data](../advanced/stream-data.md).
///
## Streaming de SSE con FastAPI { #stream-sse-with-fastapi }
Para hacer streaming de SSE con FastAPI, usa `yield` en tu path operation function y establece `response_class=EventSourceResponse`.
import `EventSourceResponse` de `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Cada ítem producido con `yield` se codifica como JSON y se envía en el campo `data:` de un evento SSE.
Si declaras el tipo de retorno como `AsyncIterable[Item]`, FastAPI lo usará para **validar**, **documentar** y **serializar** los datos usando Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Consejo
Como Pydantic lo serializará en el lado de **Rust**, obtendrás un **rendimiento** mucho mayor que si no declaras un tipo de retorno.
///
### No async *path operation functions* { #non-async-path-operation-functions }
También puedes usar funciones `def` normales (sin `async`), y usar `yield` de la misma manera.
FastAPI se asegurará de ejecutarlo correctamente para que no bloquee el event loop.
Como en este caso la función no es async, el tipo de retorno correcto sería `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Sin tipo de retorno { #no-return-type }
También puedes omitir el tipo de retorno. FastAPI usará el [`jsonable_encoder`](./encoder.md) para convertir los datos y enviarlos.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Si necesitas configurar campos SSE como `event`, `id`, `retry` o `comment`, puedes hacer `yield` de objetos `ServerSentEvent` en lugar de datos simples.
import `ServerSentEvent` de `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
El campo `data` siempre se codifica como JSON. Puedes pasar cualquier valor que pueda serializarse como JSON, incluidos modelos de Pydantic.
## Datos sin procesar { #raw-data }
Si necesitas enviar datos **sin** codificarlos a JSON, usa `raw_data` en lugar de `data`.
Esto es útil para enviar texto preformateado, líneas de log, o valores especiales de "centinela" como `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Nota
`data` y `raw_data` son mutuamente excluyentes. Solo puedes establecer uno de ellos en cada `ServerSentEvent`.
///
## Reanudar con `Last-Event-ID` { #resuming-with-last-event-id }
Cuando un navegador se reconecta después de una caída de la conexión, envía el último `id` recibido en el header `Last-Event-ID`.
Puedes leerlo como un parámetro de header y usarlo para reanudar el stream desde donde el cliente se quedó:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE con `POST` { #sse-with-post }
SSE funciona con **cualquier método HTTP**, no solo con `GET`.
Esto es útil para protocolos como [MCP](https://modelcontextprotocol.io) que hacen streaming de SSE sobre `POST`:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Detalles técnicos { #technical-details }
FastAPI implementa algunas mejores prácticas de SSE desde el primer momento.
- Enviar un comentario de **"keep alive" `ping`** cada 15 segundos cuando no ha habido ningún mensaje, para evitar que algunos proxies cierren la conexión, como se sugiere en la [Especificación HTML: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
- Configurar el header `Cache-Control: no-cache` para **evitar el almacenamiento en caché** del stream.
- Configurar un header especial `X-Accel-Buffering: no` para **evitar el buffering** en algunos proxies como Nginx.
No tienes que hacer nada, funciona tal cual viene. 🤓
================================================
FILE: docs/es/docs/tutorial/sql-databases.md
================================================
# Bases de Datos SQL (Relacionales) { #sql-relational-databases }
**FastAPI** no requiere que uses una base de datos SQL (relacional). Pero puedes utilizar **cualquier base de datos** que desees.
Aquí veremos un ejemplo usando [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** está construido sobre [SQLAlchemy](https://www.sqlalchemy.org/) y Pydantic. Fue creado por el mismo autor de **FastAPI** para ser la combinación perfecta para aplicaciones de FastAPI que necesiten usar **bases de datos SQL**.
/// tip | Consejo
Puedes usar cualquier otro paquete de bases de datos SQL o NoSQL que quieras (en algunos casos llamadas "ORMs"), FastAPI no te obliga a usar nada. 😎
///
Como SQLModel se basa en SQLAlchemy, puedes usar fácilmente **cualquier base de datos soportada** por SQLAlchemy (lo que las hace también soportadas por SQLModel), como:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, etc.
En este ejemplo, usaremos **SQLite**, porque utiliza un solo archivo y Python tiene soporte integrado. Así que puedes copiar este ejemplo y ejecutarlo tal cual.
Más adelante, para tu aplicación en producción, es posible que desees usar un servidor de base de datos como **PostgreSQL**.
/// tip | Consejo
Hay un generador de proyectos oficial con **FastAPI** y **PostgreSQL** que incluye un frontend y más herramientas: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Este es un tutorial muy simple y corto, si deseas aprender sobre bases de datos en general, sobre SQL o más funcionalidades avanzadas, ve a la [documentación de SQLModel](https://sqlmodel.tiangolo.com/).
## Instalar `SQLModel` { #install-sqlmodel }
Primero, asegúrate de crear tu [entorno virtual](../virtual-environments.md), actívalo, y luego instala `sqlmodel`:
```console
$ pip install sqlmodel
---> 100%
```
## Crear la App con un Solo Modelo { #create-the-app-with-a-single-model }
Primero crearemos la versión más simple de la aplicación con un solo modelo de **SQLModel**.
Más adelante la mejoraremos aumentando la seguridad y versatilidad con **múltiples modelos** a continuación. 🤓
### Crear Modelos { #create-models }
Importa `SQLModel` y crea un modelo de base de datos:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
La clase `Hero` es muy similar a un modelo de Pydantic (de hecho, en el fondo, realmente *es un modelo de Pydantic*).
Hay algunas diferencias:
* `table=True` le dice a SQLModel que este es un *modelo de tabla*, que debe representar una **tabla** en la base de datos SQL, no es solo un *modelo de datos* (como lo sería cualquier otra clase regular de Pydantic).
* `Field(primary_key=True)` le dice a SQLModel que `id` es la **clave primaria** en la base de datos SQL (puedes aprender más sobre claves primarias de SQL en la documentación de SQLModel).
Nota: Usamos `int | None` para el campo de clave primaria para que en el código Python podamos *crear un objeto sin un `id`* (`id=None`), asumiendo que la base de datos lo *generará al guardar*. SQLModel entiende que la base de datos proporcionará el `id` y *define la columna como un `INTEGER` no nulo* en el esquema de la base de datos. Consulta la [documentación de SQLModel sobre claves primarias](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) para más detalles.
* `Field(index=True)` le dice a SQLModel que debe crear un **índice SQL** para esta columna, lo que permitirá búsquedas más rápidas en la base de datos cuando se lean datos filtrados por esta columna.
SQLModel sabrá que algo declarado como `str` será una columna SQL de tipo `TEXT` (o `VARCHAR`, dependiendo de la base de datos).
### Crear un Engine { #create-an-engine }
Un `engine` de SQLModel (en el fondo, realmente es un `engine` de SQLAlchemy) es lo que **mantiene las conexiones** a la base de datos.
Tendrías **un solo objeto `engine`** para todo tu código para conectar a la misma base de datos.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Usar `check_same_thread=False` permite a FastAPI usar la misma base de datos SQLite en diferentes hilos. Esto es necesario ya que **una sola request** podría usar **más de un hilo** (por ejemplo, en dependencias).
No te preocupes, con la forma en que está estructurado el código, nos aseguraremos de usar **una sola *session* de SQLModel por request** más adelante, esto es realmente lo que intenta lograr el `check_same_thread`.
### Crear las Tablas { #create-the-tables }
Luego añadimos una función que usa `SQLModel.metadata.create_all(engine)` para **crear las tablas** para todos los *modelos de tabla*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Crear una Dependencia de Session { #create-a-session-dependency }
Una **`Session`** es lo que almacena los **objetos en memoria** y lleva un seguimiento de cualquier cambio necesario en los datos, luego **usa el `engine`** para comunicarse con la base de datos.
Crearemos una **dependencia de FastAPI** con `yield` que proporcionará una nueva `Session` para cada request. Esto es lo que asegura que usemos una sola session por request. 🤓
Luego creamos una dependencia `Annotated` `SessionDep` para simplificar el resto del código que usará esta dependencia.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Crear Tablas de Base de Datos al Arrancar { #create-database-tables-on-startup }
Crearemos las tablas de la base de datos cuando arranque la aplicación.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Aquí creamos las tablas en un evento de inicio de la aplicación.
Para producción probablemente usarías un script de migración que se ejecuta antes de iniciar tu aplicación. 🤓
/// tip | Consejo
SQLModel tendrá utilidades de migración envolviendo Alembic, pero por ahora, puedes usar [Alembic](https://alembic.sqlalchemy.org/en/latest/) directamente.
///
### Crear un Hero { #create-a-hero }
Debido a que cada modelo de SQLModel también es un modelo de Pydantic, puedes usarlo en las mismas **anotaciones de tipos** que podrías usar en modelos de Pydantic.
Por ejemplo, si declaras un parámetro de tipo `Hero`, será leído desde el **JSON body**.
De la misma manera, puedes declararlo como el **tipo de retorno** de la función, y luego la forma de los datos aparecerá en la interfaz automática de documentación de la API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Aquí usamos la dependencia `SessionDep` (una `Session`) para añadir el nuevo `Hero` a la instance `Session`, comiteamos los cambios a la base de datos, refrescamos los datos en el `hero` y luego lo devolvemos.
### Leer Heroes { #read-heroes }
Podemos **leer** `Hero`s de la base de datos usando un `select()`. Podemos incluir un `limit` y `offset` para paginar los resultados.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Leer Un Hero { #read-one-hero }
Podemos **leer** un único `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Eliminar un Hero { #delete-a-hero }
También podemos **eliminar** un `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Ejecutar la App { #run-the-app }
Puedes ejecutar la aplicación:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Luego dirígete a la interfaz de `/docs`, verás que **FastAPI** está usando estos **modelos** para **documentar** la API, y los usará para **serializar** y **validar** los datos también.
## Actualizar la App con Múltiples Modelos { #update-the-app-with-multiple-models }
Ahora vamos a **refactorizar** un poco esta aplicación para aumentar la **seguridad** y la **versatilidad**.
Si revisas la aplicación anterior, en la interfaz verás que, hasta ahora, permite al cliente decidir el `id` del `Hero` a crear. 😱
No deberíamos permitir que eso suceda, podrían sobrescribir un `id` que ya tenemos asignado en la base de datos. Decidir el `id` debería ser tarea del **backend** o la **base de datos**, **no del cliente**.
Además, creamos un `secret_name` para el héroe, pero hasta ahora, lo estamos devolviendo en todas partes, eso no es muy **secreto**... 😅
Arreglaremos estas cosas añadiendo unos **modelos extra**. Aquí es donde SQLModel brillará. ✨
### Crear Múltiples Modelos { #create-multiple-models }
En **SQLModel**, cualquier clase de modelo que tenga `table=True` es un **modelo de tabla**.
Y cualquier clase de modelo que no tenga `table=True` es un **modelo de datos**, estos son en realidad solo modelos de Pydantic (con un par de características extra pequeñas). 🤓
Con SQLModel, podemos usar **herencia** para **evitar duplicar** todos los campos en todos los casos.
#### `HeroBase` - la clase base { #herobase-the-base-class }
Comencemos con un modelo `HeroBase` que tiene todos los **campos que son compartidos** por todos los modelos:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - el *modelo de tabla* { #hero-the-table-model }
Luego, crearemos `Hero`, el *modelo de tabla* real, con los **campos extra** que no siempre están en los otros modelos:
* `id`
* `secret_name`
Debido a que `Hero` hereda de `HeroBase`, **también** tiene los **campos** declarados en `HeroBase`, por lo que todos los campos para `Hero` son:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - el *modelo de datos* público { #heropublic-the-public-data-model }
A continuación, creamos un modelo `HeroPublic`, este es el que será **devuelto** a los clientes de la API.
Tiene los mismos campos que `HeroBase`, por lo que no incluirá `secret_name`.
Por fin, la identidad de nuestros héroes está protegida! 🥷
También vuelve a declarar `id: int`. Al hacer esto, estamos haciendo un **contrato** con los clientes de la API, para que siempre puedan esperar que el `id` esté allí y sea un `int` (nunca será `None`).
/// tip | Consejo
Tener el modelo de retorno asegurando que un valor siempre esté disponible y siempre sea `int` (no `None`) es muy útil para los clientes de la API, pueden escribir código mucho más simple teniendo esta certeza.
Además, los **clientes generados automáticamente** tendrán interfaces más simples, para que los desarrolladores que se comuniquen con tu API puedan tener una experiencia mucho mejor trabajando con tu API. 😎
///
Todos los campos en `HeroPublic` son los mismos que en `HeroBase`, con `id` declarado como `int` (no `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - el *modelo de datos* para crear un héroe { #herocreate-the-data-model-to-create-a-hero }
Ahora creamos un modelo `HeroCreate`, este es el que **validará** los datos de los clientes.
Tiene los mismos campos que `HeroBase`, y también tiene `secret_name`.
Ahora, cuando los clientes **crean un nuevo héroe**, enviarán el `secret_name`, se almacenará en la base de datos, pero esos nombres secretos no se devolverán en la API a los clientes.
/// tip | Consejo
Esta es la forma en la que manejarías **contraseñas**. Recíbelas, pero no las devuelvas en la API.
También **hashea** los valores de las contraseñas antes de almacenarlos, **nunca los almacenes en texto plano**.
///
Los campos de `HeroCreate` son:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - el *modelo de datos* para actualizar un héroe { #heroupdate-the-data-model-to-update-a-hero }
No teníamos una forma de **actualizar un héroe** en la versión anterior de la aplicación, pero ahora con **múltiples modelos**, podemos hacerlo. 🎉
El *modelo de datos* `HeroUpdate` es algo especial, tiene **todos los mismos campos** que serían necesarios para crear un nuevo héroe, pero todos los campos son **opcionales** (todos tienen un valor por defecto). De esta forma, cuando actualices un héroe, puedes enviar solo los campos que deseas actualizar.
Debido a que todos los **campos realmente cambian** (el tipo ahora incluye `None` y ahora tienen un valor por defecto de `None`), necesitamos **volver a declararlos**.
Realmente no necesitamos heredar de `HeroBase` porque estamos volviendo a declarar todos los campos. Lo dejaré heredando solo por consistencia, pero esto no es necesario. Es más una cuestión de gusto personal. 🤷
Los campos de `HeroUpdate` son:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Crear con `HeroCreate` y devolver un `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Ahora que tenemos **múltiples modelos**, podemos actualizar las partes de la aplicación que los usan.
Recibimos en la request un *modelo de datos* `HeroCreate`, y a partir de él, creamos un *modelo de tabla* `Hero`.
Este nuevo *modelo de tabla* `Hero` tendrá los campos enviados por el cliente, y también tendrá un `id` generado por la base de datos.
Luego devolvemos el mismo *modelo de tabla* `Hero` tal cual desde la función. Pero como declaramos el `response_model` con el *modelo de datos* `HeroPublic`, **FastAPI** usará `HeroPublic` para validar y serializar los datos.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Consejo
Ahora usamos `response_model=HeroPublic` en lugar de la **anotación de tipo de retorno** `-> HeroPublic` porque el valor que estamos devolviendo en realidad *no* es un `HeroPublic`.
Si hubiéramos declarado `-> HeroPublic`, tu editor y linter se quejarían (con razón) de que estás devolviendo un `Hero` en lugar de un `HeroPublic`.
Al declararlo en `response_model` le estamos diciendo a **FastAPI** que haga lo suyo, sin interferir con las anotaciones de tipo y la ayuda de tu editor y otras herramientas.
///
### Leer Heroes con `HeroPublic` { #read-heroes-with-heropublic }
Podemos hacer lo mismo que antes para **leer** `Hero`s, nuevamente, usamos `response_model=list[HeroPublic]` para asegurar que los datos se validen y serialicen correctamente.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Leer Un Hero con `HeroPublic` { #read-one-hero-with-heropublic }
Podemos **leer** un único héroe:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Actualizar un Hero con `HeroUpdate` { #update-a-hero-with-heroupdate }
Podemos **actualizar un héroe**. Para esto usamos una operación HTTP `PATCH`.
Y en el código, obtenemos un `dict` con todos los datos enviados por el cliente, **solo los datos enviados por el cliente**, excluyendo cualquier valor que estaría allí solo por ser valores por defecto. Para hacerlo usamos `exclude_unset=True`. Este es el truco principal. 🪄
Luego usamos `hero_db.sqlmodel_update(hero_data)` para actualizar el `hero_db` con los datos de `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Eliminar un Hero de Nuevo { #delete-a-hero-again }
**Eliminar** un héroe se mantiene prácticamente igual.
No satisfaremos el deseo de refactorizar todo en este punto. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Ejecutar la App de Nuevo { #run-the-app-again }
Puedes ejecutar la aplicación de nuevo:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Si vas a la interfaz de `/docs` de la API, verás que ahora está actualizada, y no esperará recibir el `id` del cliente al crear un héroe, etc.
## Resumen { #recap }
Puedes usar [**SQLModel**](https://sqlmodel.tiangolo.com/) para interactuar con una base de datos SQL y simplificar el código con *modelos de datos* y *modelos de tablas*.
Puedes aprender mucho más en la documentación de **SQLModel**, hay un mini [tutorial más largo sobre el uso de SQLModel con **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/es/docs/tutorial/static-files.md
================================================
# Archivos Estáticos { #static-files }
Puedes servir archivos estáticos automáticamente desde un directorio utilizando `StaticFiles`.
## Usa `StaticFiles` { #use-staticfiles }
* Importa `StaticFiles`.
* "Monta" una instance de `StaticFiles()` en un path específico.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Detalles Técnicos
También podrías usar `from starlette.staticfiles import StaticFiles`.
**FastAPI** proporciona el mismo `starlette.staticfiles` como `fastapi.staticfiles` solo como una conveniencia para ti, el desarrollador. Pero en realidad viene directamente de Starlette.
///
### Qué es "Montar" { #what-is-mounting }
"Montar" significa agregar una aplicación completa "independiente" en un path específico, que luego se encargará de manejar todos los sub-paths.
Esto es diferente a usar un `APIRouter`, ya que una aplicación montada es completamente independiente. El OpenAPI y la documentación de tu aplicación principal no incluirán nada de la aplicación montada, etc.
Puedes leer más sobre esto en la [Guía de Usuario Avanzada](../advanced/index.md).
## Detalles { #details }
El primer `"/static"` se refiere al sub-path en el que esta "sub-aplicación" será "montada". Por lo tanto, cualquier path que comience con `"/static"` será manejado por ella.
El `directory="static"` se refiere al nombre del directorio que contiene tus archivos estáticos.
El `name="static"` le da un nombre que puede ser utilizado internamente por **FastAPI**.
Todos estos parámetros pueden ser diferentes a "`static`", ajústalos según las necesidades y detalles específicos de tu propia aplicación.
## Más info { #more-info }
Para más detalles y opciones revisa [la documentación de Starlette sobre Archivos Estáticos](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/es/docs/tutorial/stream-json-lines.md
================================================
# Transmitir JSON Lines { #stream-json-lines }
Podrías tener una secuencia de datos que quieras enviar en un "**stream**", podrías hacerlo con **JSON Lines**.
/// info | Información
Añadido en FastAPI 0.134.0.
///
## ¿Qué es un Stream? { #what-is-a-stream }
Hacer "**Streaming**" de datos significa que tu app empezará a enviar ítems de datos al cliente sin esperar a que toda la secuencia de ítems esté lista.
Entonces, enviará el primer ítem, el cliente lo recibirá y empezará a procesarlo, y tú podrías seguir produciendo el siguiente ítem.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Incluso podría ser un stream infinito, donde sigues enviando datos.
## JSON Lines { #json-lines }
En estos casos, es común enviar "**JSON Lines**", que es un formato donde envías un objeto JSON por línea.
Una response tendría un tipo de contenido `application/jsonl` (en lugar de `application/json`) y el response body sería algo como:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
Es muy similar a un array JSON (equivalente de una list de Python), pero en lugar de estar envuelto en `[]` y tener `,` entre los ítems, tiene **un objeto JSON por línea**, separados por un carácter de nueva línea.
/// info | Información
El punto importante es que tu app podrá producir cada línea a su turno, mientras el cliente consume las líneas anteriores.
///
/// note | Detalles técnicos
Como cada objeto JSON estará separado por una nueva línea, no pueden contener caracteres de nueva línea literales en su contenido, pero sí pueden contener nuevas líneas escapadas (`\n`), lo cual es parte del estándar JSON.
Pero normalmente no tendrás que preocuparte por eso, se hace automáticamente, sigue leyendo. 🤓
///
## Casos de uso { #use-cases }
Podrías usar esto para hacer stream de datos desde un servicio de **AI LLM**, desde **logs** o **telemetry**, o desde otros tipos de datos que puedan estructurarse en ítems **JSON**.
/// tip | Consejo
Si quieres hacer stream de datos binarios, por ejemplo video o audio, Revisa la guía avanzada: [Transmitir datos](../advanced/stream-data.md).
///
## Transmitir JSON Lines con FastAPI { #stream-json-lines-with-fastapi }
Para transmitir JSON Lines con FastAPI puedes, en lugar de usar `return` en tu *path operation function*, usar `yield` para producir cada ítem a su turno.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Si cada ítem JSON que quieres enviar de vuelta es de tipo `Item` (un modelo de Pydantic) y es una función async, puedes declarar el tipo de retorno como `AsyncIterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Si declaras el tipo de retorno, FastAPI lo usará para **validar** los datos, **documentarlos** en OpenAPI, **filtrarlos** y **serializarlos** usando Pydantic.
/// tip | Consejo
Como Pydantic lo serializará en el lado de **Rust**, obtendrás un **rendimiento** mucho mayor que si no declaras un tipo de retorno.
///
### *path operation functions* no-async { #non-async-path-operation-functions }
También puedes usar funciones `def` regulares (sin `async`), y usar `yield` de la misma forma.
FastAPI se asegurará de que se ejecute correctamente para que no bloquee el event loop.
Como en este caso la función no es async, el tipo de retorno correcto sería `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Sin tipo de retorno { #no-return-type }
También puedes omitir el tipo de retorno. Entonces FastAPI usará [`jsonable_encoder`](./encoder.md) para convertir los datos a algo que se pueda serializar a JSON y luego enviarlo como JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Server-Sent Events (SSE) { #server-sent-events-sse }
FastAPI también tiene soporte de primera clase para Server-Sent Events (SSE), que son bastante similares pero con un par de detalles extra. Puedes aprender sobre ellos en el siguiente capítulo: [Eventos enviados por el servidor (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/es/docs/tutorial/testing.md
================================================
# Testing { #testing }
Gracias a [Starlette](https://www.starlette.dev/testclient/), escribir pruebas para aplicaciones de **FastAPI** es fácil y agradable.
Está basado en [HTTPX](https://www.python-httpx.org), que a su vez está diseñado basado en Requests, por lo que es muy familiar e intuitivo.
Con él, puedes usar [pytest](https://docs.pytest.org/) directamente con **FastAPI**.
## Usando `TestClient` { #using-testclient }
/// info | Información
Para usar `TestClient`, primero instala [`httpx`](https://www.python-httpx.org).
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo y luego instalarlo, por ejemplo:
```console
$ pip install httpx
```
///
Importa `TestClient`.
Crea un `TestClient` pasándole tu aplicación de **FastAPI**.
Crea funciones con un nombre que comience con `test_` (esta es la convención estándar de `pytest`).
Usa el objeto `TestClient` de la misma manera que con `httpx`.
Escribe declaraciones `assert` simples con las expresiones estándar de Python que necesites revisar (otra vez, estándar de `pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Consejo
Nota que las funciones de prueba son `def` normales, no `async def`.
Y las llamadas al cliente también son llamadas normales, sin usar `await`.
Esto te permite usar `pytest` directamente sin complicaciones.
///
/// note | Detalles técnicos
También podrías usar `from starlette.testclient import TestClient`.
**FastAPI** proporciona el mismo `starlette.testclient` como `fastapi.testclient` solo por conveniencia para ti, el desarrollador. Pero proviene directamente de Starlette.
///
/// tip | Consejo
Si quieres llamar a funciones `async` en tus pruebas además de enviar requests a tu aplicación FastAPI (por ejemplo, funciones asincrónicas de bases de datos), echa un vistazo a las [Pruebas Asincrónicas](../advanced/async-tests.md) en el tutorial avanzado.
///
## Separando pruebas { #separating-tests }
En una aplicación real, probablemente tendrías tus pruebas en un archivo diferente.
Y tu aplicación de **FastAPI** también podría estar compuesta de varios archivos/módulos, etc.
### Archivo de aplicación **FastAPI** { #fastapi-app-file }
Digamos que tienes una estructura de archivos como se describe en [Aplicaciones Más Grandes](bigger-applications.md):
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
En el archivo `main.py` tienes tu aplicación de **FastAPI**:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Archivo de prueba { #testing-file }
Entonces podrías tener un archivo `test_main.py` con tus pruebas. Podría estar en el mismo paquete de Python (el mismo directorio con un archivo `__init__.py`):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Debido a que este archivo está en el mismo paquete, puedes usar importaciones relativas para importar el objeto `app` desde el módulo `main` (`main.py`):
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...y tener el código para las pruebas tal como antes.
## Pruebas: ejemplo extendido { #testing-extended-example }
Ahora extiende este ejemplo y añade más detalles para ver cómo escribir pruebas para diferentes partes.
### Archivo de aplicación **FastAPI** extendido { #extended-fastapi-app-file }
Continuemos con la misma estructura de archivos que antes:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Digamos que ahora el archivo `main.py` con tu aplicación de **FastAPI** tiene algunas otras **path operations**.
Tiene una operación `GET` que podría devolver un error.
Tiene una operación `POST` que podría devolver varios errores.
Ambas *path operations* requieren un `X-Token` header.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Archivo de prueba extendido { #extended-testing-file }
Podrías entonces actualizar `test_main.py` con las pruebas extendidas:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Cada vez que necesites que el cliente pase información en el request y no sepas cómo, puedes buscar (Googlear) cómo hacerlo en `httpx`, o incluso cómo hacerlo con `requests`, dado que el diseño de HTTPX está basado en el diseño de Requests.
Luego simplemente haces lo mismo en tus pruebas.
Por ejemplo:
* Para pasar un parámetro de *path* o *query*, añádelo a la URL misma.
* Para pasar un cuerpo JSON, pasa un objeto de Python (por ejemplo, un `dict`) al parámetro `json`.
* Si necesitas enviar *Form Data* en lugar de JSON, usa el parámetro `data` en su lugar.
* Para pasar *headers*, usa un `dict` en el parámetro `headers`.
* Para *cookies*, un `dict` en el parámetro `cookies`.
Para más información sobre cómo pasar datos al backend (usando `httpx` o el `TestClient`) revisa la [documentación de HTTPX](https://www.python-httpx.org).
/// info | Información
Ten en cuenta que el `TestClient` recibe datos que pueden ser convertidos a JSON, no modelos de Pydantic.
Si tienes un modelo de Pydantic en tu prueba y quieres enviar sus datos a la aplicación durante las pruebas, puedes usar el `jsonable_encoder` descrito en [Codificador Compatible con JSON](encoder.md).
///
## Ejecútalo { #run-it }
Después de eso, solo necesitas instalar `pytest`.
Asegúrate de crear un [entorno virtual](../virtual-environments.md), activarlo y luego instalarlo, por ejemplo:
```console
$ pip install pytest
---> 100%
```
Detectará los archivos y pruebas automáticamente, ejecutará las mismas y te reportará los resultados.
Ejecuta las pruebas con:
================================================
FILE: docs/es/docs/virtual-environments.md
================================================
# Entornos Virtuales { #virtual-environments }
Cuando trabajas en proyectos de Python probablemente deberías usar un **entorno virtual** (o un mecanismo similar) para aislar los paquetes que instalas para cada proyecto.
/// info | Información
Si ya sabes sobre entornos virtuales, cómo crearlos y usarlos, podrías querer saltar esta sección. 🤓
///
/// tip | Consejo
Un **entorno virtual** es diferente de una **variable de entorno**.
Una **variable de entorno** es una variable en el sistema que puede ser usada por programas.
Un **entorno virtual** es un directorio con algunos archivos en él.
///
/// info | Información
Esta página te enseñará cómo usar **entornos virtuales** y cómo funcionan.
Si estás listo para adoptar una **herramienta que gestiona todo** por ti (incluyendo la instalación de Python), prueba [uv](https://github.com/astral-sh/uv).
///
## Crea un Proyecto { #create-a-project }
Primero, crea un directorio para tu proyecto.
Lo que normalmente hago es crear un directorio llamado `code` dentro de mi directorio de usuario.
Y dentro de eso creo un directorio por proyecto.
```console
// Ve al directorio principal
$ cd
// Crea un directorio para todos tus proyectos de código
$ mkdir code
// Entra en ese directorio de código
$ cd code
// Crea un directorio para este proyecto
$ mkdir awesome-project
// Entra en ese directorio del proyecto
$ cd awesome-project
```
## Crea un Entorno Virtual { #create-a-virtual-environment }
Cuando empiezas a trabajar en un proyecto de Python **por primera vez**, crea un entorno virtual **dentro de tu proyecto**.
/// tip | Consejo
Solo necesitas hacer esto **una vez por proyecto**, no cada vez que trabajas.
///
//// tab | `venv`
Para crear un entorno virtual, puedes usar el módulo `venv` que viene con Python.
```console
$ python -m venv .venv
```
/// details | Qué significa ese comando
* `python`: usa el programa llamado `python`
* `-m`: llama a un módulo como un script, indicaremos cuál módulo a continuación
* `venv`: usa el módulo llamado `venv` que normalmente viene instalado con Python
* `.venv`: crea el entorno virtual en el nuevo directorio `.venv`
///
////
//// tab | `uv`
Si tienes instalado [`uv`](https://github.com/astral-sh/uv), puedes usarlo para crear un entorno virtual.
```console
$ uv venv
```
/// tip | Consejo
Por defecto, `uv` creará un entorno virtual en un directorio llamado `.venv`.
Pero podrías personalizarlo pasando un argumento adicional con el nombre del directorio.
///
////
Ese comando crea un nuevo entorno virtual en un directorio llamado `.venv`.
/// details | `.venv` u otro nombre
Podrías crear el entorno virtual en un directorio diferente, pero hay una convención de llamarlo `.venv`.
///
## Activa el Entorno Virtual { #activate-the-virtual-environment }
Activa el nuevo entorno virtual para que cualquier comando de Python que ejecutes o paquete que instales lo utilicen.
/// tip | Consejo
Haz esto **cada vez** que inicies una **nueva sesión de terminal** para trabajar en el proyecto.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
O si usas Bash para Windows (por ejemplo, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | Consejo
Cada vez que instales un **nuevo paquete** en ese entorno, **activa** el entorno de nuevo.
Esto asegura que si usas un programa de **terminal (CLI)** instalado por ese paquete, uses el de tu entorno virtual y no cualquier otro que podría estar instalado globalmente, probablemente con una versión diferente a la que necesitas.
///
## Revisa que el Entorno Virtual esté Activo { #check-the-virtual-environment-is-active }
Revisa que el entorno virtual esté activo (el comando anterior funcionó).
/// tip | Consejo
Esto es **opcional**, pero es una buena forma de **revisar** que todo está funcionando como se esperaba y estás usando el entorno virtual que pretendes.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Si muestra el binario de `python` en `.venv/bin/python`, dentro de tu proyecto (en este caso `awesome-project`), entonces funcionó. 🎉
////
//// tab | Windows PowerShell
Si muestra el binario de `python` en `.venv\Scripts\python`, dentro de tu proyecto (en este caso `awesome-project`), entonces funcionó. 🎉
////
## Actualiza `pip` { #upgrade-pip }
/// tip | Consejo
Si usas [`uv`](https://github.com/astral-sh/uv) usarías eso para instalar cosas en lugar de `pip`, por lo que no necesitas actualizar `pip`. 😎
///
Si estás usando `pip` para instalar paquetes (viene por defecto con Python), deberías **actualizarlo** a la última versión.
Muchos errores exóticos al instalar un paquete se resuelven simplemente actualizando `pip` primero.
/// tip | Consejo
Normalmente harías esto **una vez**, justo después de crear el entorno virtual.
///
Asegúrate de que el entorno virtual esté activo (con el comando anterior) y luego ejecuta:
/// tip | Consejo
A veces, podrías obtener un error **`No module named pip`** al intentar actualizar pip.
Si esto pasa, instala y actualiza pip usando el siguiente comando:
Este comando instalará pip si aún no está instalado y también se asegura de que la versión instalada de pip sea al menos tan reciente como la disponible en `ensurepip`.
///
## Añade `.gitignore` { #add-gitignore }
Si estás usando **Git** (deberías), añade un archivo `.gitignore` para excluir todo en tu `.venv` de Git.
/// tip | Consejo
Si usaste [`uv`](https://github.com/astral-sh/uv) para crear el entorno virtual, ya lo hizo por ti, puedes saltarte este paso. 😎
///
/// tip | Consejo
Haz esto **una vez**, justo después de crear el entorno virtual.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Qué significa ese comando
* `echo "*"`: "imprimirá" el texto `*` en el terminal (la siguiente parte cambia eso un poco)
* `>`: cualquier cosa impresa en el terminal por el comando a la izquierda de `>` no debería imprimirse, sino escribirse en el archivo que va a la derecha de `>`
* `.gitignore`: el nombre del archivo donde debería escribirse el texto
Y `*` para Git significa "todo". Así que, ignorará todo en el directorio `.venv`.
Ese comando creará un archivo `.gitignore` con el contenido:
```gitignore
*
```
///
## Instala Paquetes { #install-packages }
Después de activar el entorno, puedes instalar paquetes en él.
/// tip | Consejo
Haz esto **una vez** al instalar o actualizar los paquetes que necesita tu proyecto.
Si necesitas actualizar una versión o agregar un nuevo paquete, **harías esto de nuevo**.
///
### Instala Paquetes Directamente { #install-packages-directly }
Si tienes prisa y no quieres usar un archivo para declarar los requisitos de paquetes de tu proyecto, puedes instalarlos directamente.
/// tip | Consejo
Es una (muy) buena idea poner los paquetes y las versiones que necesita tu programa en un archivo (por ejemplo, `requirements.txt` o `pyproject.toml`).
///
//// tab | `pip`
////
### Instala desde `requirements.txt` { #install-from-requirements-txt }
Si tienes un `requirements.txt`, ahora puedes usarlo para instalar sus paquetes.
//// tab | `pip`
////
/// details | `requirements.txt`
Un `requirements.txt` con algunos paquetes podría verse así:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Ejecuta Tu Programa { #run-your-program }
Después de activar el entorno virtual, puedes ejecutar tu programa, y usará el Python dentro de tu entorno virtual con los paquetes que instalaste allí.
```console
$ python main.py
Hello World
```
## Configura Tu Editor { #configure-your-editor }
Probablemente usarías un editor, asegúrate de configurarlo para que use el mismo entorno virtual que creaste (probablemente lo autodetectará) para que puedas obtener autocompletado y errores en línea.
Por ejemplo:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Consejo
Normalmente solo tendrías que hacer esto **una vez**, cuando crees el entorno virtual.
///
## Desactiva el Entorno Virtual { #deactivate-the-virtual-environment }
Una vez que hayas terminado de trabajar en tu proyecto, puedes **desactivar** el entorno virtual.
```console
$ deactivate
```
De esta manera, cuando ejecutes `python` no intentará ejecutarse desde ese entorno virtual con los paquetes instalados allí.
## Listo para Trabajar { #ready-to-work }
Ahora estás listo para empezar a trabajar en tu proyecto.
/// tip | Consejo
¿Quieres entender todo lo anterior?
Continúa leyendo. 👇🤓
///
## Por qué Entornos Virtuales { #why-virtual-environments }
Para trabajar con FastAPI necesitas instalar [Python](https://www.python.org/).
Después de eso, necesitarías **instalar** FastAPI y cualquier otro **paquete** que desees usar.
Para instalar paquetes normalmente usarías el comando `pip` que viene con Python (o alternativas similares).
Sin embargo, si solo usas `pip` directamente, los paquetes se instalarían en tu **entorno global de Python** (la instalación global de Python).
### El Problema { #the-problem }
Entonces, ¿cuál es el problema de instalar paquetes en el entorno global de Python?
En algún momento, probablemente terminarás escribiendo muchos programas diferentes que dependen de **diferentes paquetes**. Y algunos de estos proyectos en los que trabajas dependerán de **diferentes versiones** del mismo paquete. 😱
Por ejemplo, podrías crear un proyecto llamado `philosophers-stone`, este programa depende de otro paquete llamado **`harry`, usando la versión `1`**. Así que, necesitas instalar `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Luego, en algún momento después, creas otro proyecto llamado `prisoner-of-azkaban`, y este proyecto también depende de `harry`, pero este proyecto necesita **`harry` versión `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Pero ahora el problema es, si instalas los paquetes globalmente (en el entorno global) en lugar de en un **entorno virtual local**, tendrás que elegir qué versión de `harry` instalar.
Si deseas ejecutar `philosophers-stone` necesitarás primero instalar `harry` versión `1`, por ejemplo con:
```console
$ pip install "harry==1"
```
Y entonces terminarías con `harry` versión `1` instalada en tu entorno global de Python.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Pero luego si deseas ejecutar `prisoner-of-azkaban`, necesitarás desinstalar `harry` versión `1` e instalar `harry` versión `3` (o simplemente instalar la versión `3` automáticamente desinstalaría la versión `1`).
```console
$ pip install "harry==3"
```
Y entonces terminarías con `harry` versión `3` instalada en tu entorno global de Python.
Y si intentas ejecutar `philosophers-stone` de nuevo, hay una posibilidad de que **no funcione** porque necesita `harry` versión `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Consejo
Es muy común en los paquetes de Python intentar lo mejor para **evitar romper cambios** en **nuevas versiones**, pero es mejor estar seguro e instalar nuevas versiones intencionalmente y cuando puedas ejecutar las pruebas para verificar que todo está funcionando correctamente.
///
Ahora, imagina eso con **muchos** otros **paquetes** de los que dependen todos tus **proyectos**. Eso es muy difícil de manejar. Y probablemente terminarías ejecutando algunos proyectos con algunas **versiones incompatibles** de los paquetes, y sin saber por qué algo no está funcionando.
Además, dependiendo de tu sistema operativo (por ejemplo, Linux, Windows, macOS), podría haber venido con Python ya instalado. Y en ese caso probablemente tenía algunos paquetes preinstalados con algunas versiones específicas **necesitadas por tu sistema**. Si instalas paquetes en el entorno global de Python, podrías terminar **rompiendo** algunos de los programas que vinieron con tu sistema operativo.
## Dónde se Instalan los Paquetes { #where-are-packages-installed }
Cuando instalas Python, crea algunos directorios con algunos archivos en tu computadora.
Algunos de estos directorios son los encargados de tener todos los paquetes que instalas.
Cuando ejecutas:
```console
// No ejecutes esto ahora, solo es un ejemplo 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Eso descargará un archivo comprimido con el código de FastAPI, normalmente desde [PyPI](https://pypi.org/project/fastapi/).
También **descargará** archivos para otros paquetes de los que depende FastAPI.
Luego, **extraerá** todos esos archivos y los pondrá en un directorio en tu computadora.
Por defecto, pondrá esos archivos descargados y extraídos en el directorio que viene con tu instalación de Python, eso es el **entorno global**.
## Qué son los Entornos Virtuales { #what-are-virtual-environments }
La solución a los problemas de tener todos los paquetes en el entorno global es usar un **entorno virtual para cada proyecto** en el que trabajas.
Un entorno virtual es un **directorio**, muy similar al global, donde puedes instalar los paquetes para un proyecto.
De esta manera, cada proyecto tendrá su propio entorno virtual (directorio `.venv`) con sus propios paquetes.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Qué Significa Activar un Entorno Virtual { #what-does-activating-a-virtual-environment-mean }
Cuando activas un entorno virtual, por ejemplo con:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
O si usas Bash para Windows (por ejemplo, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Ese comando creará o modificará algunas [variables de entorno](environment-variables.md) que estarán disponibles para los siguientes comandos.
Una de esas variables es la variable `PATH`.
/// tip | Consejo
Puedes aprender más sobre la variable de entorno `PATH` en la sección [Variables de Entorno](environment-variables.md#path-environment-variable).
///
Activar un entorno virtual agrega su path `.venv/bin` (en Linux y macOS) o `.venv\Scripts` (en Windows) a la variable de entorno `PATH`.
Digamos que antes de activar el entorno, la variable `PATH` se veía así:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Eso significa que el sistema buscaría programas en:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Eso significa que el sistema buscaría programas en:
* `C:\Windows\System32`
////
Después de activar el entorno virtual, la variable `PATH` se vería algo así:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Eso significa que el sistema ahora comenzará a buscar primero los programas en:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
antes de buscar en los otros directorios.
Así que, cuando escribas `python` en el terminal, el sistema encontrará el programa Python en
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
y utilizará ese.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Eso significa que el sistema ahora comenzará a buscar primero los programas en:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
antes de buscar en los otros directorios.
Así que, cuando escribas `python` en el terminal, el sistema encontrará el programa Python en
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
y utilizará ese.
////
Un detalle importante es que pondrá el path del entorno virtual al **comienzo** de la variable `PATH`. El sistema lo encontrará **antes** que cualquier otro Python disponible. De esta manera, cuando ejecutes `python`, utilizará el Python **del entorno virtual** en lugar de cualquier otro `python` (por ejemplo, un `python` de un entorno global).
Activar un entorno virtual también cambia un par de otras cosas, pero esta es una de las cosas más importantes que hace.
## Revisando un Entorno Virtual { #checking-a-virtual-environment }
Cuando revisas si un entorno virtual está activo, por ejemplo con:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Eso significa que el programa `python` que se utilizará es el que está **en el entorno virtual**.
Usas `which` en Linux y macOS y `Get-Command` en Windows PowerShell.
La forma en que funciona ese comando es que irá y revisará la variable de entorno `PATH`, pasando por **cada path en orden**, buscando el programa llamado `python`. Una vez que lo encuentre, te **mostrará el path** a ese programa.
La parte más importante es que cuando llamas a `python`, ese es el exacto "`python`" que será ejecutado.
Así que, puedes confirmar si estás en el entorno virtual correcto.
/// tip | Consejo
Es fácil activar un entorno virtual, obtener un Python, y luego **ir a otro proyecto**.
Y el segundo proyecto **no funcionaría** porque estás usando el **Python incorrecto**, de un entorno virtual para otro proyecto.
Es útil poder revisar qué `python` se está usando. 🤓
///
## Por qué Desactivar un Entorno Virtual { #why-deactivate-a-virtual-environment }
Por ejemplo, podrías estar trabajando en un proyecto `philosophers-stone`, **activar ese entorno virtual**, instalar paquetes y trabajar con ese entorno.
Y luego quieres trabajar en **otro proyecto** `prisoner-of-azkaban`.
Vas a ese proyecto:
```console
$ cd ~/code/prisoner-of-azkaban
```
Si no desactivas el entorno virtual para `philosophers-stone`, cuando ejecutes `python` en el terminal, intentará usar el Python de `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importando sirius, no está instalado 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Pero si desactivas el entorno virtual y activas el nuevo para `prisoner-of-askaban` entonces cuando ejecutes `python` utilizará el Python del entorno virtual en `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// No necesitas estar en el directorio antiguo para desactivar, puedes hacerlo donde sea que estés, incluso después de ir al otro proyecto 😎
$ deactivate
// Activa el entorno virtual en prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Ahora cuando ejecutes python, encontrará el paquete sirius instalado en este entorno virtual ✨
$ python main.py
I solemnly swear 🐺
```
## Alternativas { #alternatives }
Esta es una guía simple para comenzar y enseñarte cómo funciona todo **por debajo**.
Hay muchas **alternativas** para gestionar entornos virtuales, dependencias de paquetes (requisitos), proyectos.
Una vez que estés listo y quieras usar una herramienta para **gestionar todo el proyecto**, dependencias de paquetes, entornos virtuales, etc. Te sugeriría probar [uv](https://github.com/astral-sh/uv).
`uv` puede hacer muchas cosas, puede:
* **Instalar Python** por ti, incluyendo diferentes versiones
* Gestionar el **entorno virtual** para tus proyectos
* Instalar **paquetes**
* Gestionar **dependencias y versiones** de paquetes para tu proyecto
* Asegurarse de que tengas un conjunto **exacto** de paquetes y versiones para instalar, incluidas sus dependencias, para que puedas estar seguro de que puedes ejecutar tu proyecto en producción exactamente igual que en tu computadora mientras desarrollas, esto se llama **locking**
* Y muchas otras cosas
## Conclusión { #conclusion }
Si leíste y comprendiste todo esto, ahora **sabes mucho más** sobre entornos virtuales que muchos desarrolladores por ahí. 🤓
Conocer estos detalles probablemente te será útil en el futuro cuando estés depurando algo que parece complejo, pero sabrás **cómo funciona todo por debajo**. 😎
================================================
FILE: docs/es/llm-prompt.md
================================================
Translate to Spanish (español).
Use the informal grammar (use "tú" instead of "usted").
For instructions or titles in imperative, keep them in imperative, for example "Edit it" to "Edítalo".
---
For the next terms, use the following translations:
* framework: framework (do not translate to "marco")
* performance: rendimiento
* program (verb): programar
* code (verb): programar
* type hints: anotaciones de tipos
* type annotations: anotaciones de tipos
* autocomplete: autocompletado
* completion (in the context of autocompletion): autocompletado
* feature: funcionalidad
* sponsor: sponsor
* host (in a podcast): host
* request (as in HTTP request): request
* response (as in HTTP response): response
* path operation function: path operation function (do not translate to "función de operación de ruta")
* path operation: path operation (do not translate to "operación de ruta")
* path (as in URL path): path (do not translate to "ruta")
* query (as in URL query): query (do not translate to "consulta")
* cookie (as in HTTP cookie): cookie
* header (as in HTTP header): header
* form (as in HTML form): formulario
* type checks: chequeo de tipos
* parse: parse
* parsing: parsing
* marshall: marshall
* library: paquete (do not translate to "biblioteca" or "librería")
* instance: instance (do not translate to "instancia")
* scratch the surface: tocar los conceptos básicos
* string: string
* bug: bug
* docs: documentación (do not translate to "documentos")
* cheat sheet: cheat sheet (do not translate to "chuleta")
* key (as in key-value pair, dictionary key): clave
* array (as in JSON array): array
* API key: API key (do not translate to "clave API")
* 100% test coverage: cobertura de tests del 100%
* back and forth: de un lado a otro
* I/O (as in "input and output"): I/O (do not translate to "E/S")
* Machine Learning: Machine Learning (do not translate to "Aprendizaje Automático")
* Deep Learning: Deep Learning (do not translate to "Aprendizaje Profundo")
* callback hell: callback hell (do not translate to "infierno de callbacks")
* tip: Consejo (do not translate to "tip")
* check: Revisa (do not translate to "chequea" or "comprobación")
* Cross-Origin Resource Sharing: Cross-Origin Resource Sharing (do not translate to "Compartición de Recursos de Origen Cruzado")
* Release Notes: Release Notes (do not translate to "Notas de la Versión")
* Semantic Versioning: Semantic Versioning (do not translate to "Versionado Semántico")
* dependable: dependable (do not translate to "confiable" or "fiable")
* list (as in Python list): list
* context manager: context manager (do not translate to "gestor de contexto" or "administrador de contexto")
* a little bit: un poquito
* graph (data structure, as in "dependency graph"): grafo (do not translate to "gráfico")
* form data: form data (do not translate to "datos de formulario" or "datos de form")
* import (as in code import): import (do not translate to "importación")
* JSON Schema: JSON Schema (do not translate to "Esquema JSON")
* embed: embeber (do not translate to "incrustar")
* request body: request body (do not translate to "cuerpo de la petición")
* response body: response body (do not translate to "cuerpo de la respuesta")
* cross domain: cross domain (do not translate to "dominio cruzado")
* cross origin: cross origin (do not translate to "origen cruzado")
* plugin: plugin (do not translate to "complemento" or "extensión")
* plug-in: plug-in (do not translate to "complemento" or "extensión")
* plug-ins: plug-ins (do not translate to "complementos" or "extensiones")
* full stack: full stack (do not translate to "pila completa")
* full-stack: full-stack (do not translate to "de pila completa")
* stack: stack (do not translate to "pila")
* loop (as in async loop): loop (do not translate to "bucle" or "ciclo")
* hard dependencies: dependencias obligatorias (do not translate to "dependencias duras")
* locking: locking (do not translate to "bloqueo")
* testing (as in software testing): escribir pruebas (do not translate to "probar")
* code base: code base (do not translate to "base de código")
* default: por defecto (do not translate to "predeterminado")
* default values: valores por defecto (do not translate to "valores predeterminados")
* media type: media type (do not translate to "tipo de medio")
* instantiate: crear un instance (do not translate to "instanciar")
* OAuth2 Scopes: Scopes de OAuth2 (do not translate to "Alcances de OAuth2")
* on the fly: sobre la marcha (do not translate to "al vuelo")
* terminal: terminal (feminine, as in "la terminal")
* terminals: terminales (plural feminine, as in "las terminales")
* lifespan: lifespan (do not translate to "vida útil" or "tiempo de vida")
* unload: quitar de memoria (do not translate to "descargar")
* mount (noun): mount (do not translate to "montura")
* mount (verb): montar
* statement (as in code statement): statement (do not translate to "declaración" or "sentencia")
* worker process: worker process (do not translate to "proceso trabajador" or "proceso de trabajo")
* worker processes: worker processes (do not translate to "procesos trabajadores" or "procesos de trabajo")
* worker: worker (do not translate to "trabajador")
* load balancer: load balancer (do not translate to "balanceador de carga")
* load balance: load balance (do not translate to "balancear carga")
* self hosting: self hosting (do not translate to "auto alojamiento")
* timing attack: timing attack (do not translate to "ataque de temporización")
================================================
FILE: docs/es/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/fr/docs/_llm-test.md
================================================
# Fichier de test LLM { #llm-test-file }
Ce document teste si le LLM, qui traduit la documentation, comprend le `general_prompt` dans `scripts/translate.py` et l’invite spécifique à la langue dans `docs/{language code}/llm-prompt.md`. L’invite spécifique à la langue est ajoutée à la fin de `general_prompt`.
Les tests ajoutés ici seront visibles par tous les concepteurs d’invites spécifiques à chaque langue.
Utiliser comme suit :
* Avoir une invite spécifique à la langue - `docs/{language code}/llm-prompt.md`.
* Effectuer une nouvelle traduction de ce document dans votre langue cible souhaitée (voir par exemple la commande `translate-page` de `translate.py`). Cela créera la traduction sous `docs/{language code}/docs/_llm-test.md`.
* Vérifier si tout est correct dans la traduction.
* Si nécessaire, améliorer votre invite spécifique à la langue, l’invite générale, ou le document anglais.
* Corriger ensuite manuellement les problèmes restants dans la traduction, afin que ce soit une bonne traduction.
* Retraduire, en ayant la bonne traduction en place. Le résultat idéal serait que le LLM ne fasse plus aucun changement à la traduction. Cela signifie que l’invite générale et votre invite spécifique à la langue sont aussi bonnes que possible (il fera parfois quelques changements apparemment aléatoires, la raison étant que [les LLM ne sont pas des algorithmes déterministes](https://doublespeak.chat/#/handbook#deterministic-output)).
Les tests :
## Extraits de code { #code-snippets }
//// tab | Test
Ceci est un extrait de code : `foo`. Et ceci est un autre extrait de code : `bar`. Et encore un autre : `baz quux`.
////
//// tab | Info
Le contenu des extraits de code doit être laissé tel quel.
Voir la section `### Content of code snippets` dans l’invite générale dans `scripts/translate.py`.
////
## Guillemets { #quotes }
//// tab | Test
Hier, mon ami a écrit : « Si vous écrivez « incorrectly » correctement, vous l’avez écrit de façon incorrecte ». À quoi j’ai répondu : « Correct, mais ‘incorrectly’ est incorrectement non pas ‘« incorrectly »’ ».
/// note | Remarque
Le LLM traduira probablement ceci de manière erronée. Il est seulement intéressant de voir s’il conserve la traduction corrigée lors d’une retraduction.
///
////
//// tab | Info
Le concepteur de l’invite peut choisir s’il souhaite convertir les guillemets neutres en guillemets typographiques. Il est acceptable de les laisser tels quels.
Voir par exemple la section `### Quotes` dans `docs/de/llm-prompt.md`.
////
## Guillemets dans les extraits de code { #quotes-in-code-snippets }
//// tab | Test
`pip install "foo[bar]"`
Exemples de littéraux de chaîne dans des extraits de code : `"this"`, `'that'`.
Un exemple difficile de littéraux de chaîne dans des extraits de code : `f"I like {'oranges' if orange else "apples"}"`
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Info
... Cependant, les guillemets à l’intérieur des extraits de code doivent rester tels quels.
////
## Blocs de code { #code-blocks }
//// tab | Test
Un exemple de code Bash ...
```bash
# Afficher un message de bienvenue à l'univers
echo "Hello universe"
```
... et un exemple de code console ...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
... et un autre exemple de code console ...
```console
// Créer un répertoire "Code"
$ mkdir code
// Aller dans ce répertoire
$ cd code
```
... et un exemple de code Python ...
```Python
wont_work() # Cela ne fonctionnera pas 😱
works(foo="bar") # Cela fonctionne 🎉
```
... et c’est tout.
////
//// tab | Info
Le code dans les blocs de code ne doit pas être modifié, à l’exception des commentaires.
Voir la section `### Content of code blocks` dans l’invite générale dans `scripts/translate.py`.
////
## Onglets et encadrés colorés { #tabs-and-colored-boxes }
//// tab | Test
/// info | Info
Du texte
///
/// note | Remarque
Du texte
///
/// note | Détails techniques
Du texte
///
/// check | Vérifications
Du texte
///
/// tip | Astuce
Du texte
///
/// warning | Alertes
Du texte
///
/// danger | Danger
Du texte
///
////
//// tab | Info
Les onglets et les blocs « Info »/« Note »/« Warning »/etc. doivent avoir la traduction de leur titre ajoutée après une barre verticale (« | »).
Voir les sections `### Special blocks` et `### Tab blocks` dans l’invite générale dans `scripts/translate.py`.
////
## Liens Web et internes { #web-and-internal-links }
//// tab | Test
Le texte du lien doit être traduit, l’adresse du lien doit rester inchangée :
* [Lien vers le titre ci-dessus](#code-snippets)
* [Lien interne](index.md#installation)
* [Lien externe](https://sqlmodel.tiangolo.com/)
* [Lien vers une feuille de style](https://fastapi.tiangolo.com/css/styles.css)
* [Lien vers un script](https://fastapi.tiangolo.com/js/logic.js)
* [Lien vers une image](https://fastapi.tiangolo.com/img/foo.jpg)
Le texte du lien doit être traduit, l’adresse du lien doit pointer vers la traduction :
* [Lien FastAPI](https://fastapi.tiangolo.com/fr/)
////
//// tab | Info
Les liens doivent être traduits, mais leur adresse doit rester inchangée. Exception faite des liens absolus vers des pages de la documentation FastAPI. Dans ce cas, il faut pointer vers la traduction.
Voir la section `### Links` dans l’invite générale dans `scripts/translate.py`.
////
## Éléments HTML « abbr » { #html-abbr-elements }
//// tab | Test
Voici quelques éléments entourés d’un élément HTML « abbr » (certains sont inventés) :
### L’abbr fournit une expression complète { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### L’abbr donne une expression complète et une explication { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Info
Les attributs « title » des éléments « abbr » sont traduits en suivant des consignes spécifiques.
Les traductions peuvent ajouter leurs propres éléments « abbr » que le LLM ne doit pas supprimer. Par exemple pour expliquer des mots anglais.
Voir la section `### HTML abbr elements` dans l’invite générale dans `scripts/translate.py`.
////
## Éléments HTML « dfn » { #html-dfn-elements }
* grappe
* Apprentissage profond
## Titres { #headings }
//// tab | Test
### Créer une application Web - un tutoriel { #develop-a-webapp-a-tutorial }
Bonjour.
### Annotations de type et annotations de type { #type-hints-and-annotations }
Rebonjour.
### Superclasses et sous-classes { #super-and-subclasses }
Rebonjour.
////
//// tab | Info
La seule règle stricte pour les titres est que le LLM laisse la partie hachage entre accolades inchangée, ce qui garantit que les liens ne se rompent pas.
Voir la section `### Headings` dans l’invite générale dans `scripts/translate.py`.
Pour certaines consignes spécifiques à la langue, voir par exemple la section `### Headings` dans `docs/de/llm-prompt.md`.
////
## Termes utilisés dans les documents { #terms-used-in-the-docs }
//// tab | Test
* vous
* votre
* p. ex.
* etc.
* `foo` en tant que `int`
* `bar` en tant que `str`
* `baz` en tant que `list`
* le Tutoriel - Guide utilisateur
* le Guide utilisateur avancé
* la documentation SQLModel
* la documentation de l’API
* la documentation automatique
* Data Science
* Apprentissage profond
* Apprentissage automatique
* Injection de dépendances
* authentification HTTP Basic
* HTTP Digest
* format ISO
* la norme JSON Schema
* le schéma JSON
* la définition de schéma
* Flux Password
* Mobile
* déprécié
* conçu
* invalide
* à la volée
* standard
* par défaut
* sensible à la casse
* insensible à la casse
* servir l’application
* servir la page
* l’app
* l’application
* la requête
* la réponse
* la réponse d’erreur
* le chemin d’accès
* le décorateur de chemin d’accès
* la fonction de chemin d’accès
* le corps
* le corps de la requête
* le corps de la réponse
* le corps JSON
* le corps de formulaire
* le corps de fichier
* le corps de la fonction
* le paramètre
* le paramètre de corps
* le paramètre de chemin
* le paramètre de requête
* le paramètre de cookie
* le paramètre d’en-tête
* le paramètre de formulaire
* le paramètre de fonction
* l’événement
* l’événement de démarrage
* le démarrage du serveur
* l’événement d’arrêt
* l’événement de cycle de vie
* le gestionnaire
* le gestionnaire d’événements
* le gestionnaire d’exceptions
* gérer
* le modèle
* le modèle Pydantic
* le modèle de données
* le modèle de base de données
* le modèle de formulaire
* l’objet modèle
* la classe
* la classe de base
* la classe parente
* la sous-classe
* la classe enfant
* la classe sœur
* la méthode de classe
* l’en-tête
* les en-têtes
* l’en-tête d’autorisation
* l’en-tête `Authorization`
* l’en-tête transféré
* le système d’injection de dépendances
* la dépendance
* l’élément dépendable
* le dépendant
* lié aux E/S
* lié au processeur
* concurrence
* parallélisme
* multi-traitement
* la variable d’env
* la variable d’environnement
* le `PATH`
* la variable `PATH`
* l’authentification
* le fournisseur d’authentification
* l’autorisation
* le formulaire d’autorisation
* le fournisseur d’autorisation
* l’utilisateur s’authentifie
* le système authentifie l’utilisateur
* la CLI
* l’interface en ligne de commande
* le serveur
* le client
* le fournisseur cloud
* le service cloud
* le développement
* les étapes de développement
* le dict
* le dictionnaire
* l’énumération
* l’enum
* le membre d’enum
* l’encodeur
* le décodeur
* encoder
* décoder
* l’exception
* lever
* l’expression
* l’instruction
* le frontend
* le backend
* la discussion GitHub
* le ticket GitHub
* la performance
* l’optimisation des performances
* le type de retour
* la valeur de retour
* la sécurité
* le schéma de sécurité
* la tâche
* la tâche d’arrière-plan
* la fonction de tâche
* le template
* le moteur de templates
* l’annotation de type
* l’annotation de type
* le worker du serveur
* le worker Uvicorn
* le Worker Gunicorn
* le processus worker
* la classe de worker
* la charge de travail
* le déploiement
* déployer
* le SDK
* le kit de développement logiciel
* le `APIRouter`
* le `requirements.txt`
* le jeton Bearer
* le changement majeur incompatible
* le bogue
* le bouton
* l’appelable
* le code
* le commit
* le gestionnaire de contexte
* la coroutine
* la session de base de données
* le disque
* le domaine
* le moteur
* le faux X
* la méthode HTTP GET
* l’élément
* la bibliothèque
* le cycle de vie
* le verrou
* le middleware
* l’application mobile
* le module
* le montage
* le réseau
* l’origine
* la surcharge
* le payload
* le processeur
* la propriété
* le proxy
* la pull request
* la requête
* la RAM
* la machine distante
* le code d’état
* la chaîne
* l’étiquette
* le framework Web
* le joker
* retourner
* valider
////
//// tab | Info
Il s’agit d’une liste non exhaustive et non normative de termes (principalement) techniques présents dans les documents. Elle peut aider le concepteur de l’invite à déterminer pour quels termes le LLM a besoin d’un coup de main. Par exemple, lorsqu’il continue de remplacer une bonne traduction par une traduction sous-optimale. Ou lorsqu’il a des difficultés à conjuguer/décliner un terme dans votre langue.
Voir par exemple la section `### List of English terms and their preferred German translations` dans `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/fr/docs/about/index.md
================================================
# À propos { #about }
À propos de FastAPI, de sa conception, de ses sources d'inspiration et plus encore. 🤓
================================================
FILE: docs/fr/docs/advanced/additional-responses.md
================================================
# Réponses supplémentaires dans OpenAPI { #additional-responses-in-openapi }
/// warning | Alertes
Ceci concerne un sujet plutôt avancé.
Si vous débutez avec **FastAPI**, vous n'en aurez peut-être pas besoin.
///
Vous pouvez déclarer des réponses supplémentaires, avec des codes d'état supplémentaires, des types de médias, des descriptions, etc.
Ces réponses supplémentaires seront incluses dans le schéma OpenAPI, elles apparaîtront donc également dans la documentation de l'API.
Mais pour ces réponses supplémentaires, vous devez vous assurer de renvoyer directement une `Response` comme `JSONResponse`, avec votre code HTTP et votre contenu.
## Réponse supplémentaire avec `model` { #additional-response-with-model }
Vous pouvez passer à vos décorateurs de *chemin d'accès* un paramètre `responses`.
Il prend comme valeur un `dict` dont les clés sont des codes HTTP pour chaque réponse, comme `200`, et la valeur de ces clés sont d'autres `dict` avec des informations pour chacun d'eux.
Chacun de ces `dict` de réponse peut avoir une clé `model`, contenant un modèle Pydantic, tout comme `response_model`.
**FastAPI** prendra ce modèle, générera son schéma JSON et l'inclura au bon endroit dans OpenAPI.
Par exemple, pour déclarer une autre réponse avec un code HTTP `404` et un modèle Pydantic `Message`, vous pouvez écrire :
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Remarque
Gardez à l'esprit que vous devez renvoyer directement `JSONResponse`.
///
/// info
La clé `model` ne fait pas partie d'OpenAPI.
**FastAPI** prendra le modèle Pydantic à partir de là, générera le `JSON Schema` et le placera au bon endroit.
Le bon endroit est :
* Dans la clé `content`, qui a pour valeur un autre objet JSON (`dict`) qui contient :
* Une clé avec le type de support, par ex. `application/json`, qui contient comme valeur un autre objet JSON, qui contient :
* Une clé `schema`, qui a pour valeur le schéma JSON du modèle, voici le bon endroit.
* **FastAPI** ajoute ici une référence aux schémas JSON globaux à un autre endroit de votre OpenAPI au lieu de l'inclure directement. De cette façon, d'autres applications et clients peuvent utiliser ces schémas JSON directement, fournir de meilleurs outils de génération de code, etc.
///
Les réponses générées au format OpenAPI pour ce *chemin d'accès* seront :
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Les schémas sont référencés à un autre endroit du modèle OpenAPI :
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Types de médias supplémentaires pour la réponse principale { #additional-media-types-for-the-main-response }
Vous pouvez utiliser ce même paramètre `responses` pour ajouter différents types de médias pour la même réponse principale.
Par exemple, vous pouvez ajouter un type de média supplémentaire `image/png`, en déclarant que votre *chemin d'accès* peut renvoyer un objet JSON (avec le type de média `application/json`) ou une image PNG :
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Remarque
Notez que vous devez retourner l'image en utilisant directement un `FileResponse`.
///
/// info
À moins que vous ne spécifiiez explicitement un type de média différent dans votre paramètre `responses`, FastAPI supposera que la réponse a le même type de média que la classe de réponse principale (par défaut `application/json`).
Mais si vous avez spécifié une classe de réponse personnalisée avec `None` comme type de média, FastAPI utilisera `application/json` pour toute réponse supplémentaire associée à un modèle.
///
## Combiner les informations { #combining-information }
Vous pouvez également combiner des informations de réponse provenant de plusieurs endroits, y compris les paramètres `response_model`, `status_code` et `responses`.
Vous pouvez déclarer un `response_model`, en utilisant le code HTTP par défaut `200` (ou un code personnalisé si vous en avez besoin), puis déclarer des informations supplémentaires pour cette même réponse dans `responses`, directement dans le schéma OpenAPI.
**FastAPI** conservera les informations supplémentaires des `responses` et les combinera avec le schéma JSON de votre modèle.
Par exemple, vous pouvez déclarer une réponse avec un code HTTP `404` qui utilise un modèle Pydantic et a une `description` personnalisée.
Et une réponse avec un code HTTP `200` qui utilise votre `response_model`, mais inclut un `example` personnalisé :
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Tout sera combiné et inclus dans votre OpenAPI, et affiché dans la documentation de l'API :
## Combinez les réponses prédéfinies et les réponses personnalisées { #combine-predefined-responses-and-custom-ones }
Vous voulez peut-être avoir des réponses prédéfinies qui s'appliquent à de nombreux *chemins d'accès*, mais vous souhaitez les combiner avec des réponses personnalisées nécessaires à chaque *chemin d'accès*.
Dans ces cas, vous pouvez utiliser la technique Python « unpacking » d'un `dict` avec `**dict_to_unpack` :
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Ici, `new_dict` contiendra toutes les paires clé-valeur de `old_dict` plus la nouvelle paire clé-valeur :
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Vous pouvez utiliser cette technique pour réutiliser certaines réponses prédéfinies dans vos *chemins d'accès* et les combiner avec des réponses personnalisées supplémentaires.
Par exemple:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Plus d'informations sur les réponses OpenAPI { #more-information-about-openapi-responses }
Pour voir exactement ce que vous pouvez inclure dans les réponses, vous pouvez consulter ces sections dans la spécification OpenAPI :
* [Objet Responses de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), il inclut le `Response Object`.
* [Objet Response de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), vous pouvez inclure n'importe quoi directement dans chaque réponse à l'intérieur de votre paramètre `responses`. Y compris `description`, `headers`, `content` (à l'intérieur de cela, vous déclarez différents types de médias et schémas JSON) et `links`.
================================================
FILE: docs/fr/docs/advanced/additional-status-codes.md
================================================
# Codes HTTP supplémentaires { #additional-status-codes }
Par défaut, **FastAPI** renverra les réponses à l'aide d'une structure de données `JSONResponse`, en plaçant la réponse de votre *chemin d'accès* à l'intérieur de cette `JSONResponse`.
Il utilisera le code HTTP par défaut ou celui que vous avez défini dans votre *chemin d'accès*.
## Codes HTTP supplémentaires { #additional-status-codes_1 }
Si vous souhaitez renvoyer des codes HTTP supplémentaires en plus du code principal, vous pouvez le faire en renvoyant directement une `Response`, comme une `JSONResponse`, et en définissant directement le code HTTP supplémentaire.
Par exemple, disons que vous voulez avoir un *chemin d'accès* qui permet de mettre à jour les éléments et renvoie les codes HTTP 200 « OK » en cas de succès.
Mais vous voulez aussi qu'il accepte de nouveaux éléments. Et lorsque les éléments n'existaient pas auparavant, il les crée et renvoie un code HTTP de 201 « Créé ».
Pour y parvenir, importez `JSONResponse` et renvoyez-y directement votre contenu, en définissant le `status_code` que vous souhaitez :
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Alertes
Lorsque vous renvoyez une `Response` directement, comme dans l'exemple ci-dessus, elle sera renvoyée directement.
Elle ne sera pas sérialisée avec un modèle, etc.
Vous devez vous assurer qu'elle contient les données souhaitées et que les valeurs sont dans un format JSON valide (si vous utilisez une `JSONResponse`).
///
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.responses import JSONResponse`.
Par commodité pour vous, le développeur, **FastAPI** fournit les mêmes `starlette.responses` sous la forme de `fastapi.responses`. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en est de même avec `status`.
///
## Documents OpenAPI et API { #openapi-and-api-docs }
Si vous renvoyez directement des codes HTTP et des réponses supplémentaires, ils ne seront pas inclus dans le schéma OpenAPI (les documents de l'API), car FastAPI n'a aucun moyen de savoir à l'avance ce que vous allez renvoyer.
Mais vous pouvez documenter cela dans votre code, en utilisant : [Réponses supplémentaires](additional-responses.md).
================================================
FILE: docs/fr/docs/advanced/advanced-dependencies.md
================================================
# Dépendances avancées { #advanced-dependencies }
## Dépendances paramétrées { #parameterized-dependencies }
Toutes les dépendances que nous avons vues étaient des fonctions ou des classes fixes.
Mais il peut y avoir des cas où vous souhaitez pouvoir définir des paramètres sur la dépendance, sans devoir déclarer de nombreuses fonctions ou classes différentes.
Imaginons que nous voulions avoir une dépendance qui vérifie si le paramètre de requête `q` contient un contenu fixe.
Mais nous voulons pouvoir paramétrer ce contenu fixe.
## Une instance « callable » { #a-callable-instance }
En Python, il existe un moyen de rendre une instance de classe « callable ».
Pas la classe elle‑même (qui est déjà un callable), mais une instance de cette classe.
Pour cela, nous déclarons une méthode `__call__` :
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
Dans ce cas, ce `__call__` est ce que **FastAPI** utilisera pour détecter des paramètres supplémentaires et des sous‑dépendances, et c’est ce qui sera appelé pour transmettre ensuite une valeur au paramètre dans votre *fonction de chemin d'accès*.
## Paramétrer l'instance { #parameterize-the-instance }
Et maintenant, nous pouvons utiliser `__init__` pour déclarer les paramètres de l’instance, que nous utiliserons pour « paramétrer » la dépendance :
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
Dans ce cas, **FastAPI** n’accèdera pas à `__init__` et ne s’en souciera pas ; nous l’utiliserons directement dans notre code.
## Créer une instance { #create-an-instance }
Nous pouvons créer une instance de cette classe avec :
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Et de cette façon, nous pouvons « paramétrer » notre dépendance, qui contient maintenant « bar », en tant qu’attribut `checker.fixed_content`.
## Utiliser l'instance comme dépendance { #use-the-instance-as-a-dependency }
Ensuite, nous pourrions utiliser ce `checker` dans un `Depends(checker)`, au lieu de `Depends(FixedContentQueryChecker)`, car la dépendance est l’instance, `checker`, et non la classe elle‑même.
Et lors de la résolution de la dépendance, **FastAPI** appellera ce `checker` comme ceci :
```Python
checker(q="somequery")
```
... et passera ce que cela renvoie comme valeur de la dépendance à notre *fonction de chemin d'accès*, en tant que paramètre `fixed_content_included` :
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Astuce
Tout cela peut sembler artificiel. Et il n’est peut‑être pas encore très clair en quoi c’est utile.
Ces exemples sont volontairement simples, mais ils montrent comment tout cela fonctionne.
Dans les chapitres sur la sécurité, il existe des fonctions utilitaires implémentées de la même manière.
Si vous avez compris tout cela, vous savez déjà comment ces outils utilitaires pour la sécurité fonctionnent en interne.
///
## Dépendances avec `yield`, `HTTPException`, `except` et tâches d'arrière‑plan { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Alertes
Vous n’avez très probablement pas besoin de ces détails techniques.
Ces détails sont utiles principalement si vous aviez une application FastAPI antérieure à la version 0.121.0 et que vous rencontrez des problèmes avec des dépendances utilisant `yield`.
///
Les dépendances avec `yield` ont évolué au fil du temps pour couvrir différents cas d’utilisation et corriger certains problèmes ; voici un résumé de ce qui a changé.
### Dépendances avec `yield` et `scope` { #dependencies-with-yield-and-scope }
Dans la version 0.121.0, **FastAPI** a ajouté la prise en charge de `Depends(scope="function")` pour les dépendances avec `yield`.
Avec `Depends(scope="function")`, le code d’arrêt après `yield` s’exécute immédiatement après la fin de la *fonction de chemin d'accès*, avant que la réponse ne soit renvoyée au client.
Et lorsque vous utilisez `Depends(scope="request")` (valeur par défaut), le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
Vous pouvez en lire davantage dans les documents pour [Dépendances avec `yield` - Sortie anticipée et `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Dépendances avec `yield` et `StreamingResponse`, Détails techniques { #dependencies-with-yield-and-streamingresponse-technical-details }
Avant FastAPI 0.118.0, si vous utilisiez une dépendance avec `yield`, elle exécutait le code d’arrêt après que la *fonction de chemin d'accès* a retourné, mais juste avant d’envoyer la réponse.
L’objectif était d’éviter de conserver des ressources plus longtemps que nécessaire pendant que la réponse transitait sur le réseau.
Ce changement impliquait aussi que si vous retourniez une `StreamingResponse`, le code d’arrêt de la dépendance avec `yield` aurait déjà été exécuté.
Par exemple, si vous aviez une session de base de données dans une dépendance avec `yield`, la `StreamingResponse` ne pourrait pas utiliser cette session pendant le streaming des données, car la session aurait déjà été fermée dans le code d’arrêt après `yield`.
Ce comportement a été annulé en 0.118.0, afin que le code d’arrêt après `yield` s’exécute après l’envoi de la réponse.
/// info
Comme vous le verrez ci‑dessous, c’est très similaire au comportement avant la version 0.106.0, mais avec plusieurs améliorations et corrections de bogues pour des cas limites.
///
#### Cas d’utilisation avec sortie anticipée du code { #use-cases-with-early-exit-code }
Il existe certains cas d’utilisation avec des conditions spécifiques qui pourraient bénéficier de l’ancien comportement, où le code d’arrêt des dépendances avec `yield` s’exécute avant l’envoi de la réponse.
Par exemple, imaginez que vous ayez du code qui utilise une session de base de données dans une dépendance avec `yield` uniquement pour vérifier un utilisateur, mais que la session de base de données ne soit plus jamais utilisée dans la *fonction de chemin d'accès*, seulement dans la dépendance, et que la réponse mette longtemps à être envoyée, comme une `StreamingResponse` qui envoie les données lentement mais qui, pour une raison quelconque, n’utilise pas la base de données.
Dans ce cas, la session de base de données serait conservée jusqu’à la fin de l’envoi de la réponse, mais si vous ne l’utilisez pas, il ne serait pas nécessaire de la conserver.
Voici à quoi cela pourrait ressembler :
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Le code d’arrêt, la fermeture automatique de la `Session` dans :
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
... serait exécuté après que la réponse a fini d’envoyer les données lentes :
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Mais comme `generate_stream()` n’utilise pas la session de base de données, il n’est pas vraiment nécessaire de garder la session ouverte pendant l’envoi de la réponse.
Si vous avez ce cas d’utilisation spécifique avec SQLModel (ou SQLAlchemy), vous pouvez fermer explicitement la session dès que vous n’en avez plus besoin :
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
De cette manière, la session libérera la connexion à la base de données, afin que d’autres requêtes puissent l’utiliser.
Si vous avez un autre cas d’utilisation qui nécessite une sortie anticipée depuis une dépendance avec `yield`, veuillez créer une [Question de discussion GitHub](https://github.com/fastapi/fastapi/discussions/new?category=questions) avec votre cas spécifique et pourquoi vous bénéficieriez d’une fermeture anticipée pour les dépendances avec `yield`.
S’il existe des cas d’utilisation convaincants pour une fermeture anticipée dans les dépendances avec `yield`, j’envisagerai d’ajouter une nouvelle façon d’y opter.
### Dépendances avec `yield` et `except`, Détails techniques { #dependencies-with-yield-and-except-technical-details }
Avant FastAPI 0.110.0, si vous utilisiez une dépendance avec `yield`, puis capturiez une exception avec `except` dans cette dépendance, et que vous ne relanciez pas l’exception, l’exception était automatiquement levée/transmise à tout gestionnaire d’exceptions ou au gestionnaire d’erreur interne du serveur.
Cela a été modifié dans la version 0.110.0 pour corriger une consommation de mémoire non gérée due aux exceptions transmises sans gestionnaire (erreurs internes du serveur), et pour rendre le comportement cohérent avec celui du code Python classique.
### Tâches d'arrière‑plan et dépendances avec `yield`, Détails techniques { #background-tasks-and-dependencies-with-yield-technical-details }
Avant FastAPI 0.106.0, lever des exceptions après `yield` n’était pas possible, le code d’arrêt dans les dépendances avec `yield` s’exécutait après l’envoi de la réponse, donc les [Gestionnaires d'exceptions](../tutorial/handling-errors.md#install-custom-exception-handlers) avaient déjà été exécutés.
Cela avait été conçu ainsi principalement pour permettre d’utiliser les mêmes objets « générés par yield » par les dépendances à l’intérieur de tâches d’arrière‑plan, car le code d’arrêt s’exécutait après la fin des tâches d’arrière‑plan.
Cela a été modifié dans FastAPI 0.106.0 afin de ne pas conserver des ressources pendant l’attente de la transmission de la réponse sur le réseau.
/// tip | Astuce
De plus, une tâche d’arrière‑plan est normalement un ensemble de logique indépendant qui devrait être géré séparément, avec ses propres ressources (par ex. sa propre connexion à la base de données).
Ainsi, vous aurez probablement un code plus propre.
///
Si vous comptiez sur ce comportement, vous devez désormais créer les ressources pour les tâches d’arrière‑plan à l’intérieur de la tâche elle‑même, et n’utiliser en interne que des données qui ne dépendent pas des ressources des dépendances avec `yield`.
Par exemple, au lieu d’utiliser la même session de base de données, vous créeriez une nouvelle session de base de données à l’intérieur de la tâche d’arrière‑plan, et vous obtiendriez les objets depuis la base de données en utilisant cette nouvelle session. Puis, au lieu de passer l’objet obtenu depuis la base de données en paramètre à la fonction de tâche d’arrière‑plan, vous passeriez l’identifiant (ID) de cet objet et vous obtiendriez à nouveau l’objet à l’intérieur de la fonction de la tâche d’arrière‑plan.
================================================
FILE: docs/fr/docs/advanced/advanced-python-types.md
================================================
# Types Python avancés { #advanced-python-types }
Voici quelques idées supplémentaires qui peuvent être utiles lorsque vous travaillez avec les types Python.
## Utiliser `Union` ou `Optional` { #using-union-or-optional }
Si votre code ne peut pas utiliser `|` pour une raison quelconque, par exemple si ce n'est pas dans une annotation de type mais dans quelque chose comme `response_model=`, au lieu d'utiliser la barre verticale (`|`) vous pouvez utiliser `Union` de `typing`.
Par exemple, vous pourriez déclarer que quelque chose peut être un `str` ou `None` :
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` propose également un raccourci pour déclarer que quelque chose peut être `None`, avec `Optional`.
Voici un conseil issu de mon point de vue très subjectif :
- 🚨 Évitez d'utiliser `Optional[SomeType]`
- À la place ✨ **utilisez `Union[SomeType, None]`** ✨.
Les deux sont équivalents et, en interne, identiques, mais je recommande `Union` plutôt que `Optional` parce que le mot « optional » semble impliquer que la valeur est facultative, alors qu'il signifie en réalité « elle peut être `None` », même si elle n'est pas facultative et reste requise.
Je pense que `Union[SomeType, None]` est plus explicite quant à sa signification.
Il ne s'agit que des mots et des noms. Mais ces mots peuvent influencer la manière dont vous et vos coéquipiers pensez au code.
À titre d'exemple, prenons cette fonction :
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
Le paramètre `name` est défini comme `Optional[str]`, mais il n'est pas facultatif, vous ne pouvez pas appeler la fonction sans le paramètre :
```Python
say_hi() # Oh non, cela lève une erreur ! 😱
```
Le paramètre `name` est toujours requis (pas facultatif) car il n'a pas de valeur par défaut. En revanche, `name` accepte `None` comme valeur :
```Python
say_hi(name=None) # Ceci fonctionne, None est valide 🎉
```
La bonne nouvelle, c'est que, dans la plupart des cas, vous pourrez simplement utiliser `|` pour définir des unions de types :
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Ainsi, normalement, vous n'avez pas à vous préoccuper de noms comme `Optional` et `Union`. 😎
================================================
FILE: docs/fr/docs/advanced/async-tests.md
================================================
# Tests asynchrones { #async-tests }
Vous avez déjà vu comment tester vos applications **FastAPI** en utilisant le `TestClient` fourni. Jusqu'à présent, vous n'avez vu que comment écrire des tests synchrones, sans utiliser de fonctions `async`.
Pouvoir utiliser des fonctions asynchrones dans vos tests peut être utile, par exemple lorsque vous interrogez votre base de données de manière asynchrone. Imaginez que vous vouliez tester l'envoi de requêtes à votre application FastAPI puis vérifier que votre backend a bien écrit les bonnes données dans la base, tout en utilisant une bibliothèque de base de données asynchrone.
Voyons comment procéder.
## pytest.mark.anyio { #pytest-mark-anyio }
Si nous voulons appeler des fonctions asynchrones dans nos tests, nos fonctions de test doivent être asynchrones. AnyIO fournit un plug-in pratique qui nous permet d'indiquer que certaines fonctions de test doivent être appelées de manière asynchrone.
## HTTPX { #httpx }
Même si votre application **FastAPI** utilise des fonctions `def` normales au lieu de `async def`, c'est toujours une application `async` en interne.
Le `TestClient` fait un peu de magie pour appeler l'application FastAPI asynchrone depuis vos fonctions de test `def` normales, en utilisant pytest standard. Mais cette magie ne fonctionne plus lorsque nous l'utilisons dans des fonctions asynchrones. En exécutant nos tests de manière asynchrone, nous ne pouvons plus utiliser le `TestClient` dans nos fonctions de test.
Le `TestClient` est basé sur [HTTPX](https://www.python-httpx.org) et, heureusement, nous pouvons l'utiliser directement pour tester l'API.
## Exemple { #example }
Pour un exemple simple, considérons une structure de fichiers similaire à celle décrite dans [Applications plus grandes](../tutorial/bigger-applications.md) et [Tests](../tutorial/testing.md) :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Le fichier `main.py` contiendrait :
{* ../../docs_src/async_tests/app_a_py310/main.py *}
Le fichier `test_main.py` contiendrait les tests pour `main.py`, il pourrait maintenant ressembler à ceci :
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Exécuter { #run-it }
Vous pouvez lancer vos tests comme d'habitude via :
```console
$ pytest
---> 100%
```
## En détail { #in-detail }
Le marqueur `@pytest.mark.anyio` indique à pytest que cette fonction de test doit être appelée de manière asynchrone :
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Astuce
Notez que la fonction de test est maintenant `async def` au lieu de simplement `def` comme auparavant avec le `TestClient`.
///
Nous pouvons ensuite créer un `AsyncClient` avec l'application et lui envoyer des requêtes asynchrones en utilisant `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
C'est l'équivalent de :
```Python
response = client.get('/')
```
... que nous utilisions pour faire nos requêtes avec le `TestClient`.
/// tip | Astuce
Notez que nous utilisons async/await avec le nouveau `AsyncClient` — la requête est asynchrone.
///
/// warning | Alertes
Si votre application s'appuie sur des événements de cycle de vie, l'`AsyncClient` ne déclenchera pas ces événements. Pour vous assurer qu'ils sont déclenchés, utilisez `LifespanManager` depuis [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Autres appels de fonctions asynchrones { #other-asynchronous-function-calls }
Comme la fonction de test est désormais asynchrone, vous pouvez également appeler (et `await`) d'autres fonctions `async` en plus d'envoyer des requêtes à votre application FastAPI dans vos tests, exactement comme vous le feriez ailleurs dans votre code.
/// tip | Astuce
Si vous rencontrez une erreur `RuntimeError: Task attached to a different loop` lors de l'intégration d'appels de fonctions asynchrones dans vos tests (par exemple en utilisant [MotorClient de MongoDB](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), n'oubliez pas d'instancier les objets qui ont besoin d'une boucle d'événements uniquement dans des fonctions async, par exemple dans un callback `@app.on_event("startup")`.
///
================================================
FILE: docs/fr/docs/advanced/behind-a-proxy.md
================================================
# Être derrière un proxy { #behind-a-proxy }
Dans de nombreuses situations, vous utiliserez un **proxy** comme Traefik ou Nginx devant votre application FastAPI.
Ces proxies peuvent gérer les certificats HTTPS et d'autres aspects.
## En-têtes transférés par le proxy { #proxy-forwarded-headers }
Un **proxy** placé devant votre application définit normalement certains en-têtes à la volée avant d'envoyer les requêtes à votre **serveur**, afin d'indiquer au serveur que la requête a été **transférée** par le proxy, en lui donnant l'URL d'origine (publique), y compris le domaine, le fait qu'elle utilise HTTPS, etc.
Le programme **serveur** (par exemple **Uvicorn** via **FastAPI CLI**) est capable d'interpréter ces en‑têtes, puis de transmettre ces informations à votre application.
Mais, par sécurité, comme le serveur ne sait pas qu'il se trouve derrière un proxy de confiance, il n'interprétera pas ces en‑têtes.
/// note | Détails techniques
Les en-têtes du proxy sont :
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Activer les en-têtes transférés par le proxy { #enable-proxy-forwarded-headers }
Vous pouvez démarrer FastAPI CLI avec l'option de CLI `--forwarded-allow-ips` et fournir les adresses IP à considérer comme fiables pour lire ces en‑têtes transférés.
Si vous la définissez à `--forwarded-allow-ips="*"`, elle fera confiance à toutes les IP entrantes.
Si votre **serveur** est derrière un **proxy** de confiance et que seul le proxy lui parle, cela fera accepter l'IP de ce **proxy**, quelle qu'elle soit.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Redirections avec HTTPS { #redirects-with-https }
Par exemple, disons que vous définissez un *chemin d'accès* `/items/` :
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Si le client essaie d'aller à `/items`, par défaut, il sera redirigé vers `/items/`.
Mais avant de définir l'option de CLI `--forwarded-allow-ips`, il pourrait rediriger vers `http://localhost:8000/items/`.
Mais peut‑être que votre application est hébergée à `https://mysuperapp.com`, et la redirection devrait être vers `https://mysuperapp.com/items/`.
En définissant `--proxy-headers`, FastAPI pourra désormais rediriger vers l'emplacement correct. 😎
```
https://mysuperapp.com/items/
```
/// tip | Astuce
Si vous voulez en savoir plus sur HTTPS, consultez le guide [À propos de HTTPS](../deployment/https.md).
///
### Comment fonctionnent les en‑têtes transférés par le proxy { #how-proxy-forwarded-headers-work }
Voici une représentation visuelle de la façon dont le **proxy** ajoute des en‑têtes transférés entre le client et le **serveur d'application** :
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
Le **proxy** intercepte la requête client d'origine et ajoute les en-têtes spéciaux *forwarded* (`X-Forwarded-*`) avant de transmettre la requête au **serveur d'application**.
Ces en‑têtes conservent des informations sur la requête d'origine qui seraient autrement perdues :
* **X-Forwarded-For** : l'adresse IP du client d'origine
* **X-Forwarded-Proto** : le protocole d'origine (`https`)
* **X-Forwarded-Host** : l'hôte d'origine (`mysuperapp.com`)
Lorsque **FastAPI CLI** est configurée avec `--forwarded-allow-ips`, elle fait confiance à ces en‑têtes et les utilise, par exemple pour générer les bonnes URL dans les redirections.
## Proxy avec un préfixe de chemin supprimé { #proxy-with-a-stripped-path-prefix }
Vous pouvez avoir un proxy qui ajoute un préfixe de chemin à votre application.
Dans ces cas, vous pouvez utiliser `root_path` pour configurer votre application.
Le `root_path` est un mécanisme fourni par la spécification ASGI (sur laquelle FastAPI est construit, via Starlette).
Le `root_path` est utilisé pour gérer ces cas spécifiques.
Et il est également utilisé en interne lors du montage de sous‑applications.
Avoir un proxy avec un préfixe de chemin supprimé, dans ce cas, signifie que vous pourriez déclarer un chemin à `/app` dans votre code, mais ensuite, vous ajoutez une couche au‑dessus (le proxy) qui place votre application **FastAPI** sous un chemin comme `/api/v1`.
Dans ce cas, le chemin original `/app` serait en réalité servi à `/api/v1/app`.
Même si tout votre code est écrit en supposant qu'il n'y a que `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Et le proxy **« stripping »** le **préfixe de chemin** à la volée avant de transmettre la requête au serveur de l'application (probablement Uvicorn via FastAPI CLI), en gardant votre application convaincue qu'elle est servie à `/app`, afin que vous n'ayez pas à mettre à jour tout votre code pour inclure le préfixe `/api/v1`.
Jusqu'ici, tout fonctionnerait normalement.
Mais ensuite, lorsque vous ouvrez l'interface de documentation intégrée (le frontend), elle s'attendra à obtenir le schéma OpenAPI à `/openapi.json`, au lieu de `/api/v1/openapi.json`.
Ainsi, le frontend (qui s'exécute dans le navigateur) essaiera d'atteindre `/openapi.json` et ne pourra pas obtenir le schéma OpenAPI.
Parce que nous avons un proxy avec un préfixe de chemin `/api/v1` pour notre application, le frontend doit récupérer le schéma OpenAPI à `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Astuce
L'IP `0.0.0.0` est couramment utilisée pour signifier que le programme écoute sur toutes les IP disponibles de cette machine/serveur.
///
L'interface de documents doit également indiquer dans le schéma OpenAPI que ce `server` d'API se trouve à `/api/v1` (derrière le proxy). Par exemple :
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Plus d'éléments ici
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Plus d'éléments ici
}
}
```
Dans cet exemple, le « Proxy » pourrait être quelque chose comme **Traefik**. Et le serveur serait quelque chose comme FastAPI CLI avec **Uvicorn**, exécutant votre application FastAPI.
### Fournir le `root_path` { #providing-the-root-path }
Pour y parvenir, vous pouvez utiliser l'option de ligne de commande `--root-path` comme suit :
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Si vous utilisez Hypercorn, il dispose également de l'option `--root-path`.
/// note | Détails techniques
La spécification ASGI définit un `root_path` pour ce cas d'usage.
Et l'option de ligne de commande `--root-path` fournit ce `root_path`.
///
### Vérifier le `root_path` actuel { #checking-the-current-root-path }
Vous pouvez obtenir le `root_path` actuel utilisé par votre application pour chaque requête, il fait partie du dictionnaire `scope` (qui fait partie de la spécification ASGI).
Ici, nous l'incluons dans le message uniquement à des fins de démonstration.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Ensuite, si vous démarrez Uvicorn avec :
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
La réponse sera semblable à :
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Définir le `root_path` dans l'application FastAPI { #setting-the-root-path-in-the-fastapi-app }
Autrement, si vous n'avez pas la possibilité de fournir une option de ligne de commande comme `--root-path` ou équivalent, vous pouvez définir le paramètre `root_path` lors de la création de votre application FastAPI :
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Passer le `root_path` à `FastAPI` équivaut à passer l'option de ligne de commande `--root-path` à Uvicorn ou Hypercorn.
### À propos de `root_path` { #about-root-path }
Gardez à l'esprit que le serveur (Uvicorn) n'utilisera ce `root_path` que pour le transmettre à l'application.
Mais si vous allez avec votre navigateur sur [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), vous verrez la réponse normale :
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Donc, il ne s'attendra pas à être accessible à `http://127.0.0.1:8000/api/v1/app`.
Uvicorn s'attendra à ce que le proxy accède à Uvicorn sur `http://127.0.0.1:8000/app`, et ce sera ensuite la responsabilité du proxy d'ajouter le préfixe supplémentaire `/api/v1` au‑dessus.
## À propos des proxies avec un préfixe de chemin supprimé { #about-proxies-with-a-stripped-path-prefix }
Gardez à l'esprit qu'un proxy avec préfixe de chemin supprimé n'est qu'une des façons de le configurer.
Dans de nombreux cas, la valeur par défaut sera probablement que le proxy n'a pas de préfixe de chemin supprimé.
Dans un cas comme celui‑ci (sans préfixe de chemin supprimé), le proxy écoutera sur quelque chose comme `https://myawesomeapp.com`, puis si le navigateur va sur `https://myawesomeapp.com/api/v1/app` et que votre serveur (par ex. Uvicorn) écoute sur `http://127.0.0.1:8000`, le proxy (sans préfixe de chemin supprimé) accédera à Uvicorn au même chemin : `http://127.0.0.1:8000/api/v1/app`.
## Tester localement avec Traefik { #testing-locally-with-traefik }
Vous pouvez facilement faire l'expérience en local avec un préfixe de chemin supprimé en utilisant [Traefik](https://docs.traefik.io/).
[Téléchargez Traefik](https://github.com/containous/traefik/releases) ; c'est un binaire unique, vous pouvez extraire le fichier compressé et l'exécuter directement depuis le terminal.
Créez ensuite un fichier `traefik.toml` avec :
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Cela indique à Traefik d'écouter sur le port 9999 et d'utiliser un autre fichier `routes.toml`.
/// tip | Astuce
Nous utilisons le port 9999 au lieu du port HTTP standard 80 afin que vous n'ayez pas à l'exécuter avec des privilèges administrateur (`sudo`).
///
Créez maintenant cet autre fichier `routes.toml` :
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Ce fichier configure Traefik pour utiliser le préfixe de chemin `/api/v1`.
Puis Traefik redirigera ses requêtes vers votre Uvicorn tournant sur `http://127.0.0.1:8000`.
Démarrez maintenant Traefik :
Et démarrez maintenant votre application, en utilisant l'option `--root-path` :
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Vérifier les réponses { #check-the-responses }
Maintenant, si vous allez à l'URL avec le port pour Uvicorn : [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), vous verrez la réponse normale :
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Astuce
Remarquez que même si vous y accédez via `http://127.0.0.1:8000/app`, il affiche le `root_path` de `/api/v1`, repris depuis l'option `--root-path`.
///
Et maintenant ouvrez l'URL avec le port pour Traefik, en incluant le préfixe de chemin : [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Nous obtenons la même réponse :
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
mais cette fois à l'URL avec le préfixe fourni par le proxy : `/api/v1`.
Bien sûr, l'idée ici est que tout le monde accède à l'application via le proxy ; la version avec le préfixe de chemin `/api/v1` est donc la « correcte ».
Et la version sans préfixe de chemin (`http://127.0.0.1:8000/app`), fournie directement par Uvicorn, serait exclusivement destinée au _proxy_ (Traefik) pour y accéder.
Cela montre comment le Proxy (Traefik) utilise le préfixe de chemin et comment le serveur (Uvicorn) utilise le `root_path` fourni par l'option `--root-path`.
### Vérifier l'interface de documentation { #check-the-docs-ui }
Mais voici la partie intéressante. ✨
La manière « officielle » d'accéder à l'application serait via le proxy avec le préfixe de chemin que nous avons défini. Donc, comme on s'y attend, si vous essayez l'interface de documentation servie directement par Uvicorn, sans le préfixe de chemin dans l'URL, cela ne fonctionne pas, car elle s'attend à être accédée via le proxy.
Vous pouvez le vérifier sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) :
Mais si nous accédons à l'interface de documents à l'URL « officielle » en utilisant le proxy avec le port `9999`, à `/api/v1/docs`, cela fonctionne correctement ! 🎉
Vous pouvez le vérifier sur [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) :
Exactement comme nous le voulions. ✔️
C'est parce que FastAPI utilise ce `root_path` pour créer le `server` par défaut dans OpenAPI avec l'URL fournie par `root_path`.
## Serveurs supplémentaires { #additional-servers }
/// warning | Alertes
Ceci est un cas d'utilisation plus avancé. N'hésitez pas à l'ignorer.
///
Par défaut, **FastAPI** créera un `server` dans le schéma OpenAPI avec l'URL correspondant au `root_path`.
Mais vous pouvez aussi fournir d'autres `servers` alternatifs, par exemple si vous voulez que la même interface de documents interagisse avec un environnement de staging et un environnement de production.
Si vous passez une liste personnalisée de `servers` et qu'il y a un `root_path` (parce que votre API vit derrière un proxy), **FastAPI** insérera un « server » avec ce `root_path` au début de la liste.
Par exemple :
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Générera un schéma OpenAPI comme :
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Plus d'éléments ici
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Plus d'éléments ici
}
}
```
/// tip | Astuce
Remarquez le serveur généré automatiquement avec une valeur `url` de `/api/v1`, repris depuis le `root_path`.
///
Dans l'interface de documents sur [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs), cela ressemblera à ceci :
/// tip | Astuce
L'interface de documents interagit avec le serveur que vous sélectionnez.
///
/// note | Détails techniques
La propriété `servers` dans la spécification OpenAPI est facultative.
Si vous ne spécifiez pas le paramètre `servers` et que `root_path` est égal à `/`, la propriété `servers` dans le schéma OpenAPI généré sera entièrement omise par défaut, ce qui équivaut à un seul serveur avec une valeur `url` de `/`.
///
### Désactiver le serveur automatique issu de `root_path` { #disable-automatic-server-from-root-path }
Si vous ne voulez pas que **FastAPI** inclue un serveur automatique utilisant le `root_path`, vous pouvez utiliser le paramètre `root_path_in_servers=False` :
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
et il ne l'inclura alors pas dans le schéma OpenAPI.
## Monter une sous-application { #mounting-a-sub-application }
Si vous avez besoin de monter une sous‑application (comme décrit dans [Sous‑applications - montages](sub-applications.md)) tout en utilisant un proxy avec `root_path`, vous pouvez le faire normalement, comme vous vous y attendez.
FastAPI utilisera intelligemment le `root_path` en interne, donc cela fonctionnera simplement. ✨
================================================
FILE: docs/fr/docs/advanced/custom-response.md
================================================
# Réponse personnalisée - HTML, flux, fichier, autres { #custom-response-html-stream-file-others }
Par défaut, **FastAPI** renvoie des réponses JSON.
Vous pouvez le remplacer en renvoyant une `Response` directement comme vu dans [Renvoyer une Response directement](response-directly.md).
Mais si vous renvoyez directement une `Response` (ou n'importe quelle sous-classe, comme `JSONResponse`), les données ne seront pas automatiquement converties (même si vous déclarez un `response_model`), et la documentation ne sera pas générée automatiquement (par exemple, l'inclusion du « media type », dans l'en-tête HTTP `Content-Type` comme partie de l'OpenAPI généré).
Vous pouvez aussi déclarer la `Response` que vous voulez utiliser (par ex. toute sous-classe de `Response`), dans le décorateur de *chemin d'accès* en utilisant le paramètre `response_class`.
Le contenu que vous renvoyez depuis votre *fonction de chemin d'accès* sera placé à l'intérieur de cette `Response`.
/// note | Remarque
Si vous utilisez une classe de réponse sans media type, FastAPI s'attendra à ce que votre réponse n'ait pas de contenu ; il ne documentera donc pas le format de la réponse dans la documentation OpenAPI générée.
///
## Réponses JSON { #json-responses }
Par défaut, FastAPI renvoie des réponses JSON.
Si vous déclarez un [Modèle de réponse](../tutorial/response-model.md), FastAPI l'utilisera pour sérialiser les données en JSON, en utilisant Pydantic.
Si vous ne déclarez pas de modèle de réponse, FastAPI utilisera le `jsonable_encoder` expliqué dans [Encodeur compatible JSON](../tutorial/encoder.md) et le placera dans une `JSONResponse`.
Si vous déclarez une `response_class` avec un media type JSON (`application/json`), comme c'est le cas avec `JSONResponse`, les données que vous renvoyez seront automatiquement converties (et filtrées) avec tout `response_model` Pydantic que vous avez déclaré dans le décorateur de *chemin d'accès*. Mais les données ne seront pas sérialisées en octets JSON avec Pydantic, elles seront converties avec le `jsonable_encoder` puis passées à la classe `JSONResponse`, qui les sérialisera en octets en utilisant la bibliothèque JSON standard de Python.
### Performance JSON { #json-performance }
En bref, si vous voulez la performance maximale, utilisez un [Modèle de réponse](../tutorial/response-model.md) et ne déclarez pas de `response_class` dans le décorateur de *chemin d'accès*.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## Réponse HTML { #html-response }
Pour renvoyer une réponse avec du HTML directement depuis **FastAPI**, utilisez `HTMLResponse`.
- Importez `HTMLResponse`.
- Passez `HTMLResponse` comme paramètre `response_class` de votre *décorateur de chemin d'accès*.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info
Le paramètre `response_class` sera aussi utilisé pour définir le « media type » de la réponse.
Dans ce cas, l'en-tête HTTP `Content-Type` sera défini à `text/html`.
Et il sera documenté comme tel dans OpenAPI.
///
### Renvoyer une `Response` { #return-a-response }
Comme vu dans [Renvoyer une Response directement](response-directly.md), vous pouvez aussi remplacer la réponse directement dans votre *chemin d'accès*, en la renvoyant.
Le même exemple ci-dessus, renvoyant une `HTMLResponse`, pourrait ressembler à :
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Alertes
Une `Response` renvoyée directement par votre *fonction de chemin d'accès* ne sera pas documentée dans OpenAPI (par exemple, le `Content-Type` ne sera pas documenté) et ne sera pas visible dans les documents interactifs automatiques.
///
/// info
Bien sûr, l'en-tête `Content-Type` réel, le code d'état, etc., proviendront de l'objet `Response` que vous avez renvoyé.
///
### Documenter dans OpenAPI et remplacer `Response` { #document-in-openapi-and-override-response }
Si vous voulez remplacer la réponse depuis l'intérieur de la fonction mais en même temps documenter le « media type » dans OpenAPI, vous pouvez utiliser le paramètre `response_class` ET renvoyer un objet `Response`.
`response_class` sera alors utilisé uniquement pour documenter l’*opération de chemin d'accès* OpenAPI, mais votre `Response` sera utilisée telle quelle.
#### Renvoyer directement une `HTMLResponse` { #return-an-htmlresponse-directly }
Par exemple, cela pourrait être quelque chose comme :
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
Dans cet exemple, la fonction `generate_html_response()` génère déjà et renvoie une `Response` au lieu de renvoyer le HTML dans une `str`.
En renvoyant le résultat de l'appel à `generate_html_response()`, vous renvoyez déjà une `Response` qui remplacera le comportement par défaut de **FastAPI**.
Mais comme vous avez aussi passé `HTMLResponse` dans `response_class`, **FastAPI** saura comment la documenter dans OpenAPI et les documents interactifs comme HTML avec `text/html` :
## Réponses disponibles { #available-responses }
Voici certaines des réponses disponibles.
Gardez à l'esprit que vous pouvez utiliser `Response` pour renvoyer autre chose, ou même créer une sous-classe personnalisée.
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.responses import HTMLResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre confort de développement. Mais la plupart des réponses disponibles viennent directement de Starlette.
///
### `Response` { #response }
La classe principale `Response`, toutes les autres réponses en héritent.
Vous pouvez la renvoyer directement.
Elle accepte les paramètres suivants :
- `content` - Une `str` ou des `bytes`.
- `status_code` - Un code d'état HTTP de type `int`.
- `headers` - Un `dict` de chaînes.
- `media_type` - Une `str` donnant le media type. Par exemple « text/html ».
FastAPI (en fait Starlette) inclura automatiquement un en-tête Content-Length. Il inclura aussi un en-tête Content-Type, basé sur `media_type` et en ajoutant un charset pour les types textuels.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Prend du texte ou des octets et renvoie une réponse HTML, comme vous l'avez vu ci-dessus.
### `PlainTextResponse` { #plaintextresponse }
Prend du texte ou des octets et renvoie une réponse en texte brut.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Prend des données et renvoie une réponse encodée en `application/json`.
C'est la réponse par défaut utilisée dans **FastAPI**, comme vous l'avez lu ci-dessus.
/// note | Détails techniques
Mais si vous déclarez un modèle de réponse ou un type de retour, il sera utilisé directement pour sérialiser les données en JSON, et une réponse avec le bon media type pour JSON sera renvoyée directement, sans utiliser la classe `JSONResponse`.
C'est la manière idéale d'obtenir la meilleure performance.
///
### `RedirectResponse` { #redirectresponse }
Renvoie une redirection HTTP. Utilise par défaut un code d'état 307 (Temporary Redirect).
Vous pouvez renvoyer directement une `RedirectResponse` :
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Ou vous pouvez l'utiliser dans le paramètre `response_class` :
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Si vous faites cela, vous pouvez alors renvoyer directement l'URL depuis votre *fonction de chemin d'accès*.
Dans ce cas, le `status_code` utilisé sera celui par défaut pour `RedirectResponse`, c'est-à-dire `307`.
---
Vous pouvez aussi utiliser le paramètre `status_code` combiné avec le paramètre `response_class` :
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Prend un générateur async ou un générateur/itérateur normal (une fonction avec `yield`) et diffuse le corps de la réponse.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Détails techniques
Une tâche `async` ne peut être annulée que lorsqu'elle atteint un `await`. S'il n'y a pas de `await`, le générateur (fonction avec `yield`) ne peut pas être annulé correctement et peut continuer à s'exécuter même après la demande d'annulation.
Comme ce petit exemple n'a besoin d'aucune instruction `await`, nous ajoutons un `await anyio.sleep(0)` pour donner une chance à la boucle d'événements de gérer l'annulation.
Cela serait encore plus important avec des flux volumineux ou infinis.
///
/// tip | Astuce
Au lieu de renvoyer une `StreamingResponse` directement, vous devriez probablement suivre le style de [Diffuser des données](./stream-data.md), c'est beaucoup plus pratique et gère l'annulation en arrière-plan pour vous.
Si vous diffusez des JSON Lines, suivez le didacticiel [Diffuser des JSON Lines](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Diffuse de façon asynchrone un fichier comme réponse.
Prend un ensemble de paramètres différent à l'instanciation par rapport aux autres types de réponse :
- `path` - Le chemin du fichier à diffuser.
- `headers` - D'éventuels en-têtes personnalisés à inclure, sous forme de dictionnaire.
- `media_type` - Une chaîne donnant le media type. Si non défini, le nom du fichier ou le chemin sera utilisé pour en déduire un media type.
- `filename` - Si défini, sera inclus dans l'en-tête `Content-Disposition` de la réponse.
Les réponses de type fichier incluront les en-têtes appropriés `Content-Length`, `Last-Modified` et `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Vous pouvez aussi utiliser le paramètre `response_class` :
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
Dans ce cas, vous pouvez renvoyer directement le chemin du fichier depuis votre *fonction de chemin d'accès*.
## Classe de réponse personnalisée { #custom-response-class }
Vous pouvez créer votre propre classe de réponse personnalisée, héritant de `Response`, et l'utiliser.
Par exemple, disons que vous voulez utiliser [`orjson`](https://github.com/ijl/orjson) avec certains réglages.
Disons que vous voulez renvoyer du JSON indenté et formaté, donc vous voulez utiliser l'option orjson `orjson.OPT_INDENT_2`.
Vous pourriez créer une `CustomORJSONResponse`. L'essentiel est de créer une méthode `Response.render(content)` qui renvoie le contenu en `bytes` :
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Maintenant, au lieu de renvoyer :
```json
{"message": "Hello World"}
```
... cette réponse renverra :
```json
{
"message": "Hello World"
}
```
Bien sûr, vous trouverez probablement des moyens bien meilleurs de tirer parti de cela que de formater du JSON. 😉
### `orjson` ou Modèle de réponse { #orjson-or-response-model }
Si ce que vous recherchez est la performance, vous aurez probablement de meilleurs résultats en utilisant un [Modèle de réponse](../tutorial/response-model.md) qu'une réponse `orjson`.
Avec un modèle de réponse, FastAPI utilisera Pydantic pour sérialiser les données en JSON, sans étapes intermédiaires, comme la conversion avec `jsonable_encoder`, qui se produirait dans tout autre cas.
Et en interne, Pydantic utilise les mêmes mécanismes Rust sous-jacents que `orjson` pour sérialiser en JSON, vous obtiendrez donc déjà la meilleure performance avec un modèle de réponse.
## Classe de réponse par défaut { #default-response-class }
Lors de la création d'une instance de classe **FastAPI** ou d'un `APIRouter`, vous pouvez spécifier quelle classe de réponse utiliser par défaut.
Le paramètre qui le définit est `default_response_class`.
Dans l'exemple ci-dessous, **FastAPI** utilisera `HTMLResponse` par défaut, dans tous les *chemins d'accès*, au lieu de JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Astuce
Vous pouvez toujours remplacer `response_class` dans les *chemins d'accès* comme auparavant.
///
## Documentation supplémentaire { #additional-documentation }
Vous pouvez aussi déclarer le media type et de nombreux autres détails dans OpenAPI en utilisant `responses` : [Réponses supplémentaires dans OpenAPI](additional-responses.md).
================================================
FILE: docs/fr/docs/advanced/dataclasses.md
================================================
# Utiliser des dataclasses { #using-dataclasses }
FastAPI est construit au‑dessus de **Pydantic**, et je vous ai montré comment utiliser des modèles Pydantic pour déclarer les requêtes et les réponses.
Mais FastAPI prend aussi en charge l'utilisation de [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) de la même manière :
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Cela fonctionne grâce à **Pydantic**, qui offre une [prise en charge interne des `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Ainsi, même avec le code ci‑dessus qui n'emploie pas explicitement Pydantic, FastAPI utilise Pydantic pour convertir ces dataclasses standard en la variante de dataclasses de Pydantic.
Et bien sûr, cela prend en charge la même chose :
* validation des données
* sérialisation des données
* documentation des données, etc.
Cela fonctionne de la même manière qu'avec les modèles Pydantic. Et, en réalité, c'est mis en œuvre de la même façon en interne, en utilisant Pydantic.
/// info
Gardez à l'esprit que les dataclasses ne peuvent pas tout ce que peuvent faire les modèles Pydantic.
Vous pourriez donc avoir encore besoin d'utiliser des modèles Pydantic.
Mais si vous avez déjà un ensemble de dataclasses sous la main, c'est une astuce pratique pour les utiliser afin d'alimenter une API Web avec FastAPI. 🤓
///
## Utiliser des dataclasses dans `response_model` { #dataclasses-in-response-model }
Vous pouvez aussi utiliser `dataclasses` dans le paramètre `response_model` :
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
La dataclass sera automatiquement convertie en dataclass Pydantic.
Ainsi, son schéma apparaîtra dans l'interface utilisateur de la documentation de l'API :
## Utiliser des dataclasses dans des structures de données imbriquées { #dataclasses-in-nested-data-structures }
Vous pouvez aussi combiner `dataclasses` avec d'autres annotations de type pour créer des structures de données imbriquées.
Dans certains cas, vous devrez peut‑être encore utiliser la version `dataclasses` de Pydantic. Par exemple, si vous rencontrez des erreurs avec la documentation d'API générée automatiquement.
Dans ce cas, vous pouvez simplement remplacer les `dataclasses` standard par `pydantic.dataclasses`, qui est un remplacement drop‑in :
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Nous continuons à importer `field` depuis les `dataclasses` standard.
2. `pydantic.dataclasses` est un remplacement drop‑in pour `dataclasses`.
3. La dataclass `Author` inclut une liste de dataclasses `Item`.
4. La dataclass `Author` est utilisée comme paramètre `response_model`.
5. Vous pouvez utiliser d'autres annotations de type standard avec des dataclasses comme corps de la requête.
Dans ce cas, il s'agit d'une liste de dataclasses `Item`.
6. Ici, nous renvoyons un dictionnaire qui contient `items`, qui est une liste de dataclasses.
FastAPI est toujours capable de sérialiser les données en JSON.
7. Ici, `response_model` utilise une annotation de type correspondant à une liste de dataclasses `Author`.
Là encore, vous pouvez combiner `dataclasses` avec des annotations de type standard.
8. Notez que cette *fonction de chemin d'accès* utilise un `def` classique au lieu de `async def`.
Comme toujours, avec FastAPI vous pouvez combiner `def` et `async def` selon vos besoins.
Si vous avez besoin d'un rappel sur quand utiliser l'un ou l'autre, consultez la section _« In a hurry? »_ dans la documentation à propos de [`async` et `await`](../async.md#in-a-hurry).
9. Cette *fonction de chemin d'accès* ne renvoie pas des dataclasses (même si elle le pourrait), mais une liste de dictionnaires contenant des données internes.
FastAPI utilisera le paramètre `response_model` (qui inclut des dataclasses) pour convertir la réponse.
Vous pouvez combiner `dataclasses` avec d'autres annotations de type, selon de nombreuses combinaisons, pour former des structures de données complexes.
Reportez‑vous aux annotations dans le code ci‑dessus pour voir plus de détails spécifiques.
## En savoir plus { #learn-more }
Vous pouvez aussi combiner `dataclasses` avec d'autres modèles Pydantic, en hériter, les inclure dans vos propres modèles, etc.
Pour en savoir plus, consultez la [documentation Pydantic sur les dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Version { #version }
C'est disponible depuis FastAPI version `0.67.0`. 🔖
================================================
FILE: docs/fr/docs/advanced/events.md
================================================
# Événements de cycle de vie { #lifespan-events }
Vous pouvez définir une logique (du code) qui doit être exécutée avant que l'application ne **démarre**. Cela signifie que ce code sera exécuté **une seule fois**, **avant** que l'application ne **commence à recevoir des requêtes**.
De la même manière, vous pouvez définir une logique (du code) qui doit être exécutée lorsque l'application **s'arrête**. Dans ce cas, ce code sera exécuté **une seule fois**, **après** avoir traité potentiellement **de nombreuses requêtes**.
Comme ce code est exécuté avant que l'application ne **commence** à recevoir des requêtes, et juste après qu'elle **termine** de les traiter, il couvre tout le **cycle de vie** de l'application (le mot « lifespan » va être important dans un instant 😉).
Cela peut être très utile pour configurer des **ressources** dont vous avez besoin pour l'ensemble de l'application, qui sont **partagées** entre les requêtes, et/ou que vous devez **nettoyer** ensuite. Par exemple, un pool de connexions à une base de données, ou le chargement d'un modèle d'apprentissage automatique partagé.
## Cas d'utilisation { #use-case }
Commençons par un exemple de **cas d'utilisation**, puis voyons comment le résoudre avec ceci.
Imaginons que vous ayez des **modèles d'apprentissage automatique** que vous souhaitez utiliser pour traiter des requêtes. 🤖
Les mêmes modèles sont partagés entre les requêtes, ce n'est donc pas un modèle par requête, ni un par utilisateur, ou quelque chose de similaire.
Imaginons que le chargement du modèle puisse **prendre pas mal de temps**, car il doit lire beaucoup de **données depuis le disque**. Vous ne voulez donc pas le faire pour chaque requête.
Vous pourriez le charger au niveau supérieur du module/fichier, mais cela signifierait aussi qu'il **chargerait le modèle** même si vous exécutez simplement un test automatisé simple ; ce test serait alors **lent** car il devrait attendre le chargement du modèle avant de pouvoir exécuter une partie indépendante du code.
C'est ce que nous allons résoudre : chargeons le modèle avant que les requêtes ne soient traitées, mais seulement juste avant que l'application ne commence à recevoir des requêtes, pas pendant le chargement du code.
## Cycle de vie { #lifespan }
Vous pouvez définir cette logique de *démarrage* et d'*arrêt* en utilisant le paramètre `lifespan` de l'application `FastAPI`, et un « gestionnaire de contexte » (je vais vous montrer ce que c'est dans un instant).
Commençons par un exemple, puis voyons-le en détail.
Nous créons une fonction async `lifespan()` avec `yield` comme ceci :
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Ici, nous simulons l'opération de *démarrage* coûteuse de chargement du modèle en plaçant la fonction (factice) du modèle dans le dictionnaire avec les modèles d'apprentissage automatique avant le `yield`. Ce code sera exécuté **avant** que l'application ne **commence à recevoir des requêtes**, pendant le *démarrage*.
Puis, juste après le `yield`, nous déchargeons le modèle. Ce code sera exécuté **après** que l'application **a fini de traiter les requêtes**, juste avant l'*arrêt*. Cela pourrait, par exemple, libérer des ressources comme la mémoire ou un GPU.
/// tip | Astuce
Le `shutdown` se produit lorsque vous **arrêtez** l'application.
Peut-être devez-vous démarrer une nouvelle version, ou vous en avez simplement assez de l'exécuter. 🤷
///
### Fonction de cycle de vie { #lifespan-function }
La première chose à remarquer est que nous définissons une fonction async avec `yield`. C'est très similaire aux Dépendances avec `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
La première partie de la fonction, avant le `yield`, sera exécutée **avant** le démarrage de l'application.
Et la partie après le `yield` sera exécutée **après** que l'application a terminé.
### Gestionnaire de contexte asynchrone { #async-context-manager }
Si vous regardez, la fonction est décorée avec `@asynccontextmanager`.
Cela convertit la fonction en quelque chose appelé un « **gestionnaire de contexte asynchrone** ».
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Un **gestionnaire de contexte** en Python est quelque chose que vous pouvez utiliser dans une instruction `with`. Par exemple, `open()` peut être utilisé comme gestionnaire de contexte :
```Python
with open("file.txt") as file:
file.read()
```
Dans les versions récentes de Python, il existe aussi un **gestionnaire de contexte asynchrone**. Vous l'utiliseriez avec `async with` :
```Python
async with lifespan(app):
await do_stuff()
```
Quand vous créez un gestionnaire de contexte ou un gestionnaire de contexte asynchrone comme ci-dessus, ce qu'il fait, c'est qu'avant d'entrer dans le bloc `with`, il exécute le code avant le `yield`, et après être sorti du bloc `with`, il exécute le code après le `yield`.
Dans notre exemple de code ci-dessus, nous ne l'utilisons pas directement, mais nous le transmettons à FastAPI pour qu'il l'utilise.
Le paramètre `lifespan` de l'application `FastAPI` accepte un **gestionnaire de contexte asynchrone**, nous pouvons donc lui passer notre nouveau gestionnaire de contexte asynchrone `lifespan`.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Événements alternatifs (déprécié) { #alternative-events-deprecated }
/// warning | Alertes
La méthode recommandée pour gérer le *démarrage* et l'*arrêt* est d'utiliser le paramètre `lifespan` de l'application `FastAPI` comme décrit ci-dessus. Si vous fournissez un paramètre `lifespan`, les gestionnaires d'événements `startup` et `shutdown` ne seront plus appelés. C'est soit tout en `lifespan`, soit tout en événements, pas les deux.
Vous pouvez probablement passer cette partie.
///
Il existe une autre manière de définir cette logique à exécuter au *démarrage* et à l'*arrêt*.
Vous pouvez définir des gestionnaires d'événements (fonctions) qui doivent être exécutés avant le démarrage de l'application, ou lorsque l'application s'arrête.
Ces fonctions peuvent être déclarées avec `async def` ou un `def` normal.
### Événement `startup` { #startup-event }
Pour ajouter une fonction qui doit être exécutée avant le démarrage de l'application, déclarez-la avec l'événement « startup » :
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
Dans ce cas, la fonction gestionnaire de l'événement `startup` initialisera la « base de données » des items (juste un `dict`) avec quelques valeurs.
Vous pouvez ajouter plusieurs fonctions de gestion d'événements.
Et votre application ne commencera pas à recevoir des requêtes avant que tous les gestionnaires de l'événement `startup` aient terminé.
### Événement `shutdown` { #shutdown-event }
Pour ajouter une fonction qui doit être exécutée lorsque l'application s'arrête, déclarez-la avec l'événement « shutdown » :
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Ici, la fonction gestionnaire de l'événement `shutdown` écrira une ligne de texte « Application shutdown » dans un fichier `log.txt`.
/// info
Dans la fonction `open()`, le `mode="a"` signifie « append » (ajouter) ; la ligne sera donc ajoutée après ce qui se trouve déjà dans ce fichier, sans écraser le contenu précédent.
///
/// tip | Astuce
Notez que dans ce cas, nous utilisons une fonction Python standard `open()` qui interagit avec un fichier.
Cela implique des E/S (input/output), qui nécessitent « d'attendre » que des choses soient écrites sur le disque.
Mais `open()` n'utilise pas `async` et `await`.
Nous déclarons donc la fonction gestionnaire d'événement avec un `def` standard plutôt qu'avec `async def`.
///
### `startup` et `shutdown` ensemble { #startup-and-shutdown-together }
Il y a de fortes chances que la logique de votre *démarrage* et de votre *arrêt* soit liée : vous pourriez vouloir démarrer quelque chose puis le terminer, acquérir une ressource puis la libérer, etc.
Faire cela dans des fonctions séparées qui ne partagent pas de logique ni de variables est plus difficile, car vous devriez stocker des valeurs dans des variables globales ou recourir à des astuces similaires.
Pour cette raison, il est désormais recommandé d'utiliser plutôt le `lifespan` comme expliqué ci-dessus.
## Détails techniques { #technical-details }
Juste un détail technique pour les nerds curieux. 🤓
Sous le capot, dans la spécification technique ASGI, cela fait partie du [protocole Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), et il y définit des événements appelés `startup` et `shutdown`.
/// info
Vous pouvez en lire plus sur les gestionnaires `lifespan` de Starlette dans la [documentation « Lifespan » de Starlette](https://www.starlette.dev/lifespan/).
Y compris comment gérer l'état de cycle de vie qui peut être utilisé dans d'autres parties de votre code.
///
## Sous-applications { #sub-applications }
🚨 Gardez à l'esprit que ces événements de cycle de vie (démarrage et arrêt) ne seront exécutés que pour l'application principale, pas pour [Sous-applications - Montages](sub-applications.md).
================================================
FILE: docs/fr/docs/advanced/generate-clients.md
================================================
# Générer des SDK { #generating-sdks }
Parce que **FastAPI** est basé sur la spécification **OpenAPI**, ses API peuvent être décrites dans un format standard compris par de nombreux outils.
Cela facilite la génération de **documentation** à jour, de bibliothèques clientes (**SDKs**) dans plusieurs langages, ainsi que de **tests** ou de **workflows d’automatisation** qui restent synchronisés avec votre code.
Dans ce guide, vous apprendrez à générer un **SDK TypeScript** pour votre backend FastAPI.
## Générateurs de SDK open source { #open-source-sdk-generators }
Une option polyvalente est le [OpenAPI Generator](https://openapi-generator.tech/), qui prend en charge **de nombreux langages de programmation** et peut générer des SDK à partir de votre spécification OpenAPI.
Pour les **clients TypeScript**, [Hey API](https://heyapi.dev/) est une solution dédiée, offrant une expérience optimisée pour l’écosystème TypeScript.
Vous pouvez découvrir davantage de générateurs de SDK sur [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Astuce
FastAPI génère automatiquement des spécifications **OpenAPI 3.1**, donc tout outil que vous utilisez doit prendre en charge cette version.
///
## Générateurs de SDK par les sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
Cette section met en avant des solutions **soutenues par des fonds** et **par des entreprises** qui sponsorisent FastAPI. Ces produits offrent **des fonctionnalités supplémentaires** et **des intégrations** en plus de SDK de haute qualité générés.
En ✨ [**sponsorisant FastAPI**](../help-fastapi.md#sponsor-the-author) ✨, ces entreprises contribuent à garantir que le framework et son **écosystème** restent sains et **durables**.
Leur sponsoring démontre également un fort engagement envers la **communauté** FastAPI (vous), montrant qu’elles se soucient non seulement d’offrir un **excellent service**, mais aussi de soutenir un **framework robuste et florissant**, FastAPI. 🙇
Par exemple, vous pourriez essayer :
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Certaines de ces solutions peuvent aussi être open source ou proposer des niveaux gratuits, afin que vous puissiez les essayer sans engagement financier. D’autres générateurs de SDK commerciaux existent et peuvent être trouvés en ligne. 🤓
## Créer un SDK TypeScript { #create-a-typescript-sdk }
Commençons par une application FastAPI simple :
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Remarquez que les *chemins d'accès* définissent les modèles qu’ils utilisent pour le payload de requête et le payload de réponse, en utilisant les modèles `Item` et `ResponseMessage`.
### Documentation de l’API { #api-docs }
Si vous allez sur `/docs`, vous verrez qu’elle contient les **schémas** pour les données à envoyer dans les requêtes et reçues dans les réponses :
Vous voyez ces schémas parce qu’ils ont été déclarés avec les modèles dans l’application.
Ces informations sont disponibles dans le **schéma OpenAPI** de l’application, puis affichées dans la documentation de l’API.
Ces mêmes informations issues des modèles, incluses dans OpenAPI, peuvent être utilisées pour **générer le code client**.
### Hey API { #hey-api }
Une fois que vous avez une application FastAPI avec les modèles, vous pouvez utiliser Hey API pour générer un client TypeScript. Le moyen le plus rapide de le faire est via npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Cela générera un SDK TypeScript dans `./src/client`.
Vous pouvez apprendre à [installer `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started) et lire à propos du [résultat généré](https://heyapi.dev/openapi-ts/output) sur leur site.
### Utiliser le SDK { #using-the-sdk }
Vous pouvez maintenant importer et utiliser le code client. Cela pourrait ressembler à ceci, remarquez que vous obtenez l’autocomplétion pour les méthodes :
Vous obtiendrez également l’autocomplétion pour le payload à envoyer :
/// tip | Astuce
Remarquez l’autocomplétion pour `name` et `price`, qui a été définie dans l’application FastAPI, dans le modèle `Item`.
///
Vous aurez des erreurs en ligne pour les données que vous envoyez :
L’objet de réponse aura également l’autocomplétion :
## Application FastAPI avec des tags { #fastapi-app-with-tags }
Dans de nombreux cas, votre application FastAPI sera plus grande, et vous utiliserez probablement des tags pour séparer différents groupes de *chemins d'accès*.
Par exemple, vous pourriez avoir une section pour les **items** et une autre section pour les **users**, et elles pourraient être séparées par des tags :
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Générer un client TypeScript avec des tags { #generate-a-typescript-client-with-tags }
Si vous générez un client pour une application FastAPI utilisant des tags, il séparera normalement aussi le code client en fonction des tags.
De cette façon, vous pourrez avoir les éléments ordonnés et correctement groupés côté client :
Dans ce cas, vous avez :
* `ItemsService`
* `UsersService`
### Noms des méthodes du client { #client-method-names }
À l’heure actuelle, les noms de méthodes générés comme `createItemItemsPost` ne sont pas très propres :
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
... c’est parce que le générateur de client utilise l’**operation ID** interne OpenAPI pour chaque *chemin d'accès*.
OpenAPI exige que chaque operation ID soit unique parmi tous les *chemins d'accès*, donc FastAPI utilise le **nom de la fonction**, le **chemin**, et la **méthode/opération HTTP** pour générer cet operation ID, car de cette façon il peut s’assurer que les operation IDs sont uniques.
Mais je vais vous montrer comment améliorer cela ensuite. 🤓
## IDs d’opération personnalisés et meilleurs noms de méthodes { #custom-operation-ids-and-better-method-names }
Vous pouvez **modifier** la façon dont ces operation IDs sont **générés** pour les simplifier et obtenir des **noms de méthodes plus simples** dans les clients.
Dans ce cas, vous devez vous assurer que chaque operation ID est **unique** d’une autre manière.
Par exemple, vous pouvez vous assurer que chaque *chemin d'accès* a un tag, puis générer l’operation ID à partir du **tag** et du **nom** du *chemin d'accès* (le nom de la fonction).
### Fonction personnalisée de génération d’ID unique { #custom-generate-unique-id-function }
FastAPI utilise un **ID unique** pour chaque *chemin d'accès*, qui est utilisé pour l’**operation ID** et également pour les noms des modèles personnalisés nécessaires, pour les requêtes ou les réponses.
Vous pouvez personnaliser cette fonction. Elle prend un `APIRoute` et retourne une chaîne.
Par exemple, ici elle utilise le premier tag (vous n’en aurez probablement qu’un) et le nom du *chemin d'accès* (le nom de la fonction).
Vous pouvez ensuite passer cette fonction personnalisée à **FastAPI** via le paramètre `generate_unique_id_function` :
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Générer un client TypeScript avec des IDs d’opération personnalisés { #generate-a-typescript-client-with-custom-operation-ids }
Maintenant, si vous régénérez le client, vous verrez qu’il possède des noms de méthodes améliorés :
Comme vous le voyez, les noms de méthodes contiennent maintenant le tag puis le nom de la fonction ; ils n’incluent plus d’informations provenant du chemin d’URL et de l’opération HTTP.
### Prétraiter la spécification OpenAPI pour le générateur de client { #preprocess-the-openapi-specification-for-the-client-generator }
Le code généré contient encore des **informations dupliquées**.
Nous savons déjà que cette méthode est liée aux **items** parce que ce mot figure dans `ItemsService` (issu du tag), mais nous avons encore le nom du tag préfixé dans le nom de la méthode. 😕
Nous voudrons probablement le conserver pour OpenAPI en général, car cela garantira que les operation IDs sont **uniques**.
Mais pour le client généré, nous pourrions **modifier** les operation IDs d’OpenAPI juste avant de générer les clients, simplement pour rendre ces noms de méthodes plus agréables et **plus clairs**.
Nous pourrions télécharger le JSON OpenAPI dans un fichier `openapi.json` puis **supprimer ce tag préfixé** avec un script comme celui-ci :
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Avec cela, les operation IDs seraient renommés de `items-get_items` en simplement `get_items`, de sorte que le générateur de client puisse produire des noms de méthodes plus simples.
### Générer un client TypeScript avec l’OpenAPI prétraité { #generate-a-typescript-client-with-the-preprocessed-openapi }
Puisque le résultat final se trouve maintenant dans un fichier `openapi.json`, vous devez mettre à jour l’emplacement d’entrée :
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Après avoir généré le nouveau client, vous aurez désormais des **noms de méthodes propres**, avec toute l’**autocomplétion**, les **erreurs en ligne**, etc. :
## Avantages { #benefits }
En utilisant les clients générés automatiquement, vous obtiendrez de l’**autocomplétion** pour :
* Méthodes.
* Payloads de requête dans le corps, paramètres de requête, etc.
* Payloads de réponse.
Vous auriez également des **erreurs en ligne** pour tout.
Et chaque fois que vous mettez à jour le code du backend et **régénérez** le frontend, il inclura les nouveaux *chemins d'accès* disponibles en tant que méthodes, supprimera les anciens, et tout autre changement sera reflété dans le code généré. 🤓
Cela signifie aussi que si quelque chose change, cela sera **reflété** automatiquement dans le code client. Et si vous **bâtissez** le client, il échouera en cas de **discordance** dans les données utilisées.
Ainsi, vous **détecterez de nombreuses erreurs** très tôt dans le cycle de développement au lieu d’attendre qu’elles apparaissent pour vos utilisateurs finaux en production puis de tenter de déboguer l’origine du problème. ✨
================================================
FILE: docs/fr/docs/advanced/index.md
================================================
# Guide de l'utilisateur avancé { #advanced-user-guide }
## Caractéristiques supplémentaires { #additional-features }
Le [Tutoriel - Guide de l'utilisateur](../tutorial/index.md) devrait suffire à vous faire découvrir toutes les fonctionnalités principales de **FastAPI**.
Dans les sections suivantes, vous verrez des options, configurations et fonctionnalités supplémentaires.
/// tip | Astuce
Les sections suivantes ne sont **pas nécessairement « avancées »**.
Et il est possible que, pour votre cas d'utilisation, la solution se trouve dans l'une d'entre elles.
///
## Lire d'abord le tutoriel { #read-the-tutorial-first }
Vous pouvez utiliser la plupart des fonctionnalités de **FastAPI** grâce aux connaissances du [Tutoriel - Guide de l'utilisateur](../tutorial/index.md).
Et les sections suivantes supposent que vous l'avez lu et que vous en connaissez les idées principales.
================================================
FILE: docs/fr/docs/advanced/json-base64-bytes.md
================================================
# JSON avec des octets en Base64 { #json-with-bytes-as-base64 }
Si votre application doit recevoir et envoyer des données JSON, mais que vous devez y inclure des données binaires, vous pouvez les encoder en base64.
## Base64 vs fichiers { #base64-vs-files }
Envisagez d'abord d'utiliser [Fichiers de requête](../tutorial/request-files.md) pour téléverser des données binaires et [Réponse personnalisée - FileResponse](./custom-response.md#fileresponse--fileresponse-) pour envoyer des données binaires, plutôt que de les encoder dans du JSON.
JSON ne peut contenir que des chaînes encodées en UTF-8, il ne peut donc pas contenir d'octets bruts.
Base64 peut encoder des données binaires en chaînes, mais pour cela il doit utiliser plus de caractères que les données binaires originales ; c'est donc en général moins efficace que des fichiers classiques.
N'utilisez base64 que si vous devez absolument inclure des données binaires dans du JSON et que vous ne pouvez pas utiliser des fichiers pour cela.
## Pydantic `bytes` { #pydantic-bytes }
Vous pouvez déclarer un modèle Pydantic avec des champs `bytes`, puis utiliser `val_json_bytes` dans la configuration du modèle pour lui indiquer d'utiliser base64 pour valider les données JSON en entrée ; dans le cadre de cette validation, il décodera la chaîne base64 en octets.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Si vous allez sur les `/docs`, vous verrez que le champ `data` attend des octets encodés en base64 :
Vous pourriez envoyer une requête comme :
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Astuce
`aGVsbG8=` est l'encodage base64 de `hello`.
///
Pydantic décodera ensuite la chaîne base64 et vous fournira les octets originaux dans le champ `data` du modèle.
Vous recevrez une réponse comme :
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` pour les données de sortie { #pydantic-bytes-for-output-data }
Vous pouvez également utiliser des champs `bytes` avec `ser_json_bytes` dans la configuration du modèle pour les données de sortie ; Pydantic sérialisera alors les octets en base64 lors de la génération de la réponse JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` pour les données d'entrée et de sortie { #pydantic-bytes-for-input-and-output-data }
Et bien sûr, vous pouvez utiliser le même modèle configuré pour utiliser base64 afin de gérer à la fois l'entrée (valider) avec `val_json_bytes` et la sortie (sérialiser) avec `ser_json_bytes` lors de la réception et de l'envoi de données JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/fr/docs/advanced/middleware.md
================================================
# Utiliser des middlewares avancés { #advanced-middleware }
Dans le tutoriel principal, vous avez vu comment ajouter des [middlewares personnalisés](../tutorial/middleware.md) à votre application.
Vous avez également vu comment gérer [CORS avec le `CORSMiddleware`](../tutorial/cors.md).
Dans cette section, nous allons voir comment utiliser d'autres middlewares.
## Ajouter des middlewares ASGI { #adding-asgi-middlewares }
Comme **FastAPI** est basé sur Starlette et implémente la spécification ASGI, vous pouvez utiliser n'importe quel middleware ASGI.
Un middleware n'a pas besoin d'être conçu pour FastAPI ou Starlette pour fonctionner, tant qu'il suit la spécification ASGI.
En général, les middlewares ASGI sont des classes qui s'attendent à recevoir une application ASGI en premier argument.
Ainsi, dans la documentation de middlewares ASGI tiers, on vous indiquera probablement de faire quelque chose comme :
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Mais FastAPI (en fait Starlette) fournit une manière plus simple de le faire, qui garantit que les middlewares internes gèrent les erreurs serveur et que les gestionnaires d'exceptions personnalisés fonctionnent correctement.
Pour cela, vous utilisez `app.add_middleware()` (comme dans l'exemple pour CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` reçoit une classe de middleware en premier argument, ainsi que tout argument supplémentaire à transmettre au middleware.
## Utiliser les middlewares intégrés { #integrated-middlewares }
**FastAPI** inclut plusieurs middlewares pour des cas d'usage courants ; voyons comment les utiliser.
/// note | Détails techniques
Pour les prochains exemples, vous pourriez aussi utiliser `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** fournit plusieurs middlewares dans `fastapi.middleware` simplement pour vous faciliter la vie, en tant que développeur. Mais la plupart des middlewares disponibles viennent directement de Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Impose que toutes les requêtes entrantes soient soit `https`, soit `wss`.
Toute requête entrante en `http` ou `ws` sera redirigée vers le schéma sécurisé correspondant.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Impose que toutes les requêtes entrantes aient un en-tête `Host` correctement défini, afin de se prémunir contre les attaques de type HTTP Host Header.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Les arguments suivants sont pris en charge :
- `allowed_hosts` - Une liste de noms de domaine autorisés comme noms d'hôte. Les domaines génériques tels que `*.example.com` sont pris en charge pour faire correspondre les sous-domaines. Pour autoriser n'importe quel nom d'hôte, utilisez `allowed_hosts=["*"]` ou omettez le middleware.
- `www_redirect` - Si défini à `True`, les requêtes vers les versions sans www des hôtes autorisés seront redirigées vers leurs équivalents avec www. Valeur par défaut : `True`.
Si une requête entrante n'est pas valide, une réponse `400` sera envoyée.
## `GZipMiddleware` { #gzipmiddleware }
Gère les réponses GZip pour toute requête qui inclut « gzip » dans l'en-tête `Accept-Encoding`.
Le middleware gérera les réponses standard et en streaming.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Les arguments suivants sont pris en charge :
- `minimum_size` - Ne pas compresser en GZip les réponses dont la taille est inférieure à ce minimum en octets. Valeur par défaut : `500`.
- `compresslevel` - Utilisé pendant la compression GZip. Entier compris entre 1 et 9. Valeur par défaut : `9`. Une valeur plus faible entraîne une compression plus rapide mais des fichiers plus volumineux, tandis qu'une valeur plus élevée entraîne une compression plus lente mais des fichiers plus petits.
## Autres middlewares { #other-middlewares }
Il existe de nombreux autres middlewares ASGI.
Par exemple :
- [Le `ProxyHeadersMiddleware` d'Uvicorn](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
- [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Pour voir d'autres middlewares disponibles, consultez la [documentation des middlewares de Starlette](https://www.starlette.dev/middleware/) et la [liste ASGI Awesome](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/fr/docs/advanced/openapi-callbacks.md
================================================
# Callbacks OpenAPI { #openapi-callbacks }
Vous pourriez créer une API avec un *chemin d'accès* qui déclenche une requête vers une *API externe* créée par quelqu'un d'autre (probablement la même personne développeuse qui utiliserait votre API).
Le processus qui se produit lorsque votre application API appelle l’*API externe* s’appelle un « callback ». Parce que le logiciel écrit par la personne développeuse externe envoie une requête à votre API puis votre API « rappelle », en envoyant une requête à une *API externe* (probablement créée par la même personne développeuse).
Dans ce cas, vous pourriez vouloir documenter à quoi cette API externe devrait ressembler. Quel *chemin d'accès* elle devrait avoir, quel corps elle devrait attendre, quelle réponse elle devrait renvoyer, etc.
## Une application avec des callbacks { #an-app-with-callbacks }
Voyons tout cela avec un exemple.
Imaginez que vous développiez une application qui permet de créer des factures.
Ces factures auront un `id`, un `title` (facultatif), un `customer` et un `total`.
L’utilisateur de votre API (une personne développeuse externe) créera une facture dans votre API avec une requête POST.
Ensuite votre API va (imaginons) :
* Envoyer la facture à un client de la personne développeuse externe.
* Encaisser l’argent.
* Renvoyer une notification à l’utilisateur de l’API (la personne développeuse externe).
* Cela sera fait en envoyant une requête POST (depuis *votre API*) vers une *API externe* fournie par cette personne développeuse externe (c’est le « callback »).
## L’application **FastAPI** normale { #the-normal-fastapi-app }
Voyons d’abord à quoi ressemble l’application API normale avant d’ajouter le callback.
Elle aura un *chemin d'accès* qui recevra un corps `Invoice`, et un paramètre de requête `callback_url` qui contiendra l’URL pour le callback.
Cette partie est assez normale, la plupart du code vous est probablement déjà familier :
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Astuce
Le paramètre de requête `callback_url` utilise un type Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).
///
La seule nouveauté est `callbacks=invoices_callback_router.routes` comme argument du *décorateur de chemin d'accès*. Nous allons voir ce que c’est ensuite.
## Documenter le callback { #documenting-the-callback }
Le code réel du callback dépendra fortement de votre application API.
Et il variera probablement beaucoup d’une application à l’autre.
Cela pourrait être seulement une ou deux lignes de code, comme :
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Mais la partie la plus importante du callback est sans doute de vous assurer que l’utilisateur de votre API (la personne développeuse externe) implémente correctement l’*API externe*, conformément aux données que *votre API* va envoyer dans le corps de la requête du callback, etc.
Ainsi, ce que nous allons faire ensuite, c’est ajouter le code pour documenter à quoi cette *API externe* devrait ressembler pour recevoir le callback de *votre API*.
Cette documentation apparaîtra dans Swagger UI à `/docs` dans votre API, et permettra aux personnes développeuses externes de savoir comment construire l’*API externe*.
Cet exemple n’implémente pas le callback lui-même (qui pourrait être une simple ligne de code), uniquement la partie documentation.
/// tip | Astuce
Le callback réel n’est qu’une requête HTTP.
En implémentant vous-même le callback, vous pourriez utiliser quelque chose comme [HTTPX](https://www.python-httpx.org) ou [Requests](https://requests.readthedocs.io/).
///
## Écrire le code de documentation du callback { #write-the-callback-documentation-code }
Ce code ne sera pas exécuté dans votre application, nous en avons seulement besoin pour *documenter* à quoi devrait ressembler cette *API externe*.
Mais vous savez déjà comment créer facilement une documentation automatique pour une API avec **FastAPI**.
Nous allons donc utiliser ce même savoir pour documenter à quoi l’*API externe* devrait ressembler ... en créant le(s) *chemin(s) d'accès* que l’API externe devrait implémenter (ceux que votre API appellera).
/// tip | Astuce
Lorsque vous écrivez le code pour documenter un callback, il peut être utile d’imaginer que vous êtes cette *personne développeuse externe*. Et que vous implémentez actuellement l’*API externe*, pas *votre API*.
Adopter temporairement ce point de vue (celui de la *personne développeuse externe*) peut vous aider à trouver plus évident où placer les paramètres, le modèle Pydantic pour le corps, pour la réponse, etc., pour cette *API externe*.
///
### Créer un `APIRouter` de callback { #create-a-callback-apirouter }
Commencez par créer un nouveau `APIRouter` qui contiendra un ou plusieurs callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Créer le *chemin d'accès* du callback { #create-the-callback-path-operation }
Pour créer le *chemin d'accès* du callback, utilisez le même `APIRouter` que vous avez créé ci-dessus.
Il devrait ressembler exactement à un *chemin d'accès* FastAPI normal :
* Il devrait probablement déclarer le corps qu’il doit recevoir, par exemple `body: InvoiceEvent`.
* Et il pourrait aussi déclarer la réponse qu’il doit renvoyer, par exemple `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Il y a 2 principales différences par rapport à un *chemin d'accès* normal :
* Il n’a pas besoin d’avoir de code réel, car votre application n’appellera jamais ce code. Il sert uniquement à documenter l’*API externe*. La fonction peut donc simplement contenir `pass`.
* Le *chemin* peut contenir une [expression OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (voir plus bas) où il peut utiliser des variables avec des paramètres et des parties de la requête originale envoyée à *votre API*.
### L’expression du chemin de callback { #the-callback-path-expression }
Le *chemin* du callback peut contenir une [expression OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) qui peut inclure des parties de la requête originale envoyée à *votre API*.
Dans ce cas, c’est la `str` :
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Ainsi, si l’utilisateur de votre API (la personne développeuse externe) envoie une requête à *votre API* vers :
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
avec un corps JSON :
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
alors *votre API* traitera la facture et, à un moment ultérieur, enverra une requête de callback à `callback_url` (l’*API externe*) :
```
https://www.external.org/events/invoices/2expen51ve
```
avec un corps JSON contenant quelque chose comme :
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
et elle s’attendra à une réponse de cette *API externe* avec un corps JSON comme :
```JSON
{
"ok": true
}
```
/// tip | Astuce
Remarquez que l’URL de callback utilisée contient l’URL reçue en paramètre de requête dans `callback_url` (`https://www.external.org/events`) et aussi l’`id` de la facture à l’intérieur du corps JSON (`2expen51ve`).
///
### Ajouter le routeur de callback { #add-the-callback-router }
À ce stade, vous avez le(s) *chemin(s) d'accès de callback* nécessaire(s) (celui/ceux que la *personne développeuse externe* doit implémenter dans l’*API externe*) dans le routeur de callback que vous avez créé ci-dessus.
Utilisez maintenant le paramètre `callbacks` dans *le décorateur de chemin d'accès de votre API* pour passer l’attribut `.routes` (qui est en fait juste une `list` de routes/*chemins d'accès*) depuis ce routeur de callback :
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Astuce
Remarquez que vous ne passez pas le routeur lui-même (`invoices_callback_router`) à `callback=`, mais l’attribut `.routes`, comme dans `invoices_callback_router.routes`.
///
### Vérifier la documentation { #check-the-docs }
Vous pouvez maintenant démarrer votre application et aller sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez votre documentation incluant une section « Callbacks » pour votre *chemin d'accès* qui montre à quoi l’*API externe* devrait ressembler :
================================================
FILE: docs/fr/docs/advanced/openapi-webhooks.md
================================================
# Webhooks OpenAPI { #openapi-webhooks }
Il existe des cas où vous voulez informer les utilisateurs de votre API que votre application peut appeler leur application (en envoyant une requête) avec des données, généralement pour notifier un type d'événement.
Cela signifie qu'au lieu du processus habituel où vos utilisateurs envoient des requêtes à votre API, c'est votre API (ou votre application) qui peut envoyer des requêtes vers leur système (vers leur API, leur application).
On appelle généralement cela un webhook.
## Étapes des webhooks { #webhooks-steps }
Le processus consiste généralement à définir dans votre code le message que vous enverrez, c'est-à-dire le corps de la requête.
Vous définissez également, d'une manière ou d'une autre, à quels moments votre application enverra ces requêtes ou événements.
Et vos utilisateurs définissent aussi, d'une manière ou d'une autre (par exemple dans un tableau de bord Web), l'URL à laquelle votre application doit envoyer ces requêtes.
Toute la logique de gestion des URL des webhooks et le code qui envoie effectivement ces requêtes vous incombent. Vous l'implémentez comme vous le souhaitez dans votre propre code.
## Documenter des webhooks avec FastAPI et OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
Avec FastAPI, en utilisant OpenAPI, vous pouvez définir les noms de ces webhooks, les types d'opérations HTTP que votre application peut envoyer (par exemple `POST`, `PUT`, etc.) et les corps des requêtes que votre application enverra.
Cela peut grandement faciliter la tâche de vos utilisateurs pour implémenter leurs API afin de recevoir vos requêtes de webhook ; ils pourront même peut-être générer automatiquement une partie de leur propre code d'API.
/// info
Les webhooks sont disponibles dans OpenAPI 3.1.0 et versions ultérieures, pris en charge par FastAPI `0.99.0` et versions ultérieures.
///
## Créer une application avec des webhooks { #an-app-with-webhooks }
Lorsque vous créez une application FastAPI, il existe un attribut `webhooks` que vous pouvez utiliser pour définir des webhooks, de la même manière que vous définiriez des chemins d'accès, par exemple avec `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Les webhooks que vous définissez apparaîtront dans le schéma OpenAPI et dans l'interface de documentation automatique.
/// info
L'objet `app.webhooks` est en fait simplement un `APIRouter`, le même type que vous utiliseriez pour structurer votre application en plusieurs fichiers.
///
Notez qu'avec les webhooks, vous ne déclarez pas réellement un chemin (comme `/items/`), le texte que vous y passez est simplement un identifiant du webhook (le nom de l'événement). Par exemple, dans `@app.webhooks.post("new-subscription")`, le nom du webhook est `new-subscription`.
C'est parce qu'on s'attend à ce que vos utilisateurs définissent, par un autre moyen (par exemple un tableau de bord Web), le véritable chemin d'URL où ils souhaitent recevoir la requête de webhook.
### Consulter la documentation { #check-the-docs }
Vous pouvez maintenant démarrer votre application et aller sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez que votre documentation contient les chemins d'accès habituels et désormais aussi des webhooks :
================================================
FILE: docs/fr/docs/advanced/path-operation-advanced-configuration.md
================================================
# Configuration avancée des chemins d'accès { #path-operation-advanced-configuration }
## ID d’opération OpenAPI { #openapi-operationid }
/// warning | Alertes
Si vous n’êtes pas un « expert » d’OpenAPI, vous n’en avez probablement pas besoin.
///
Vous pouvez définir l’OpenAPI `operationId` à utiliser dans votre chemin d’accès avec le paramètre `operation_id`.
Vous devez vous assurer qu’il est unique pour chaque opération.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Utiliser le nom de la fonction de chemin d’accès comme operationId { #using-the-path-operation-function-name-as-the-operationid }
Si vous souhaitez utiliser les noms de fonction de vos API comme `operationId`, vous pouvez les parcourir tous et remplacer l’`operation_id` de chaque chemin d’accès en utilisant leur `APIRoute.name`.
Vous devez le faire après avoir ajouté tous vos chemins d’accès.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Astuce
Si vous appelez manuellement `app.openapi()`, vous devez mettre à jour les `operationId` avant cela.
///
/// warning | Alertes
Si vous faites cela, vous devez vous assurer que chacune de vos fonctions de chemin d’accès a un nom unique.
Même si elles se trouvent dans des modules différents (fichiers Python).
///
## Exclusion d’OpenAPI { #exclude-from-openapi }
Pour exclure un chemin d’accès du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre `include_in_schema` et définissez-le à `False` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Description avancée depuis la docstring { #advanced-description-from-docstring }
Vous pouvez limiter les lignes utilisées de la docstring d’une fonction de chemin d’accès pour OpenAPI.
L’ajout d’un `\f` (un caractère « form feed » échappé) amène **FastAPI** à tronquer la sortie utilisée pour OpenAPI à cet endroit.
Cela n’apparaîtra pas dans la documentation, mais d’autres outils (comme Sphinx) pourront utiliser le reste.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Réponses supplémentaires { #additional-responses }
Vous avez probablement vu comment déclarer le `response_model` et le `status_code` pour un chemin d’accès.
Cela définit les métadonnées sur la réponse principale d’un chemin d’accès.
Vous pouvez également déclarer des réponses supplémentaires avec leurs modèles, codes de statut, etc.
Il y a un chapitre entier dans la documentation à ce sujet, vous pouvez le lire dans [Réponses supplémentaires dans OpenAPI](additional-responses.md).
## OpenAPI supplémentaire { #openapi-extra }
Lorsque vous déclarez un chemin d’accès dans votre application, **FastAPI** génère automatiquement les métadonnées pertinentes à propos de ce chemin d’accès à inclure dans le schéma OpenAPI.
/// note | Détails techniques
Dans la spécification OpenAPI, cela s’appelle l’[objet Operation](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
Il contient toutes les informations sur le chemin d’accès et est utilisé pour générer la documentation automatique.
Il inclut les `tags`, `parameters`, `requestBody`, `responses`, etc.
Ce schéma OpenAPI spécifique à un chemin d’accès est normalement généré automatiquement par **FastAPI**, mais vous pouvez également l’étendre.
/// tip | Astuce
Ceci est un point d’extension de bas niveau.
Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d’utiliser [Réponses supplémentaires dans OpenAPI](additional-responses.md).
///
Vous pouvez étendre le schéma OpenAPI pour un chemin d’accès en utilisant le paramètre `openapi_extra`.
### Extensions OpenAPI { #openapi-extensions }
Cet `openapi_extra` peut être utile, par exemple, pour déclarer des [Extensions OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) :
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Si vous ouvrez la documentation automatique de l’API, votre extension apparaîtra en bas du chemin d’accès spécifique.
Et si vous consultez l’OpenAPI résultant (à `/openapi.json` dans votre API), vous verrez également votre extension comme partie du chemin d’accès spécifique :
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Personnaliser le schéma OpenAPI d’un chemin d’accès { #custom-openapi-path-operation-schema }
Le dictionnaire dans `openapi_extra` sera fusionné en profondeur avec le schéma OpenAPI généré automatiquement pour le chemin d’accès.
Ainsi, vous pouvez ajouter des données supplémentaires au schéma généré automatiquement.
Par exemple, vous pourriez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de FastAPI avec Pydantic, mais vous pourriez tout de même vouloir définir la requête dans le schéma OpenAPI.
Vous pourriez le faire avec `openapi_extra` :
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
Dans cet exemple, nous n’avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n’est même pas parsé en JSON, il est lu directement en tant que `bytes`, et la fonction `magic_data_reader()` serait chargée de l’analyser d’une manière ou d’une autre.
Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.
### Type de contenu OpenAPI personnalisé { #custom-openapi-content-type }
En utilisant cette même astuce, vous pourriez utiliser un modèle Pydantic pour définir le JSON Schema qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le chemin d’accès.
Et vous pourriez le faire même si le type de données dans la requête n’est pas du JSON.
Par exemple, dans cette application nous n’utilisons pas la fonctionnalité intégrée de FastAPI pour extraire le JSON Schema des modèles Pydantic ni la validation automatique pour le JSON. En fait, nous déclarons le type de contenu de la requête comme YAML, pas JSON :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Néanmoins, bien que nous n’utilisions pas la fonctionnalité intégrée par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le JSON Schema pour les données que nous souhaitons recevoir en YAML.
Ensuite, nous utilisons directement la requête et extrayons le corps en tant que `bytes`. Cela signifie que FastAPI n’essaiera même pas d’analyser le payload de la requête en JSON.
Ensuite, dans notre code, nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Astuce
Ici, nous réutilisons le même modèle Pydantic.
Mais de la même manière, nous aurions pu le valider autrement.
///
================================================
FILE: docs/fr/docs/advanced/response-change-status-code.md
================================================
# Réponse - Modifier le code d'état { #response-change-status-code }
Vous avez probablement déjà lu que vous pouvez définir un [Code d'état de la réponse](../tutorial/response-status-code.md) par défaut.
Mais dans certains cas, vous devez renvoyer un code d'état différent de celui par défaut.
## Cas d'utilisation { #use-case }
Par exemple, imaginez que vous vouliez renvoyer par défaut un code d'état HTTP « OK » `200`.
Mais si les données n'existent pas, vous voulez les créer et renvoyer un code d'état HTTP « CREATED » `201`.
Mais vous souhaitez toujours pouvoir filtrer et convertir les données que vous renvoyez avec un `response_model`.
Pour ces cas, vous pouvez utiliser un paramètre `Response`.
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre fonction de chemin d'accès (comme vous pouvez le faire pour les cookies et les en-têtes).
Vous pouvez ensuite définir le `status_code` dans cet objet de réponse *temporaire*.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Vous pouvez ensuite renvoyer n'importe quel objet nécessaire, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse *temporaire* pour extraire le code d'état (ainsi que les cookies et les en-têtes), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances et y définir le code d'état. Mais gardez à l'esprit que la dernière valeur définie prévaut.
================================================
FILE: docs/fr/docs/advanced/response-cookies.md
================================================
# Cookies de réponse { #response-cookies }
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre *fonction de chemin d'accès*.
Vous pouvez ensuite définir des cookies dans cet objet de réponse *temporaire*.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Vous pouvez ensuite renvoyer n'importe quel objet dont vous avez besoin, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse *temporaire* pour extraire les cookies (ainsi que les en-têtes et le code d'état), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances, et y définir des cookies (et des en-têtes).
## Renvoyer une `Response` directement { #return-a-response-directly }
Vous pouvez également créer des cookies en renvoyant une `Response` directement dans votre code.
Pour ce faire, vous pouvez créer une réponse comme décrit dans [Renvoyer une Response directement](response-directly.md).
Définissez ensuite des cookies dessus, puis renvoyez-la :
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Astuce
Gardez à l'esprit que si vous renvoyez une réponse directement au lieu d'utiliser le paramètre `Response`, FastAPI la renverra telle quelle.
Vous devez donc vous assurer que vos données sont du type correct. Par exemple, qu'elles sont compatibles avec JSON si vous renvoyez une `JSONResponse`.
Et également que vous n'envoyez pas de données qui auraient dû être filtrées par un `response_model`.
///
### En savoir plus { #more-info }
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` que `fastapi.responses` simplement pour votre commodité, en tant que développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
Et comme `Response` peut être utilisé fréquemment pour définir des en-têtes et des cookies, **FastAPI** la met également à disposition via `fastapi.Response`.
///
Pour voir tous les paramètres et options disponibles, consultez la [documentation de Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/fr/docs/advanced/response-directly.md
================================================
# Renvoyer directement une réponse { #return-a-response-directly }
Lorsque vous créez un *chemin d'accès* **FastAPI**, vous pouvez normalement renvoyer n'importe quelle donnée : un `dict`, une `list`, un modèle Pydantic, un modèle de base de données, etc.
Si vous déclarez un [Modèle de réponse](../tutorial/response-model.md), FastAPI l'utilise pour sérialiser les données en JSON, en utilisant Pydantic.
Si vous ne déclarez pas de modèle de réponse, FastAPI utilise le `jsonable_encoder` expliqué dans [Encodeur compatible JSON](../tutorial/encoder.md) et le place dans une `JSONResponse`.
Vous pouvez également créer directement une `JSONResponse` et la renvoyer.
/// tip | Astuce
Vous aurez normalement une bien meilleure performance en utilisant un [Modèle de réponse](../tutorial/response-model.md) qu'en renvoyant directement une `JSONResponse`, car de cette façon la sérialisation des données est effectuée par Pydantic, en Rust.
///
## Renvoyer une `Response` { #return-a-response }
Vous pouvez renvoyer une `Response` ou n'importe laquelle de ses sous-classes.
/// info
`JSONResponse` est elle-même une sous-classe de `Response`.
///
Et lorsque vous renvoyez une `Response`, **FastAPI** la transmet directement.
Il n'effectue aucune conversion de données avec les modèles Pydantic, il ne convertit pas le contenu en un autre type, etc.
Cela vous donne beaucoup de flexibilité. Vous pouvez renvoyer n'importe quel type de données, surcharger toute déclaration ou validation de données, etc.
Cela vous donne aussi beaucoup de responsabilité. Vous devez vous assurer que les données que vous renvoyez sont correctes, dans le bon format, qu'elles peuvent être sérialisées, etc.
## Utiliser le `jsonable_encoder` dans une `Response` { #using-the-jsonable-encoder-in-a-response }
Comme **FastAPI** n'apporte aucune modification à une `Response` que vous renvoyez, vous devez vous assurer que son contenu est prêt pour cela.
Par exemple, vous ne pouvez pas mettre un modèle Pydantic dans une `JSONResponse` sans d'abord le convertir en un `dict` avec tous les types de données (comme `datetime`, `UUID`, etc.) convertis en types compatibles JSON.
Pour ces cas, vous pouvez utiliser le `jsonable_encoder` pour convertir vos données avant de les passer à une réponse :
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Détails techniques
Vous pouvez aussi utiliser `from starlette.responses import JSONResponse`.
**FastAPI** fournit le même `starlette.responses` que `fastapi.responses` uniquement par commodité pour vous, développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
///
## Renvoyer une `Response` personnalisée { #returning-a-custom-response }
L'exemple ci-dessus montre toutes les parties dont vous avez besoin, mais il n'est pas encore très utile, car vous auriez pu renvoyer l'`item` directement, et **FastAPI** l'aurait placé dans une `JSONResponse` pour vous, en le convertissant en `dict`, etc. Tout cela par défaut.
Voyons maintenant comment vous pourriez utiliser cela pour renvoyer une réponse personnalisée.
Disons que vous voulez renvoyer une [réponse XML](https://en.wikipedia.org/wiki/XML).
Vous pouvez placer votre contenu XML dans une chaîne de caractères, le mettre dans une `Response` et le renvoyer :
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Comprendre le fonctionnement d'un Modèle de réponse { #how-a-response-model-works }
Lorsque vous déclarez un [Modèle de réponse - Type de retour](../tutorial/response-model.md) dans un chemin d'accès, **FastAPI** l'utilise pour sérialiser les données en JSON, en utilisant Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Comme cela se passe côté Rust, la performance sera bien meilleure que si cela était fait avec le Python classique et la classe `JSONResponse`.
Lorsque vous utilisez un `response_model` ou un type de retour, FastAPI n'utilise ni le `jsonable_encoder` pour convertir les données (ce qui serait plus lent) ni la classe `JSONResponse`.
À la place, il prend les octets JSON générés avec Pydantic en utilisant le modèle de réponse (ou le type de retour) et renvoie directement une `Response` avec le type de média approprié pour JSON (`application/json`).
## Notes { #notes }
Lorsque vous renvoyez une `Response` directement, ses données ne sont pas validées, converties (sérialisées), ni documentées automatiquement.
Mais vous pouvez toujours les documenter comme décrit dans [Réponses supplémentaires dans OpenAPI](additional-responses.md).
Vous pouvez voir dans les sections suivantes comment utiliser/déclarer ces `Response` personnalisées tout en conservant la conversion automatique des données, la documentation, etc.
================================================
FILE: docs/fr/docs/advanced/response-headers.md
================================================
# En-têtes de réponse { #response-headers }
## Utiliser un paramètre `Response` { #use-a-response-parameter }
Vous pouvez déclarer un paramètre de type `Response` dans votre fonction de chemin d'accès (comme vous pouvez le faire pour les cookies).
Vous pouvez ensuite définir des en-têtes dans cet objet de réponse temporaire.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Ensuite, vous pouvez renvoyer n'importe quel objet dont vous avez besoin, comme d'habitude (un `dict`, un modèle de base de données, etc.).
Et si vous avez déclaré un `response_model`, il sera toujours utilisé pour filtrer et convertir l'objet que vous avez renvoyé.
**FastAPI** utilisera cette réponse temporaire pour extraire les en-têtes (ainsi que les cookies et le code de statut), et les placera dans la réponse finale qui contient la valeur que vous avez renvoyée, filtrée par tout `response_model`.
Vous pouvez également déclarer le paramètre `Response` dans des dépendances, et y définir des en-têtes (et des cookies).
## Renvoyer une `Response` directement { #return-a-response-directly }
Vous pouvez également ajouter des en-têtes lorsque vous renvoyez une `Response` directement.
Créez une réponse comme décrit dans [Renvoyer une Response directement](response-directly.md) et passez les en-têtes comme paramètre supplémentaire :
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre commodité, en tant que développeur. Mais la plupart des réponses disponibles viennent directement de Starlette.
Et comme `Response` peut être utilisée fréquemment pour définir des en-têtes et des cookies, **FastAPI** la fournit aussi via `fastapi.Response`.
///
## En-têtes personnalisés { #custom-headers }
Gardez à l'esprit que des en-têtes propriétaires personnalisés peuvent être ajoutés [en utilisant le préfixe `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Mais si vous avez des en-têtes personnalisés que vous voulez qu'un client dans un navigateur puisse voir, vous devez les ajouter à vos configurations CORS (en savoir plus dans [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), en utilisant le paramètre `expose_headers` documenté dans [la documentation CORS de Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/fr/docs/advanced/security/http-basic-auth.md
================================================
# Authentification HTTP Basic { #http-basic-auth }
Pour les cas les plus simples, vous pouvez utiliser l'authentification HTTP Basic.
Avec l'authentification HTTP Basic, l'application attend un en-tête contenant un nom d'utilisateur et un mot de passe.
Si elle ne le reçoit pas, elle renvoie une erreur HTTP 401 « Unauthorized ».
Et elle renvoie un en-tête `WWW-Authenticate` avec la valeur `Basic`, et un paramètre optionnel `realm`.
Cela indique au navigateur d'afficher l'invite intégrée pour saisir un nom d'utilisateur et un mot de passe.
Ensuite, lorsque vous saisissez ce nom d'utilisateur et ce mot de passe, le navigateur les envoie automatiquement dans l'en-tête.
## Authentification HTTP Basic simple { #simple-http-basic-auth }
- Importer `HTTPBasic` et `HTTPBasicCredentials`.
- Créer un « schéma de sécurité » en utilisant `HTTPBasic`.
- Utiliser ce `security` avec une dépendance dans votre chemin d'accès.
- Cela renvoie un objet de type `HTTPBasicCredentials` :
- Il contient le `username` et le `password` envoyés.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Lorsque vous essayez d'ouvrir l'URL pour la première fois (ou cliquez sur le bouton « Execute » dans les documents) le navigateur vous demandera votre nom d'utilisateur et votre mot de passe :
## Vérifier le nom d'utilisateur { #check-the-username }
Voici un exemple plus complet.
Utilisez une dépendance pour vérifier si le nom d'utilisateur et le mot de passe sont corrects.
Pour cela, utilisez le module standard Python [`secrets`](https://docs.python.org/3/library/secrets.html) pour vérifier le nom d'utilisateur et le mot de passe.
`secrets.compare_digest()` doit recevoir des `bytes` ou une `str` ne contenant que des caractères ASCII (ceux de l'anglais), ce qui signifie qu'elle ne fonctionnerait pas avec des caractères comme `á`, comme dans `Sebastián`.
Pour gérer cela, nous convertissons d'abord `username` et `password` en `bytes` en les encodant en UTF-8.
Nous pouvons ensuite utiliser `secrets.compare_digest()` pour vérifier que `credentials.username` est « stanleyjobson » et que `credentials.password` est « swordfish ».
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Cela serait équivalent à :
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Renvoyer une erreur
...
```
Mais en utilisant `secrets.compare_digest()`, cela sera sécurisé contre un type d'attaques appelé « attaques par chronométrage ».
### Attaques par chronométrage { #timing-attacks }
Mais qu'est-ce qu'une « attaque par chronométrage » ?
Imaginons que des attaquants essaient de deviner le nom d'utilisateur et le mot de passe.
Ils envoient alors une requête avec un nom d'utilisateur `johndoe` et un mot de passe `love123`.
Le code Python de votre application serait alors équivalent à quelque chose comme :
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Mais au moment où Python compare le premier `j` de `johndoe` au premier `s` de `stanleyjobson`, il retournera `False`, car il sait déjà que ces deux chaînes ne sont pas identiques, en se disant qu'« il n'est pas nécessaire de gaspiller plus de calcul pour comparer le reste des lettres ». Et votre application dira « Nom d'utilisateur ou mot de passe incorrect ».
Mais ensuite, les attaquants essaient avec le nom d'utilisateur `stanleyjobsox` et le mot de passe `love123`.
Et le code de votre application fait quelque chose comme :
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python devra comparer tout `stanleyjobso` dans `stanleyjobsox` et `stanleyjobson` avant de réaliser que les deux chaînes ne sont pas identiques. Cela prendra donc quelques microsecondes supplémentaires pour répondre « Nom d'utilisateur ou mot de passe incorrect ».
#### Le temps de réponse aide les attaquants { #the-time-to-answer-helps-the-attackers }
À ce stade, en remarquant que le serveur a mis quelques microsecondes de plus à envoyer la réponse « Nom d'utilisateur ou mot de passe incorrect », les attaquants sauront qu'ils ont trouvé quelque chose de juste : certaines des premières lettres étaient correctes.
Ils peuvent alors réessayer en sachant que c'est probablement quelque chose de plus proche de `stanleyjobsox` que de `johndoe`.
#### Une attaque « professionnelle » { #a-professional-attack }
Bien sûr, les attaquants n'essaieraient pas tout cela à la main ; ils écriraient un programme pour le faire, avec éventuellement des milliers ou des millions de tests par seconde. Ils obtiendraient une lettre correcte supplémentaire à la fois.
Ce faisant, en quelques minutes ou heures, les attaquants devineraient le nom d'utilisateur et le mot de passe corrects, avec « l'aide » de notre application, simplement en se basant sur le temps de réponse.
#### Corrigez-le avec `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Mais dans notre code nous utilisons justement `secrets.compare_digest()`.
En bref, il faudra le même temps pour comparer `stanleyjobsox` à `stanleyjobson` que pour comparer `johndoe` à `stanleyjobson`. Il en va de même pour le mot de passe.
Ainsi, en utilisant `secrets.compare_digest()` dans le code de votre application, votre application sera protégée contre toute cette gamme d'attaques de sécurité.
### Renvoyer l'erreur { #return-the-error }
Après avoir détecté que les identifiants sont incorrects, renvoyez une `HTTPException` avec un code d'état 401 (le même que lorsque aucun identifiant n'est fourni) et ajoutez l'en-tête `WWW-Authenticate` pour que le navigateur affiche à nouveau l'invite de connexion :
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/fr/docs/advanced/security/index.md
================================================
# Sécurité avancée { #advanced-security }
## Fonctionnalités supplémentaires { #additional-features }
Il existe des fonctionnalités supplémentaires pour gérer la sécurité en plus de celles couvertes dans le [Tutoriel - Guide utilisateur : Sécurité](../../tutorial/security/index.md).
/// tip | Astuce
Les sections suivantes **ne sont pas nécessairement « advanced »**.
Et il est possible que, pour votre cas d’utilisation, la solution se trouve dans l’une d’entre elles.
///
## Lire d’abord le tutoriel { #read-the-tutorial-first }
Les sections suivantes partent du principe que vous avez déjà lu le [Tutoriel - Guide utilisateur : Sécurité](../../tutorial/security/index.md) principal.
Elles s’appuient toutes sur les mêmes concepts, mais permettent des fonctionnalités supplémentaires.
================================================
FILE: docs/fr/docs/advanced/security/oauth2-scopes.md
================================================
# Scopes OAuth2 { #oauth2-scopes }
Vous pouvez utiliser des scopes OAuth2 directement avec **FastAPI**, ils sont intégrés pour fonctionner de manière transparente.
Cela vous permettrait d’avoir un système d’autorisations plus fin, conforme au standard OAuth2, intégré à votre application OpenAPI (et à la documentation de l’API).
OAuth2 avec scopes est le mécanisme utilisé par de nombreux grands fournisseurs d’authentification, comme Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Ils l’utilisent pour fournir des permissions spécifiques aux utilisateurs et aux applications.
Chaque fois que vous « log in with » Facebook, Google, GitHub, Microsoft, X (Twitter), cette application utilise OAuth2 avec scopes.
Dans cette section, vous verrez comment gérer l’authentification et l’autorisation avec le même OAuth2 avec scopes dans votre application **FastAPI**.
/// warning | Alertes
C’est une section plus ou moins avancée. Si vous débutez, vous pouvez la passer.
Vous n’avez pas nécessairement besoin des scopes OAuth2, et vous pouvez gérer l’authentification et l’autorisation comme vous le souhaitez.
Mais OAuth2 avec scopes peut s’intégrer élégamment à votre API (avec OpenAPI) et à votre documentation d’API.
Néanmoins, c’est toujours à vous de faire appliquer ces scopes, ou toute autre exigence de sécurité/autorisation, selon vos besoins, dans votre code.
Dans de nombreux cas, OAuth2 avec scopes peut être excessif.
Mais si vous savez que vous en avez besoin, ou si vous êtes curieux, continuez à lire.
///
## Scopes OAuth2 et OpenAPI { #oauth2-scopes-and-openapi }
La spécification OAuth2 définit des « scopes » comme une liste de chaînes séparées par des espaces.
Le contenu de chacune de ces chaînes peut avoir n’importe quel format, mais ne doit pas contenir d’espaces.
Ces scopes représentent des « permissions ».
Dans OpenAPI (par ex. la documentation de l’API), vous pouvez définir des « schémas de sécurité ».
Lorsqu’un de ces schémas de sécurité utilise OAuth2, vous pouvez aussi déclarer et utiliser des scopes.
Chaque « scope » est juste une chaîne (sans espaces).
Ils sont généralement utilisés pour déclarer des permissions de sécurité spécifiques, par exemple :
* `users:read` ou `users:write` sont des exemples courants.
* `instagram_basic` est utilisé par Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` est utilisé par Google.
/// info
Dans OAuth2, un « scope » est simplement une chaîne qui déclare une permission spécifique requise.
Peu importe s’il contient d’autres caractères comme `:` ou si c’est une URL.
Ces détails dépendent de l’implémentation.
Pour OAuth2, ce ne sont que des chaînes.
///
## Vue d’ensemble { #global-view }
Voyons d’abord rapidement les parties qui changent par rapport aux exemples du **Tutoriel - Guide utilisateur** pour [OAuth2 avec mot de passe (et hachage), Bearer avec jetons JWT](../../tutorial/security/oauth2-jwt.md). Cette fois, en utilisant des scopes OAuth2 :
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Passons maintenant en revue ces changements étape par étape.
## Déclarer le schéma de sécurité OAuth2 { #oauth2-security-scheme }
Le premier changement est que nous déclarons maintenant le schéma de sécurité OAuth2 avec deux scopes disponibles, `me` et `items`.
Le paramètre `scopes` reçoit un `dict` avec chaque scope en clé et la description en valeur :
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Comme nous déclarons maintenant ces scopes, ils apparaîtront dans la documentation de l’API lorsque vous vous authentifiez/autorisez.
Et vous pourrez sélectionner à quels scopes vous souhaitez accorder l’accès : `me` et `items`.
C’est le même mécanisme utilisé lorsque vous donnez des permissions en vous connectant avec Facebook, Google, GitHub, etc. :
## Jeton JWT avec scopes { #jwt-token-with-scopes }
Modifiez maintenant le *chemin d’accès* du jeton pour renvoyer les scopes demandés.
Nous utilisons toujours le même `OAuth2PasswordRequestForm`. Il inclut une propriété `scopes` avec une `list` de `str`, contenant chaque scope reçu dans la requête.
Et nous renvoyons les scopes comme partie du jeton JWT.
/// danger | Danger
Pour simplifier, ici nous ajoutons directement au jeton les scopes reçus.
Mais dans votre application, pour la sécurité, vous devez vous assurer de n’ajouter que les scopes que l’utilisateur est réellement autorisé à avoir, ou ceux que vous avez prédéfinis.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Déclarer des scopes dans les chemins d’accès et les dépendances { #declare-scopes-in-path-operations-and-dependencies }
Nous déclarons maintenant que le *chemin d’accès* `/users/me/items/` nécessite le scope `items`.
Pour cela, nous importons et utilisons `Security` depuis `fastapi`.
Vous pouvez utiliser `Security` pour déclarer des dépendances (comme `Depends`), mais `Security` reçoit aussi un paramètre `scopes` avec une liste de scopes (chaînes).
Dans ce cas, nous passons une fonction de dépendance `get_current_active_user` à `Security` (de la même manière que nous le ferions avec `Depends`).
Mais nous passons aussi une `list` de scopes, ici avec un seul scope : `items` (il pourrait y en avoir plus).
Et la fonction de dépendance `get_current_active_user` peut également déclarer des sous-dépendances, non seulement avec `Depends` mais aussi avec `Security`. En déclarant sa propre fonction de sous-dépendance (`get_current_user`), et davantage d’exigences de scopes.
Dans ce cas, elle nécessite le scope `me` (elle pourrait en exiger plusieurs).
/// note | Remarque
Vous n’avez pas nécessairement besoin d’ajouter des scopes différents à différents endroits.
Nous le faisons ici pour montrer comment **FastAPI** gère des scopes déclarés à différents niveaux.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Détails techniques
`Security` est en réalité une sous-classe de `Depends`, et elle n’a qu’un paramètre supplémentaire que nous verrons plus tard.
Mais en utilisant `Security` au lieu de `Depends`, **FastAPI** saura qu’il peut déclarer des scopes de sécurité, les utiliser en interne et documenter l’API avec OpenAPI.
Cependant, lorsque vous importez `Query`, `Path`, `Depends`, `Security` et d’autres depuis `fastapi`, ce sont en fait des fonctions qui renvoient des classes spéciales.
///
## Utiliser `SecurityScopes` { #use-securityscopes }
Mettez maintenant à jour la dépendance `get_current_user`.
C’est celle utilisée par les dépendances ci-dessus.
C’est ici que nous utilisons le même schéma OAuth2 que nous avons créé auparavant, en le déclarant comme dépendance : `oauth2_scheme`.
Comme cette fonction de dépendance n’a pas elle-même d’exigences de scope, nous pouvons utiliser `Depends` avec `oauth2_scheme`, nous n’avons pas à utiliser `Security` quand nous n’avons pas besoin de spécifier des scopes de sécurité.
Nous déclarons également un paramètre spécial de type `SecurityScopes`, importé de `fastapi.security`.
Cette classe `SecurityScopes` est similaire à `Request` (`Request` servait à obtenir directement l’objet requête).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Utiliser les `scopes` { #use-the-scopes }
Le paramètre `security_scopes` sera de type `SecurityScopes`.
Il aura une propriété `scopes` avec une liste contenant tous les scopes requis par lui-même et par toutes les dépendances qui l’utilisent comme sous-dépendance. Cela signifie, tous les « dépendants » ... cela peut paraître déroutant, c’est expliqué à nouveau plus bas.
L’objet `security_scopes` (de classe `SecurityScopes`) fournit aussi un attribut `scope_str` avec une chaîne unique, contenant ces scopes séparés par des espaces (nous allons l’utiliser).
Nous créons une `HTTPException` que nous pouvons réutiliser (`raise`) plus tard à plusieurs endroits.
Dans cette exception, nous incluons les scopes requis (le cas échéant) sous forme de chaîne séparée par des espaces (en utilisant `scope_str`). Nous plaçons cette chaîne contenant les scopes dans l’en-tête `WWW-Authenticate` (cela fait partie de la spécification).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Vérifier le `username` et la structure des données { #verify-the-username-and-data-shape }
Nous vérifions que nous obtenons un `username`, et extrayons les scopes.
Nous validons ensuite ces données avec le modèle Pydantic (en capturant l’exception `ValidationError`), et si nous obtenons une erreur lors de la lecture du jeton JWT ou de la validation des données avec Pydantic, nous levons la `HTTPException` que nous avons créée auparavant.
Pour cela, nous mettons à jour le modèle Pydantic `TokenData` avec une nouvelle propriété `scopes`.
En validant les données avec Pydantic, nous pouvons nous assurer que nous avons, par exemple, exactement une `list` de `str` pour les scopes et un `str` pour le `username`.
Au lieu, par exemple, d’un `dict`, ou autre chose, ce qui pourrait casser l’application plus tard et constituer un risque de sécurité.
Nous vérifions également que nous avons un utilisateur avec ce nom d’utilisateur, et sinon, nous levons la même exception que précédemment.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Vérifier les `scopes` { #verify-the-scopes }
Nous vérifions maintenant que tous les scopes requis, par cette dépendance et tous les dépendants (y compris les *chemins d’accès*), sont inclus dans les scopes fournis dans le jeton reçu, sinon nous levons une `HTTPException`.
Pour cela, nous utilisons `security_scopes.scopes`, qui contient une `list` avec tous ces scopes en `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Arbre de dépendances et scopes { #dependency-tree-and-scopes }
Revoyons encore cet arbre de dépendances et les scopes.
Comme la dépendance `get_current_active_user` a une sous-dépendance `get_current_user`, le scope « me » déclaré dans `get_current_active_user` sera inclus dans la liste des scopes requis dans `security_scopes.scopes` passé à `get_current_user`.
Le *chemin d’accès* lui-même déclare également un scope, « items », il sera donc aussi présent dans la liste `security_scopes.scopes` passée à `get_current_user`.
Voici à quoi ressemble la hiérarchie des dépendances et des scopes :
* Le *chemin d’accès* `read_own_items` a :
* Des scopes requis `["items"]` avec la dépendance :
* `get_current_active_user` :
* La fonction de dépendance `get_current_active_user` a :
* Des scopes requis `["me"]` avec la dépendance :
* `get_current_user` :
* La fonction de dépendance `get_current_user` a :
* Aucun scope requis par elle-même.
* Une dépendance utilisant `oauth2_scheme`.
* Un paramètre `security_scopes` de type `SecurityScopes` :
* Ce paramètre `security_scopes` a une propriété `scopes` avec une `list` contenant tous les scopes déclarés ci-dessus, donc :
* `security_scopes.scopes` contiendra `["me", "items"]` pour le *chemin d’accès* `read_own_items`.
* `security_scopes.scopes` contiendra `["me"]` pour le *chemin d’accès* `read_users_me`, car il est déclaré dans la dépendance `get_current_active_user`.
* `security_scopes.scopes` contiendra `[]` (rien) pour le *chemin d’accès* `read_system_status`, car il n’a déclaré aucun `Security` avec des `scopes`, et sa dépendance, `get_current_user`, ne déclare pas non plus de `scopes`.
/// tip | Astuce
L’élément important et « magique » ici est que `get_current_user` aura une liste différente de `scopes` à vérifier pour chaque *chemin d’accès*.
Tout dépend des `scopes` déclarés dans chaque *chemin d’accès* et chaque dépendance dans l’arbre de dépendances pour ce *chemin d’accès* spécifique.
///
## Détails supplémentaires sur `SecurityScopes` { #more-details-about-securityscopes }
Vous pouvez utiliser `SecurityScopes` à n’importe quel endroit, et à de multiples endroits, il n’a pas besoin d’être dans la dépendance « root ».
Il aura toujours les scopes de sécurité déclarés dans les dépendances `Security` actuelles et tous les dépendants pour **ce** *chemin d’accès* spécifique et **cet** arbre de dépendances spécifique.
Comme `SecurityScopes` contient tous les scopes déclarés par les dépendants, vous pouvez l’utiliser pour vérifier qu’un jeton possède les scopes requis dans une fonction de dépendance centrale, puis déclarer des exigences de scopes différentes dans différents *chemins d’accès*.
Elles seront vérifiées indépendamment pour chaque *chemin d’accès*.
## Tester { #check-it }
Si vous ouvrez la documentation de l’API, vous pouvez vous authentifier et spécifier quels scopes vous voulez autoriser.
Si vous ne sélectionnez aucun scope, vous serez « authenticated », mais lorsque vous essayerez d’accéder à `/users/me/` ou `/users/me/items/`, vous obtiendrez une erreur indiquant que vous n’avez pas suffisamment de permissions. Vous pourrez toujours accéder à `/status/`.
Et si vous sélectionnez le scope `me` mais pas le scope `items`, vous pourrez accéder à `/users/me/` mais pas à `/users/me/items/`.
C’est ce qui arriverait à une application tierce qui tenterait d’accéder à l’un de ces *chemins d’accès* avec un jeton fourni par un utilisateur, selon le nombre de permissions que l’utilisateur a accordées à l’application.
## À propos des intégrations tierces { #about-third-party-integrations }
Dans cet exemple, nous utilisons le flux OAuth2 « password ».
C’est approprié lorsque nous nous connectons à notre propre application, probablement avec notre propre frontend.
Parce que nous pouvons lui faire confiance pour recevoir le `username` et le `password`, puisque nous le contrôlons.
Mais si vous construisez une application OAuth2 à laquelle d’autres se connecteraient (c.-à-d., si vous construisez un fournisseur d’authentification équivalent à Facebook, Google, GitHub, etc.), vous devez utiliser l’un des autres flux.
Le plus courant est le flux implicite.
Le plus sûr est le flux « code », mais il est plus complexe à implémenter car il nécessite plus d’étapes. Comme il est plus complexe, de nombreux fournisseurs finissent par recommander le flux implicite.
/// note | Remarque
Il est courant que chaque fournisseur d’authentification nomme ses flux différemment, pour en faire une partie de sa marque.
Mais au final, ils implémentent le même standard OAuth2.
///
**FastAPI** inclut des utilitaires pour tous ces flux d’authentification OAuth2 dans `fastapi.security.oauth2`.
## `Security` dans les dépendances du décorateur `dependencies` { #security-in-decorator-dependencies }
De la même manière que vous pouvez définir une `list` de `Depends` dans le paramètre `dependencies` du décorateur (comme expliqué dans [Dépendances dans les décorateurs de chemins d’accès](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), vous pouvez aussi utiliser `Security` avec des `scopes` à cet endroit.
================================================
FILE: docs/fr/docs/advanced/settings.md
================================================
# Paramètres et variables d'environnement { #settings-and-environment-variables }
Dans de nombreux cas, votre application peut avoir besoin de paramètres ou de configurations externes, par exemple des clés secrètes, des identifiants de base de données, des identifiants pour des services d'e-mail, etc.
La plupart de ces paramètres sont variables (peuvent changer), comme les URL de base de données. Et beaucoup peuvent être sensibles, comme les secrets.
C'est pourquoi il est courant de les fournir via des variables d'environnement lues par l'application.
/// tip | Astuce
Pour comprendre les variables d'environnement, vous pouvez lire [Variables d'environnement](../environment-variables.md).
///
## Types et validation { #types-and-validation }
Ces variables d'environnement ne gèrent que des chaînes de texte, car elles sont externes à Python et doivent être compatibles avec d'autres programmes et le reste du système (et même avec différents systèmes d'exploitation, comme Linux, Windows, macOS).
Cela signifie que toute valeur lue en Python depuis une variable d'environnement sera une `str`, et toute conversion vers un autre type ou toute validation doit être effectuée dans le code.
## Pydantic `Settings` { #pydantic-settings }
Heureusement, Pydantic fournit un excellent utilitaire pour gérer ces paramètres provenant des variables d'environnement avec [Pydantic : gestion des paramètres](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Installer `pydantic-settings` { #install-pydantic-settings }
D'abord, vous devez créer votre [environnement virtuel](../virtual-environments.md), l'activer, puis installer le paquet `pydantic-settings` :
### Créer l'objet `Settings` { #create-the-settings-object }
Importez `BaseSettings` depuis Pydantic et créez une sous-classe, comme pour un modèle Pydantic.
De la même manière qu'avec les modèles Pydantic, vous déclarez des attributs de classe avec des annotations de type, et éventuellement des valeurs par défaut.
Vous pouvez utiliser toutes les mêmes fonctionnalités et outils de validation que pour les modèles Pydantic, comme différents types de données et des validations supplémentaires avec `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Astuce
Si vous voulez quelque chose à copier-coller rapidement, n'utilisez pas cet exemple, utilisez le dernier ci-dessous.
///
Ensuite, lorsque vous créez une instance de cette classe `Settings` (dans ce cas, l'objet `settings`), Pydantic lira les variables d'environnement de manière insensible à la casse, donc une variable en majuscules `APP_NAME` sera tout de même lue pour l'attribut `app_name`.
Il convertira ensuite et validera les données. Ainsi, lorsque vous utilisez cet objet `settings`, vous aurez des données des types que vous avez déclarés (par exemple, `items_per_user` sera un `int`).
### Utiliser `settings` { #use-the-settings }
Vous pouvez ensuite utiliser le nouvel objet `settings` dans votre application :
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Exécuter le serveur { #run-the-server }
Ensuite, vous exécutez le serveur en passant les configurations comme variables d'environnement ; par exemple, vous pouvez définir un `ADMIN_EMAIL` et `APP_NAME` avec :
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Astuce
Pour définir plusieurs variables d'environnement pour une seule commande, séparez-les simplement par un espace et placez-les toutes avant la commande.
///
Ainsi, le paramètre `admin_email` sera défini sur « deadpool@example.com ».
Le `app_name` sera « ChimichangApp ».
Et `items_per_user` conservera sa valeur par défaut de `50`.
## Paramètres dans un autre module { #settings-in-another-module }
Vous pouvez placer ces paramètres dans un autre module comme vous l'avez vu dans [Applications plus grandes - Plusieurs fichiers](../tutorial/bigger-applications.md).
Par exemple, vous pourriez avoir un fichier `config.py` avec :
{* ../../docs_src/settings/app01_py310/config.py *}
Puis l'utiliser dans un fichier `main.py` :
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Astuce
Vous aurez également besoin d'un fichier `__init__.py` comme vous l'avez vu dans [Applications plus grandes - Plusieurs fichiers](../tutorial/bigger-applications.md).
///
## Paramètres dans une dépendance { #settings-in-a-dependency }
Dans certains cas, il peut être utile de fournir les paramètres via une dépendance, au lieu d'avoir un objet global `settings` utilisé partout.
Cela peut être particulièrement utile pendant les tests, car il est très facile de surcharger une dépendance avec vos propres paramètres personnalisés.
### Le fichier de configuration { #the-config-file }
En repartant de l'exemple précédent, votre fichier `config.py` pourrait ressembler à :
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Notez que maintenant, nous ne créons pas d'instance par défaut `settings = Settings()`.
### Le fichier principal de l'application { #the-main-app-file }
Nous créons maintenant une dépendance qui renvoie un nouveau `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Astuce
Nous parlerons de `@lru_cache` dans un instant.
Pour l'instant, vous pouvez supposer que `get_settings()` est une fonction normale.
///
Nous pouvons ensuite l'exiger depuis la fonction de chemin d'accès comme dépendance et l'utiliser où nous en avons besoin.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Paramètres et tests { #settings-and-testing }
Il devient alors très simple de fournir un autre objet de paramètres pendant les tests en créant une surcharge de dépendance pour `get_settings` :
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
Dans la surcharge de dépendance, nous définissons une nouvelle valeur pour `admin_email` lors de la création du nouvel objet `Settings`, puis nous renvoyons ce nouvel objet.
Nous pouvons ensuite tester qu'il est bien utilisé.
## Lire un fichier `.env` { #reading-a-env-file }
Si vous avez de nombreux paramètres susceptibles de beaucoup changer, peut-être selon les environnements, il peut être utile de les placer dans un fichier, puis de les lire comme s'il s'agissait de variables d'environnement.
Cette pratique est suffisamment courante pour avoir un nom ; ces variables d'environnement sont fréquemment placées dans un fichier `.env`, et le fichier est appelé un « dotenv ».
/// tip | Astuce
Un fichier commençant par un point (`.`) est un fichier caché dans les systèmes de type Unix, comme Linux et macOS.
Mais un fichier dotenv n'a pas forcément exactement ce nom de fichier.
///
Pydantic prend en charge la lecture depuis ce type de fichiers en utilisant une bibliothèque externe. Vous pouvez en lire davantage ici : [Pydantic Settings : prise en charge de Dotenv (.env)](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Astuce
Pour que cela fonctionne, vous devez exécuter `pip install python-dotenv`.
///
### Le fichier `.env` { #the-env-file }
Vous pouvez avoir un fichier `.env` avec :
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Lire les paramètres depuis `.env` { #read-settings-from-env }
Puis mettre à jour votre `config.py` avec :
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Astuce
L'attribut `model_config` est utilisé uniquement pour la configuration Pydantic. Vous pouvez en lire davantage ici : [Pydantic : Concepts : Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Ici, nous définissons la configuration `env_file` à l'intérieur de votre classe Pydantic `Settings` et lui attribuons le nom du fichier dotenv que nous voulons utiliser.
### Créer `Settings` une seule fois avec `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Lire un fichier depuis le disque est normalement une opération coûteuse (lente), vous voudrez donc probablement le faire une seule fois puis réutiliser le même objet de paramètres, au lieu de le lire à chaque requête.
Mais chaque fois que nous faisons :
```Python
Settings()
```
un nouvel objet `Settings` serait créé, et à sa création il lirait à nouveau le fichier `.env`.
Si la fonction de dépendance était simplement :
```Python
def get_settings():
return Settings()
```
nous créerions cet objet pour chaque requête, et nous lirions le fichier `.env` pour chaque requête. ⚠️
Mais comme nous utilisons le décorateur `@lru_cache` au-dessus, l'objet `Settings` sera créé une seule fois, la première fois qu'il est appelé. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Ensuite, pour tout appel ultérieur de `get_settings()` dans les dépendances pour les requêtes suivantes, au lieu d'exécuter le code interne de `get_settings()` et de créer un nouvel objet `Settings`, il renverra le même objet que celui retourné au premier appel, encore et encore.
#### Détails techniques de `lru_cache` { #lru-cache-technical-details }
`@lru_cache` modifie la fonction qu'il décore pour renvoyer la même valeur que celle qui a été retournée la première fois, au lieu de la recalculer en exécutant le code de la fonction à chaque fois.
Ainsi, la fonction située en dessous sera exécutée une fois pour chaque combinaison d'arguments. Ensuite, les valeurs renvoyées par chacune de ces combinaisons d'arguments seront réutilisées à chaque fois que la fonction sera appelée avec exactement la même combinaison d'arguments.
Par exemple, si vous avez une fonction :
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
votre programme pourrait s'exécuter comme ceci :
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
Dans le cas de notre dépendance `get_settings()`, la fonction ne prend même aucun argument, elle renvoie donc toujours la même valeur.
De cette façon, elle se comporte presque comme s'il s'agissait simplement d'une variable globale. Mais comme elle utilise une fonction de dépendance, nous pouvons alors la surcharger facilement pour les tests.
`@lru_cache` fait partie de `functools` qui fait partie de la bibliothèque standard de Python, vous pouvez en lire davantage dans la [documentation Python pour `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Récapitulatif { #recap }
Vous pouvez utiliser Pydantic Settings pour gérer les paramètres ou configurations de votre application, avec toute la puissance des modèles Pydantic.
* En utilisant une dépendance, vous pouvez simplifier les tests.
* Vous pouvez utiliser des fichiers `.env`.
* Utiliser `@lru_cache` vous permet d'éviter de relire le fichier dotenv à chaque requête, tout en vous permettant de le surcharger pendant les tests.
================================================
FILE: docs/fr/docs/advanced/stream-data.md
================================================
# Diffuser des données { #stream-data }
Si vous voulez diffuser des données pouvant être structurées en JSON, vous devez [Diffuser des JSON Lines](../tutorial/stream-json-lines.md).
Mais si vous voulez diffuser des données binaires pures ou des chaînes, voici comment procéder.
/// info
Ajouté dans FastAPI 0.134.0.
///
## Cas d'utilisation { #use-cases }
Vous pouvez l'utiliser si vous souhaitez diffuser des chaînes pures, par exemple directement depuis la sortie d'un service d'**IA LLM**.
Vous pouvez également l'utiliser pour diffuser de gros fichiers binaires, en envoyant chaque bloc de données au fur et à mesure de la lecture, sans tout charger en mémoire d'un coup.
Vous pouvez aussi diffuser de la **vidéo** ou de l'**audio** de cette manière ; cela peut même être généré au fil du traitement et de l'envoi.
## Utiliser une `StreamingResponse` avec `yield` { #a-streamingresponse-with-yield }
Si vous déclarez un `response_class=StreamingResponse` dans votre *fonction de chemin d'accès*, vous pouvez utiliser `yield` pour envoyer chaque bloc de données à son tour.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI transmettra chaque bloc de données à la `StreamingResponse` tel quel ; il n'essaiera pas de le convertir en JSON ni autre chose similaire.
### Fonctions de chemin d'accès non async { #non-async-path-operation-functions }
Vous pouvez également utiliser des fonctions `def` classiques (sans `async`), et utiliser `yield` de la même manière.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Sans annotation { #no-annotation }
Vous n'avez pas vraiment besoin de déclarer l'annotation de type de retour pour diffuser des données binaires.
Comme FastAPI n'essaiera pas de convertir les données en JSON avec Pydantic ni de les sérialiser, dans ce cas l'annotation de type ne sert qu'à votre éditeur et à vos outils ; elle ne sera pas utilisée par FastAPI.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Cela signifie aussi qu'avec `StreamingResponse` vous avez la liberté — et la responsabilité — de produire et d'encoder les octets de données exactement comme vous avez besoin de les envoyer, indépendamment des annotations de type. 🤓
### Diffuser des bytes { #stream-bytes }
L'un des principaux cas d'usage consiste à diffuser des `bytes` au lieu de chaînes ; vous pouvez bien sûr le faire.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Créer une `PNGStreamingResponse` personnalisée { #a-custom-pngstreamingresponse }
Dans les exemples ci-dessus, les octets de données étaient diffusés, mais la réponse n'avait pas d'en-tête `Content-Type`, le client ne savait donc pas quel type de données il recevait.
Vous pouvez créer une sous-classe personnalisée de `StreamingResponse` qui définit l'en-tête `Content-Type` sur le type de données que vous diffusez.
Par exemple, vous pouvez créer une `PNGStreamingResponse` qui définit l'en-tête `Content-Type` à `image/png` en utilisant l'attribut `media_type` :
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Vous pouvez ensuite utiliser cette nouvelle classe dans `response_class=PNGStreamingResponse` dans votre *fonction de chemin d'accès* :
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Simuler un fichier { #simulate-a-file }
Dans cet exemple, nous simulons un fichier avec `io.BytesIO`, qui est un objet de type fichier résidant uniquement en mémoire, mais qui permet d'utiliser la même interface.
Par exemple, nous pouvons itérer dessus pour en consommer le contenu, comme nous le ferions avec un fichier.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Détails techniques
Les deux autres variables, `image_base64` et `binary_image`, correspondent à une image encodée en Base64, puis convertie en bytes, afin de la passer à `io.BytesIO`.
C'est uniquement pour que tout tienne dans le même fichier pour cet exemple, et que vous puissiez le copier et l'exécuter tel quel. 🥚
///
En utilisant un bloc `with`, nous nous assurons que l'objet de type fichier est fermé après l'exécution de la fonction génératrice (la fonction avec `yield`). Donc, après la fin de l'envoi de la réponse.
Ce ne serait pas si important dans cet exemple précis, car il s'agit d'un faux fichier en mémoire (avec `io.BytesIO`), mais avec un vrai fichier, il est important de s'assurer qu'il est fermé une fois le travail terminé.
### Gérer les fichiers et async { #files-and-async }
Dans la plupart des cas, les objets de type fichier ne sont pas compatibles avec `async` et `await` par défaut.
Par exemple, ils n'ont pas de `await file.read()`, ni de `async for chunk in file`.
Et dans de nombreux cas, leur lecture serait une opération bloquante (pouvant bloquer la boucle d'événements), car ils sont lus depuis le disque ou le réseau.
/// info
L'exemple ci-dessus est en réalité une exception, car l'objet `io.BytesIO` est déjà en mémoire ; sa lecture ne bloquera donc rien.
Mais dans de nombreux cas, la lecture d'un fichier ou d'un objet de type fichier bloquera.
///
Pour éviter de bloquer la boucle d'événements, vous pouvez simplement déclarer la *fonction de chemin d'accès* avec un `def` classique au lieu de `async def`. Ainsi, FastAPI l'exécutera dans un worker de pool de threads, afin d'éviter de bloquer la boucle principale.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Astuce
Si vous devez appeler du code bloquant depuis une fonction async, ou une fonction async depuis une fonction bloquante, vous pouvez utiliser [Asyncer](https://asyncer.tiangolo.com), une bibliothèque sœur de FastAPI.
///
### `yield from` { #yield-from }
Lorsque vous itérez sur quelque chose, comme un objet de type fichier, et que vous faites un `yield` pour chaque élément, vous pouvez aussi utiliser `yield from` pour émettre chaque élément directement et éviter la boucle `for`.
Ce n'est pas spécifique à FastAPI, c'est simplement Python, mais c'est une astuce utile à connaître. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/fr/docs/advanced/strict-content-type.md
================================================
# Vérifier strictement le Content-Type { #strict-content-type-checking }
Par défaut, FastAPI applique une vérification stricte de l’en-tête `Content-Type` pour les corps de requêtes JSON ; cela signifie que les requêtes JSON doivent inclure un en-tête `Content-Type` valide (par ex. `application/json`) pour que le corps soit analysé comme JSON.
## Risque CSRF { #csrf-risk }
Ce comportement par défaut offre une protection contre une catégorie d’attaques de Cross-Site Request Forgery (CSRF) dans un scénario très spécifique.
Ces attaques exploitent le fait que les navigateurs permettent à des scripts d’envoyer des requêtes sans effectuer de pré-vérification CORS (preflight) lorsqu’ils :
* n’ont pas d’en-tête `Content-Type` (par ex. en utilisant `fetch()` avec un corps `Blob`)
* et n’envoient aucune information d’authentification.
Ce type d’attaque est surtout pertinent lorsque :
* l’application s’exécute localement (par ex. sur `localhost`) ou sur un réseau interne
* et l’application n’a aucun mécanisme d’authentification, elle part du principe que toute requête provenant du même réseau est fiable.
## Exemple d’attaque { #example-attack }
Imaginez que vous mettiez au point un moyen d’exécuter un agent IA local.
Il expose une API à l’adresse
```
http://localhost:8000/v1/agents/multivac
```
Il y a aussi un frontend à l’adresse
```
http://localhost:8000
```
/// tip | Astuce
Notez qu’ils ont le même hôte.
///
Vous pouvez alors, via le frontend, amener l’agent IA à effectuer des actions en votre nom.
Comme il s’exécute localement, et non sur l’Internet ouvert, vous décidez de ne mettre en place aucun mécanisme d’authentification, en vous fiant simplement à l’accès au réseau local.
Un de vos utilisateurs pourrait alors l’installer et l’exécuter localement.
Il pourrait ensuite ouvrir un site malveillant, par exemple quelque chose comme
```
https://evilhackers.example.com
```
Et ce site malveillant enverrait des requêtes en utilisant `fetch()` avec un corps `Blob` vers l’API locale à l’adresse
```
http://localhost:8000/v1/agents/multivac
```
Même si l’hôte du site malveillant et celui de l’application locale sont différents, le navigateur ne déclenchera pas de pré-vérification CORS (preflight) parce que :
* Elle s’exécute sans aucune authentification, il n’y a pas à envoyer d’informations d’authentification.
* Le navigateur pense qu’il n’envoie pas de JSON (faute d’en-tête `Content-Type`).
Le site malveillant pourrait alors amener l’agent IA local à envoyer des messages en colère à l’ancien patron de l’utilisateur ... ou pire. 😅
## Internet ouvert { #open-internet }
Si votre application est exposée sur l’Internet ouvert, vous ne « ferez pas confiance au réseau » et ne laisserez pas n’importe qui envoyer des requêtes privilégiées sans authentification.
Des attaquants pourraient simplement exécuter un script pour envoyer des requêtes à votre API, sans interaction avec le navigateur ; vous sécurisez donc probablement déjà tout endpoint privilégié.
Dans ce cas, cette attaque / ce risque ne vous concerne pas.
Ce risque et cette attaque sont surtout pertinents lorsque l’application s’exécute sur le réseau local et que c’est la seule protection supposée.
## Autoriser les requêtes sans Content-Type { #allowing-requests-without-content-type }
Si vous devez prendre en charge des clients qui n’envoient pas d’en-tête `Content-Type`, vous pouvez désactiver la vérification stricte en définissant `strict_content_type=False` :
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
Avec ce paramètre, les requêtes sans en-tête `Content-Type` verront leur corps analysé comme JSON, ce qui correspond au comportement des anciennes versions de FastAPI.
/// info
Ce comportement et cette configuration ont été ajoutés dans FastAPI 0.132.0.
///
================================================
FILE: docs/fr/docs/advanced/sub-applications.md
================================================
# Sous-applications - Montage { #sub-applications-mounts }
Si vous avez besoin de deux applications FastAPI indépendantes, avec leur propre OpenAPI et leurs propres interfaces de la documentation, vous pouvez avoir une application principale et « monter » une (ou plusieurs) sous‑application(s).
## Monter une application **FastAPI** { #mounting-a-fastapi-application }
« Monter » signifie ajouter une application entièrement « indépendante » à un chemin spécifique, qui se chargera ensuite de tout gérer sous ce chemin, avec les _chemins d'accès_ déclarés dans cette sous‑application.
### Application de premier niveau { #top-level-application }
Créez d'abord l'application **FastAPI** principale (de premier niveau) et ses *chemins d'accès* :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sous-application { #sub-application }
Ensuite, créez votre sous‑application et ses *chemins d'accès*.
Cette sous‑application est simplement une autre application FastAPI standard, mais c'est celle qui sera « montée » :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Monter la sous-application { #mount-the-sub-application }
Dans votre application de premier niveau, `app`, montez la sous‑application, `subapi`.
Dans ce cas, elle sera montée au chemin `/subapi` :
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Vérifier la documentation API automatique { #check-the-automatic-api-docs }
Exécutez maintenant la commande `fastapi` :
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Et ouvrez la documentation à [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez la documentation API automatique pour l'application principale, n'incluant que ses propres _chemins d'accès_ :
Ensuite, ouvrez la documentation de la sous‑application à [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Vous verrez la documentation API automatique pour la sous‑application, n'incluant que ses propres _chemins d'accès_, tous sous le préfixe de sous‑chemin correct `/subapi` :
Si vous essayez d'interagir avec l'une ou l'autre des deux interfaces, elles fonctionneront correctement, car le navigateur pourra communiquer avec chaque application ou sous‑application spécifique.
### Détails techniques : `root_path` { #technical-details-root-path }
Lorsque vous montez une sous‑application comme ci‑dessus, FastAPI se charge de communiquer le chemin de montage à la sous‑application au moyen d'un mécanisme de la spécification ASGI appelé `root_path`.
De cette manière, la sous‑application saura utiliser ce préfixe de chemin pour l'interface de documentation.
La sous‑application peut également avoir ses propres sous‑applications montées et tout fonctionnera correctement, car FastAPI gère automatiquement tous ces `root_path`.
Vous en apprendrez davantage sur `root_path` et sur la façon de l'utiliser explicitement dans la section [Derrière un proxy](behind-a-proxy.md).
================================================
FILE: docs/fr/docs/advanced/templates.md
================================================
# Templates { #templates }
Vous pouvez utiliser n'importe quel moteur de templates avec **FastAPI**.
Un choix courant est Jinja2, le même que celui utilisé par Flask et d'autres outils.
Il existe des utilitaires pour le configurer facilement que vous pouvez utiliser directement dans votre application **FastAPI** (fournis par Starlette).
## Installer les dépendances { #install-dependencies }
Vous devez créer un [environnement virtuel](../virtual-environments.md), l'activer, puis installer `jinja2` :
/// info
Il existe d'autres formats et outils pour définir et installer des dépendances de paquets.
///
### Créer le code **FastAPI** { #create-the-fastapi-code }
* Créez un répertoire `app` et entrez dedans.
* Créez un fichier vide `__init__.py`.
* Créez 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Maintenant, dans le même répertoire de projet, créez un fichier `Dockerfile` avec :
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Démarrer à partir de l'image de base Python officielle.
2. Définir le répertoire de travail courant sur `/code`.
C'est là que nous placerons le fichier `requirements.txt` et le répertoire `app`.
3. Copier le fichier des dépendances vers le répertoire `/code`.
Copier **uniquement** le fichier des dépendances en premier, pas le reste du code.
Comme ce fichier **ne change pas souvent**, Docker le détectera et utilisera le **cache** pour cette étape, ce qui activera le cache pour l'étape suivante aussi.
4. Installer les dépendances listées dans le fichier des dépendances.
L'option `--no-cache-dir` indique à `pip` de ne pas enregistrer localement les paquets téléchargés, car cela ne sert que si `pip` devait être relancé pour installer les mêmes paquets, mais ce n'est pas le cas lorsque l'on travaille avec des conteneurs.
/// note | Remarque
Le `--no-cache-dir` concerne uniquement `pip`, cela n'a rien à voir avec Docker ou les conteneurs.
///
L'option `--upgrade` indique à `pip` de mettre à niveau les paquets s'ils sont déjà installés.
Comme l'étape précédente de copie du fichier peut être détectée par le **cache Docker**, cette étape **utilisera également le cache Docker** lorsqu'il est disponible.
L'utilisation du cache à cette étape vous **fera gagner** beaucoup de **temps** lors de la reconstruction de l'image encore et encore pendant le développement, au lieu de **télécharger et installer** toutes les dépendances **à chaque fois**.
5. Copier le répertoire `./app` dans le répertoire `/code`.
Comme cela contient tout le code qui est ce qui **change le plus fréquemment**, le **cache** Docker ne sera pas facilement utilisé pour cette étape ou pour les **étapes suivantes**.
Il est donc important de placer cela **vers la fin** du `Dockerfile`, pour optimiser les temps de construction de l'image de conteneur.
6. Définir la **commande** pour utiliser `fastapi run`, qui utilise Uvicorn sous le capot.
`CMD` prend une liste de chaînes, chacune de ces chaînes correspond à ce que vous taperiez en ligne de commande séparé par des espaces.
Cette commande sera exécutée à partir du **répertoire de travail courant**, le même répertoire `/code` que vous avez défini plus haut avec `WORKDIR /code`.
/// tip | Astuce
Passez en revue ce que fait chaque ligne en cliquant sur chaque bulle numérotée dans le code. 👆
///
/// warning | Alertes
Vous devez vous assurer d'utiliser **toujours** la **forme exec** de l'instruction `CMD`, comme expliqué ci-dessous.
///
#### Utiliser `CMD` - Forme Exec { #use-cmd-exec-form }
L'instruction Docker [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) peut être écrite sous deux formes :
✅ Forme **Exec** :
```Dockerfile
# ✅ À faire
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Forme **Shell** :
```Dockerfile
# ⛔️ À ne pas faire
CMD fastapi run app/main.py --port 80
```
Assurez-vous d'utiliser toujours la forme **exec** pour garantir que FastAPI peut s'arrêter proprement et que les [événements de cycle de vie](../advanced/events.md) sont déclenchés.
Vous pouvez en lire davantage dans la [documentation Docker sur les formes shell et exec](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Cela peut être très visible lors de l'utilisation de `docker compose`. Voir cette section de la FAQ Docker Compose pour plus de détails techniques : [Pourquoi mes services mettent-ils 10 secondes à se recréer ou à s'arrêter ?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Structure du répertoire { #directory-structure }
Vous devriez maintenant avoir une structure de répertoire comme :
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Derrière un proxy de terminaison TLS { #behind-a-tls-termination-proxy }
Si vous exécutez votre conteneur derrière un proxy de terminaison TLS (load balancer) comme Nginx ou Traefik, ajoutez l'option `--proxy-headers`, cela indiquera à Uvicorn (via la CLI FastAPI) de faire confiance aux en-têtes envoyés par ce proxy lui indiquant que l'application s'exécute derrière HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Cache Docker { #docker-cache }
Il y a une astuce importante dans ce `Dockerfile`, nous copions d'abord **le fichier des dépendances seul**, pas le reste du code. Laissez-moi vous expliquer pourquoi.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker et d'autres outils **construisent** ces images de conteneur **de manière incrémentale**, en ajoutant **une couche au-dessus de l'autre**, en commençant par le haut du `Dockerfile` et en ajoutant tous les fichiers créés par chacune des instructions du `Dockerfile`.
Docker et des outils similaires utilisent également un **cache interne** lors de la construction de l'image : si un fichier n'a pas changé depuis la dernière construction de l'image de conteneur, alors il va **réutiliser la même couche** créée la dernière fois, au lieu de recopier le fichier et créer une nouvelle couche à partir de zéro.
Éviter simplement la copie des fichiers n'améliore pas nécessairement les choses de manière significative, mais comme il a utilisé le cache pour cette étape, il peut **utiliser le cache pour l'étape suivante**. Par exemple, il peut utiliser le cache pour l'instruction qui installe les dépendances avec :
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Le fichier des dépendances **ne changera pas fréquemment**. Ainsi, en copiant uniquement ce fichier, Docker pourra **utiliser le cache** pour cette étape.
Et ensuite, Docker pourra **utiliser le cache pour l'étape suivante** qui télécharge et installe ces dépendances. Et c'est là que nous **gagnons beaucoup de temps**. ✨ ... et évitons l'ennui en attendant. 😪😆
Télécharger et installer les dépendances de paquets **peut prendre des minutes**, mais utiliser le **cache** ne **prendra que quelques secondes** au plus.
Et comme vous reconstruirez l'image de conteneur encore et encore pendant le développement pour vérifier que vos modifications de code fonctionnent, cela vous fera gagner beaucoup de temps cumulé.
Ensuite, vers la fin du `Dockerfile`, nous copions tout le code. Comme c'est ce qui **change le plus fréquemment**, nous le plaçons vers la fin, car presque toujours, tout ce qui suit cette étape ne pourra pas utiliser le cache.
```Dockerfile
COPY ./app /code/app
```
### Construire l'image Docker { #build-the-docker-image }
Maintenant que tous les fichiers sont en place, construisons l'image de conteneur.
* Allez dans le répertoire du projet (là où se trouve votre `Dockerfile`, contenant votre répertoire `app`).
* Construisez votre image FastAPI :
/// tip | Astuce
Remarquez le `.` à la fin, équivalent à `./`, il indique à Docker le répertoire à utiliser pour construire l'image de conteneur.
Dans ce cas, c'est le même répertoire courant (`.`).
///
### Démarrer le conteneur Docker { #start-the-docker-container }
* Exécutez un conteneur basé sur votre image :
## Vérifier { #check-it }
Vous devriez pouvoir le vérifier via l'URL de votre conteneur Docker, par exemple : [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) ou [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (ou équivalent, en utilisant votre hôte Docker).
Vous verrez quelque chose comme :
```JSON
{"item_id": 5, "q": "somequery"}
```
## Documentation interactive de l'API { #interactive-api-docs }
Vous pouvez maintenant aller sur [http://192.168.99.100/docs](http://192.168.99.100/docs) ou [http://127.0.0.1/docs](http://127.0.0.1/docs) (ou équivalent, en utilisant votre hôte Docker).
Vous verrez la documentation interactive automatique de l'API (fournie par [Swagger UI](https://github.com/swagger-api/swagger-ui)) :

## Documentation alternative de l'API { #alternative-api-docs }
Et vous pouvez aussi aller sur [http://192.168.99.100/redoc](http://192.168.99.100/redoc) ou [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (ou équivalent, en utilisant votre hôte Docker).
Vous verrez la documentation automatique alternative (fournie par [ReDoc](https://github.com/Rebilly/ReDoc)) :

## Construire une image Docker avec un FastAPI mono-fichier { #build-a-docker-image-with-a-single-file-fastapi }
Si votre FastAPI est un seul fichier, par exemple `main.py` sans répertoire `./app`, votre structure de fichiers pourrait ressembler à ceci :
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Vous n'auriez alors qu'à changer les chemins correspondants pour copier le fichier dans le `Dockerfile` :
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copier le fichier `main.py` directement dans le répertoire `/code` (sans répertoire `./app`).
2. Utiliser `fastapi run` pour servir votre application dans le fichier unique `main.py`.
Lorsque vous passez le fichier à `fastapi run`, il détectera automatiquement qu'il s'agit d'un fichier unique et non d'un package et saura comment l'importer et servir votre application FastAPI. 😎
## Concepts de déploiement { #deployment-concepts }
Parlons à nouveau de certains des mêmes [Concepts de déploiement](concepts.md) en termes de conteneurs.
Les conteneurs sont principalement un outil pour simplifier le processus de **construction et de déploiement** d'une application, mais ils n'imposent pas une approche particulière pour gérer ces **concepts de déploiement**, et il existe plusieurs stratégies possibles.
La **bonne nouvelle**, c'est qu'avec chaque stratégie différente, il existe un moyen de couvrir tous les concepts de déploiement. 🎉
Passons en revue ces **concepts de déploiement** en termes de conteneurs :
* HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables au démarrage
## HTTPS { #https }
Si l'on se concentre uniquement sur l'**image de conteneur** pour une application FastAPI (et plus tard sur le **conteneur** en cours d'exécution), HTTPS serait normalement géré **à l'extérieur** par un autre outil.
Cela pourrait être un autre conteneur, par exemple avec [Traefik](https://traefik.io/), gérant **HTTPS** et l'acquisition **automatique** des **certificats**.
/// tip | Astuce
Traefik s'intègre avec Docker, Kubernetes, et d'autres, donc il est très facile de configurer HTTPS pour vos conteneurs avec lui.
///
Alternativement, HTTPS pourrait être géré par un fournisseur cloud comme l'un de leurs services (tout en exécutant l'application dans un conteneur).
## Exécution au démarrage et redémarrages { #running-on-startup-and-restarts }
Il y a normalement un autre outil chargé de **démarrer et exécuter** votre conteneur.
Cela pourrait être **Docker** directement, **Docker Compose**, **Kubernetes**, un **service cloud**, etc.
Dans la plupart (ou toutes) des situations, il existe une option simple pour activer l'exécution du conteneur au démarrage et activer les redémarrages en cas d'échec. Par exemple, dans Docker, c'est l'option de ligne de commande `--restart`.
Sans utiliser de conteneurs, faire en sorte que les applications s'exécutent au démarrage et avec redémarrages peut être fastidieux et difficile. Mais en **travaillant avec des conteneurs**, dans la plupart des cas, cette fonctionnalité est incluse par défaut. ✨
## Réplication - Nombre de processus { #replication-number-of-processes }
Si vous avez un cluster de machines avec **Kubernetes**, Docker Swarm Mode, Nomad, ou un autre système complexe similaire pour gérer des conteneurs distribués sur plusieurs machines, alors vous voudrez probablement **gérer la réplication** au **niveau du cluster** plutôt que d'utiliser un **gestionnaire de processus** (comme Uvicorn avec workers) dans chaque conteneur.
L'un de ces systèmes de gestion de conteneurs distribués comme Kubernetes dispose normalement d'une manière intégrée de gérer la **réplication des conteneurs** tout en supportant l'**équilibrage de charge** des requêtes entrantes. Le tout au **niveau du cluster**.
Dans ces cas, vous voudrez probablement construire une **image Docker à partir de zéro** comme [expliqué ci-dessus](#dockerfile), en installant vos dépendances et en exécutant **un seul processus Uvicorn** au lieu d'utiliser plusieurs workers Uvicorn.
### Équilibreur de charge { #load-balancer }
Lors de l'utilisation de conteneurs, vous aurez normalement un composant **à l'écoute sur le port principal**. Cela pourrait être un autre conteneur qui est également un **proxy de terminaison TLS** pour gérer **HTTPS** ou un outil similaire.
Comme ce composant prend la **charge** des requêtes et la distribue entre les workers de façon (espérons-le) **équilibrée**, on l'appelle également communément un **équilibreur de charge**.
/// tip | Astuce
Le même composant de **proxy de terminaison TLS** utilisé pour HTTPS sera probablement aussi un **équilibreur de charge**.
///
Et en travaillant avec des conteneurs, le même système que vous utilisez pour les démarrer et les gérer dispose déjà d'outils internes pour transmettre la **communication réseau** (par ex. les requêtes HTTP) depuis cet **équilibreur de charge** (qui peut aussi être un **proxy de terminaison TLS**) vers le ou les conteneurs avec votre application.
### Un équilibreur de charge - Plusieurs conteneurs worker { #one-load-balancer-multiple-worker-containers }
Lorsque vous travaillez avec **Kubernetes** ou des systèmes de gestion de conteneurs distribués similaires, l'utilisation de leurs mécanismes réseau internes permet au **seul équilibreur de charge** à l'écoute sur le **port** principal de transmettre la communication (les requêtes) vers potentiellement **plusieurs conteneurs** exécutant votre application.
Chacun de ces conteneurs exécutant votre application aura normalement **un seul processus** (par ex. un processus Uvicorn exécutant votre application FastAPI). Ils seront tous des **conteneurs identiques**, exécutant la même chose, mais chacun avec son propre processus, sa mémoire, etc. De cette façon, vous profiterez de la **parallélisation** sur **différents cœurs** du CPU, voire sur **différentes machines**.
Et le système de conteneurs distribués avec l'**équilibreur de charge** **distribuera les requêtes** à chacun des conteneurs exécutant votre application **à tour de rôle**. Ainsi, chaque requête pourrait être traitée par l'un des multiples **conteneurs répliqués** exécutant votre application.
Et normalement cet **équilibreur de charge** pourra gérer des requêtes qui vont vers *d'autres* applications dans votre cluster (par ex. vers un autre domaine, ou sous un autre préfixe de chemin d'URL), et transmettra cette communication aux bons conteneurs pour *cette autre* application s'exécutant dans votre cluster.
### Un processus par conteneur { #one-process-per-container }
Dans ce type de scénario, vous voudrez probablement avoir **un seul processus (Uvicorn) par conteneur**, puisque vous gérez déjà la réplication au niveau du cluster.
Donc, dans ce cas, vous **ne voudrez pas** avoir plusieurs workers dans le conteneur, par exemple avec l'option de ligne de commande `--workers`. Vous voudrez avoir **un seul processus Uvicorn** par conteneur (mais probablement plusieurs conteneurs).
Avoir un autre gestionnaire de processus à l'intérieur du conteneur (comme ce serait le cas avec plusieurs workers) n'ajouterait que de la **complexité inutile** que vous gérez très probablement déjà avec votre système de cluster.
### Conteneurs avec plusieurs processus et cas particuliers { #containers-with-multiple-processes-and-special-cases }
Bien sûr, il existe des **cas particuliers** où vous pourriez vouloir avoir **un conteneur** avec plusieurs **processus worker Uvicorn** à l'intérieur.
Dans ces cas, vous pouvez utiliser l'option de ligne de commande `--workers` pour définir le nombre de workers que vous souhaitez exécuter :
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Ici, nous utilisons l'option de ligne de commande `--workers` pour définir le nombre de workers à 4.
Voici quelques exemples où cela pourrait avoir du sens :
#### Une application simple { #a-simple-app }
Vous pourriez vouloir un gestionnaire de processus dans le conteneur si votre application est **suffisamment simple** pour s'exécuter sur un **seul serveur**, pas un cluster.
#### Docker Compose { #docker-compose }
Vous pourriez déployer sur un **seul serveur** (pas un cluster) avec **Docker Compose**, donc vous n'auriez pas un moyen simple de gérer la réplication des conteneurs (avec Docker Compose) tout en préservant le réseau partagé et l'**équilibrage de charge**.
Vous pourriez alors vouloir avoir **un seul conteneur** avec un **gestionnaire de processus** qui démarre **plusieurs processus worker** à l'intérieur.
---
L'idée principale est que **rien** de tout cela ne sont des **règles gravées dans la pierre** que vous devez suivre aveuglément. Vous pouvez utiliser ces idées pour **évaluer votre propre cas d'usage** et décider de la meilleure approche pour votre système, en vérifiant comment gérer les concepts suivants :
* Sécurité - HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables au démarrage
## Mémoire { #memory }
Si vous exécutez **un seul processus par conteneur**, vous aurez une quantité de mémoire consommée plus ou moins bien définie, stable et limitée par chacun de ces conteneurs (plus d'un s'ils sont répliqués).
Vous pouvez alors définir ces mêmes limites et exigences de mémoire dans vos configurations pour votre système de gestion de conteneurs (par exemple dans **Kubernetes**). De cette façon, il pourra **répliquer les conteneurs** sur les **machines disponibles** en tenant compte de la quantité de mémoire dont ils ont besoin et de la quantité disponible sur les machines du cluster.
Si votre application est **simple**, cela ne sera probablement **pas un problème**, et vous n'aurez peut-être pas besoin de spécifier des limites de mémoire strictes. Mais si vous **utilisez beaucoup de mémoire** (par exemple avec des modèles de **machine learning**), vous devez vérifier combien de mémoire vous consommez et ajuster le **nombre de conteneurs** qui s'exécutent sur **chaque machine** (et peut-être ajouter plus de machines à votre cluster).
Si vous exécutez **plusieurs processus par conteneur**, vous devez vous assurer que le nombre de processus démarrés ne **consomme pas plus de mémoire** que ce qui est disponible.
## Étapes préalables au démarrage et conteneurs { #previous-steps-before-starting-and-containers }
Si vous utilisez des conteneurs (par ex. Docker, Kubernetes), alors il existe deux approches principales que vous pouvez utiliser.
### Plusieurs conteneurs { #multiple-containers }
Si vous avez **plusieurs conteneurs**, probablement chacun exécutant un **seul processus** (par exemple, dans un cluster **Kubernetes**), alors vous voudrez probablement avoir un **conteneur séparé** effectuant le travail des **étapes préalables** dans un seul conteneur, exécutant un seul processus, **avant** d'exécuter les conteneurs worker répliqués.
/// info
Si vous utilisez Kubernetes, ce sera probablement un [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Si, dans votre cas d'usage, il n'y a pas de problème à exécuter ces étapes préalables **plusieurs fois en parallèle** (par exemple si vous n'exécutez pas de migrations de base de données, mais vérifiez simplement si la base de données est prête), alors vous pourriez aussi simplement les mettre dans chaque conteneur juste avant de démarrer le processus principal.
### Un seul conteneur { #single-container }
Si vous avez une configuration simple, avec **un seul conteneur** qui démarre ensuite plusieurs **processus worker** (ou un seul processus aussi), vous pouvez alors exécuter ces étapes préalables dans le même conteneur, juste avant de démarrer le processus avec l'application.
### Image Docker de base { #base-docker-image }
Il existait une image Docker officielle FastAPI : [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Mais elle est désormais dépréciée. ⛔️
Vous ne devriez probablement **pas** utiliser cette image Docker de base (ni aucune autre similaire).
Si vous utilisez **Kubernetes** (ou autres) et que vous définissez déjà la **réplication** au niveau du cluster, avec plusieurs **conteneurs**. Dans ces cas, il est préférable de **construire une image à partir de zéro** comme décrit ci-dessus : [Construire une image Docker pour FastAPI](#build-a-docker-image-for-fastapi).
Et si vous devez avoir plusieurs workers, vous pouvez simplement utiliser l'option de ligne de commande `--workers`.
/// note | Détails techniques
L'image Docker a été créée à une époque où Uvicorn ne supportait pas la gestion et le redémarrage des workers morts, il fallait donc utiliser Gunicorn avec Uvicorn, ce qui ajoutait pas mal de complexité, uniquement pour que Gunicorn gère et redémarre les processus worker Uvicorn.
Mais maintenant qu'Uvicorn (et la commande `fastapi`) supporte l'usage de `--workers`, il n'y a plus de raison d'utiliser une image Docker de base au lieu de construire la vôtre (c'est à peu près la même quantité de code 😅).
///
## Déployer l'image de conteneur { #deploy-the-container-image }
Après avoir une image de conteneur (Docker), il existe plusieurs façons de la déployer.
Par exemple :
* Avec **Docker Compose** sur un seul serveur
* Avec un cluster **Kubernetes**
* Avec un cluster Docker Swarm Mode
* Avec un autre outil comme Nomad
* Avec un service cloud qui prend votre image de conteneur et la déploie
## Image Docker avec `uv` { #docker-image-with-uv }
Si vous utilisez [uv](https://github.com/astral-sh/uv) pour installer et gérer votre projet, vous pouvez suivre leur [guide Docker pour uv](https://docs.astral.sh/uv/guides/integration/docker/).
## Récapitulatif { #recap }
Avec les systèmes de conteneurs (par ex. avec **Docker** et **Kubernetes**), il devient assez simple de gérer tous les **concepts de déploiement** :
* HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes préalables au démarrage
Dans la plupart des cas, vous ne voudrez probablement pas utiliser d'image de base, et au contraire **construire une image de conteneur à partir de zéro** basée sur l'image Docker Python officielle.
En prenant soin de l'**ordre** des instructions dans le `Dockerfile` et du **cache Docker**, vous pouvez **minimiser les temps de construction**, maximiser votre productivité (et éviter l'ennui). 😎
================================================
FILE: docs/fr/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Vous pouvez déployer votre application FastAPI sur [FastAPI Cloud](https://fastapicloud.com) avec une **seule commande**, allez vous inscrire sur la liste d’attente si ce n’est pas déjà fait. 🚀
## Se connecter { #login }
Vous devez vous assurer que vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste d’attente 😉).
Connectez-vous ensuite :
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Déployer { #deploy }
Déployez maintenant votre application, avec une **seule commande** :
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
C’est tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
## À propos de FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** est développé par le même auteur et la même équipe à l’origine de **FastAPI**.
Cela simplifie le processus de **création**, de **déploiement** et **d’accès** à une API avec un effort minimal.
Cela apporte la même **expérience développeur** que pour créer des applications avec FastAPI au **déploiement** dans le cloud. 🎉
Cela prend également en charge la plupart des éléments nécessaires lors du déploiement d’une application, notamment :
* HTTPS
* Réplication, avec mise à l’échelle automatique basée sur les requêtes
* etc.
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
## Déployer sur d’autres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur n’importe quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour déployer des applications FastAPI avec eux. 🤓
## Déployer votre propre serveur { #deploy-your-own-server }
Je vous expliquerai également plus loin dans ce guide de **Déploiement** tous les détails, afin que vous compreniez ce qui se passe, ce qui doit être fait, et comment déployer des applications FastAPI par vous-même, y compris sur vos propres serveurs. 🤓
================================================
FILE: docs/fr/docs/deployment/https.md
================================================
# À propos de HTTPS { #about-https }
Il est facile de supposer que HTTPS est quelque chose qui est simplement « activé » ou non.
Mais c'est beaucoup plus complexe que cela.
/// tip | Astuce
Si vous êtes pressé ou si cela ne vous intéresse pas, continuez avec les sections suivantes pour obtenir des instructions étape par étape afin de tout configurer avec différentes techniques.
///
Pour apprendre les bases du HTTPS, du point de vue d'un utilisateur, consultez [https://howhttps.works/](https://howhttps.works/).
Maintenant, du point de vue d'un développeur, voici plusieurs choses à avoir en tête en pensant au HTTPS :
* Pour le HTTPS, **le serveur** doit **disposer de « certificats »** générés par une **tierce partie**.
* Ces certificats sont en réalité **acquis** auprès de la tierce partie, et non « générés ».
* Les certificats ont une **durée de vie**.
* Ils **expirent**.
* Puis ils doivent être **renouvelés**, **acquis à nouveau** auprès de la tierce partie.
* Le cryptage de la connexion se fait au **niveau TCP**.
* C'est une couche **en dessous de HTTP**.
* Donc, la gestion du **certificat et du cryptage** est effectuée **avant HTTP**.
* **TCP ne connaît pas les « domaines »**. Il ne connaît que les adresses IP.
* L'information sur le **domaine spécifique** demandé se trouve dans les **données HTTP**.
* Les **certificats HTTPS** « certifient » un **certain domaine**, mais le protocole et le cryptage se font au niveau TCP, **avant de savoir** quel domaine est traité.
* **Par défaut**, cela signifie que vous ne pouvez avoir qu'**un seul certificat HTTPS par adresse IP**.
* Quelle que soit la taille de votre serveur ou la petitesse de chacune des applications qu'il contient.
* Il existe cependant une **solution** à ce problème.
* Il existe une **extension** du protocole **TLS** (celui qui gère le cryptage au niveau TCP, avant HTTP) appelée **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* Cette extension SNI permet à un seul serveur (avec une **seule adresse IP**) d'avoir **plusieurs certificats HTTPS** et de servir **plusieurs domaines/applications HTTPS**.
* Pour que cela fonctionne, un **seul** composant (programme) fonctionnant sur le serveur, écoutant sur l'**adresse IP publique**, doit avoir **tous les certificats HTTPS** du serveur.
* **Après** l'établissement d'une connexion sécurisée, le protocole de communication est **toujours HTTP**.
* Le contenu est **crypté**, même s'il est envoyé avec le **protocole HTTP**.
Il est courant d'avoir **un seul programme/serveur HTTP** fonctionnant sur le serveur (la machine, l'hôte, etc.) et **gérant toutes les parties HTTPS** : recevoir les **requêtes HTTPS chiffrées**, envoyer les **requêtes HTTP déchiffrées** à l'application HTTP réelle fonctionnant sur le même serveur (l'application **FastAPI**, dans ce cas), prendre la **réponse HTTP** de l'application, la **chiffrer** en utilisant le **certificat HTTPS** approprié et la renvoyer au client en utilisant **HTTPS**. Ce serveur est souvent appelé un **[Proxy de terminaison TLS](https://en.wikipedia.org/wiki/TLS_termination_proxy)**.
Parmi les options que vous pourriez utiliser comme Proxy de terminaison TLS :
* Traefik (qui peut également gérer les renouvellements de certificats)
* Caddy (qui peut également gérer les renouvellements de certificats)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Avant Let's Encrypt, ces **certificats HTTPS** étaient vendus par des tiers de confiance.
Le processus d'acquisition de l'un de ces certificats était auparavant lourd, nécessitait pas mal de paperasses et les certificats étaient assez chers.
Mais ensuite, **[Let's Encrypt](https://letsencrypt.org/)** a été créé.
Il s'agit d'un projet de la Fondation Linux. Il fournit **des certificats HTTPS gratuitement**, de manière automatisée. Ces certificats utilisent toutes les sécurités cryptographiques standard et ont une durée de vie courte (environ 3 mois), de sorte que la **sécurité est en fait meilleure** en raison de leur durée de vie réduite.
Les domaines sont vérifiés de manière sécurisée et les certificats sont générés automatiquement. Cela permet également d'automatiser le renouvellement de ces certificats.
L'idée est d'automatiser l'acquisition et le renouvellement de ces certificats, afin que vous puissiez disposer d'un **HTTPS sécurisé, gratuitement et pour toujours**.
## HTTPS pour les développeurs { #https-for-developers }
Voici un exemple de ce à quoi pourrait ressembler une API HTTPS, étape par étape, en portant principalement attention aux idées importantes pour les développeurs.
### Nom de domaine { #domain-name }
Tout commencerait probablement par le fait que vous **acquériez** un **nom de domaine**. Ensuite, vous le configureriez dans un serveur DNS (possiblement le même que votre fournisseur cloud).
Vous obtiendriez probablement un serveur cloud (une machine virtuelle) ou quelque chose de similaire, et il aurait une adresse IP publique fixe.
Dans le ou les serveurs DNS, vous configureriez un enregistrement (un « `A record` ») pour faire pointer **votre domaine** vers l'**adresse IP publique de votre serveur**.
Vous feriez probablement cela une seule fois, la première fois, lors de la mise en place de l'ensemble.
/// tip | Astuce
Cette partie relative au nom de domaine intervient bien avant HTTPS, mais comme tout dépend du domaine et de l'adresse IP, il vaut la peine de la mentionner ici.
///
### DNS { #dns }
Concentrons-nous maintenant sur toutes les parties réellement liées à HTTPS.
D'abord, le navigateur vérifierait auprès des **serveurs DNS** quelle est l'**IP du domaine**, dans ce cas, `someapp.example.com`.
Les serveurs DNS indiqueraient au navigateur d'utiliser une **adresse IP** spécifique. Ce serait l'adresse IP publique utilisée par votre serveur, celle que vous avez configurée dans les serveurs DNS.
### Début de la négociation TLS (Handshake) { #tls-handshake-start }
Le navigateur communiquerait ensuite avec cette adresse IP sur le **port 443** (le port HTTPS).
La première partie de la communication consiste simplement à établir la connexion entre le client et le serveur et à décider des clés cryptographiques qu'ils utiliseront, etc.
Cette interaction entre le client et le serveur pour établir la connexion TLS s'appelle la **négociation TLS (TLS handshake)**.
### TLS avec l'extension SNI { #tls-with-sni-extension }
**Un seul processus** sur le serveur peut écouter sur un **port** spécifique d'une **adresse IP** spécifique. Il pourrait y avoir d'autres processus écoutant sur d'autres ports de la même adresse IP, mais un seul pour chaque combinaison d'adresse IP et de port.
TLS (HTTPS) utilise par défaut le port spécifique `443`. C'est donc le port dont nous aurions besoin.
Comme un seul processus peut écouter sur ce port, le processus qui le ferait serait le **Proxy de terminaison TLS**.
Le Proxy de terminaison TLS aurait accès à un ou plusieurs **certificats TLS** (certificats HTTPS).
En utilisant l'**extension SNI** mentionnée plus haut, le Proxy de terminaison TLS vérifierait lequel des certificats TLS (HTTPS) disponibles il devrait utiliser pour cette connexion, en choisissant celui qui correspond au domaine attendu par le client.
Dans ce cas, il utiliserait le certificat pour `someapp.example.com`.
Le client **fait déjà confiance** à l'entité qui a généré ce certificat TLS (dans ce cas Let's Encrypt, mais nous y reviendrons plus tard), il peut donc **vérifier** que le certificat est valide.
Ensuite, en utilisant le certificat, le client et le Proxy de terminaison TLS **décident comment chiffrer** le reste de la **communication TCP**. Cela termine la partie **négociation TLS**.
Après cela, le client et le serveur disposent d'une **connexion TCP chiffrée**, c'est ce que fournit TLS. Ils peuvent alors utiliser cette connexion pour démarrer la **communication HTTP** proprement dite.
Et c'est ce qu'est **HTTPS** : c'est simplement du **HTTP** à l'intérieur d'une **connexion TLS sécurisée** au lieu d'une connexion TCP pure (non chiffrée).
/// tip | Astuce
Remarquez que le cryptage de la communication se produit au **niveau TCP**, pas au niveau HTTP.
///
### Requête HTTPS { #https-request }
Maintenant que le client et le serveur (spécifiquement le navigateur et le Proxy de terminaison TLS) ont une **connexion TCP chiffrée**, ils peuvent démarrer la **communication HTTP**.
Ainsi, le client envoie une **requête HTTPS**. Ce n'est qu'une requête HTTP à travers une connexion TLS chiffrée.
### Déchiffrer la requête { #decrypt-the-request }
Le Proxy de terminaison TLS utiliserait le chiffrement convenu pour **déchiffrer la requête**, et transmettrait la **requête HTTP en clair (déchiffrée)** au processus exécutant l'application (par exemple un processus avec Uvicorn exécutant l'application FastAPI).
### Réponse HTTP { #http-response }
L'application traiterait la requête et enverrait une **réponse HTTP en clair (non chiffrée)** au Proxy de terminaison TLS.
### Réponse HTTPS { #https-response }
Le Proxy de terminaison TLS **chiffrerait ensuite la réponse** en utilisant la cryptographie convenue auparavant (qui a commencé avec le certificat pour `someapp.example.com`), et la renverrait au navigateur.
Ensuite, le navigateur vérifierait que la réponse est valide et chiffrée avec la bonne clé cryptographique, etc. Il **déchiffrerait la réponse** et la traiterait.
Le client (navigateur) saura que la réponse provient du bon serveur parce qu'elle utilise la cryptographie convenue auparavant à l'aide du **certificat HTTPS**.
### Applications multiples { #multiple-applications }
Sur le même serveur (ou les mêmes serveurs), il pourrait y avoir **plusieurs applications**, par exemple d'autres programmes d'API ou une base de données.
Un seul processus peut gérer l'adresse IP et le port spécifiques (le Proxy de terminaison TLS dans notre exemple), mais les autres applications/processus peuvent également s'exécuter sur le ou les serveurs, tant qu'ils n'essaient pas d'utiliser la même **combinaison d'adresse IP publique et de port**.
De cette façon, le Proxy de terminaison TLS pourrait gérer HTTPS et les certificats pour **plusieurs domaines**, pour plusieurs applications, puis transmettre les requêtes à la bonne application dans chaque cas.
### Renouvellement des certificats { #certificate-renewal }
À un moment donné dans le futur, chaque certificat **expirerait** (environ 3 mois après son acquisition).
Ensuite, il y aurait un autre programme (dans certains cas c'est un autre programme, dans d'autres cas cela pourrait être le même Proxy de terminaison TLS) qui communiquerait avec Let's Encrypt et renouvellerait le ou les certificats.
Les **certificats TLS** sont **associés à un nom de domaine**, pas à une adresse IP.
Ainsi, pour renouveler les certificats, le programme de renouvellement doit **prouver** à l'autorité (Let's Encrypt) qu'il **« possède » et contrôle ce domaine**.
Pour ce faire, et pour s'adapter aux différents besoins des applications, il existe plusieurs façons de procéder. Parmi les plus courantes :
* **Modifier certains enregistrements DNS**.
* Pour cela, le programme de renouvellement doit prendre en charge les API du fournisseur DNS ; ainsi, selon le fournisseur DNS que vous utilisez, cela peut être ou non une option.
* **S'exécuter comme un serveur** (au moins pendant le processus d'acquisition du certificat) sur l'adresse IP publique associée au domaine.
* Comme nous l'avons dit plus haut, un seul processus peut écouter sur une adresse IP et un port spécifiques.
* C'est l'une des raisons pour lesquelles il est très utile que le même Proxy de terminaison TLS prenne également en charge le processus de renouvellement des certificats.
* Sinon, vous pourriez avoir à arrêter le Proxy de terminaison TLS momentanément, démarrer le programme de renouvellement pour acquérir les certificats, puis les configurer avec le Proxy de terminaison TLS, et ensuite redémarrer le Proxy de terminaison TLS. Ce n'est pas idéal, car votre/vos application(s) ne seront pas disponibles pendant le temps où le Proxy de terminaison TLS est arrêté.
Tout ce processus de renouvellement, tout en continuant à servir l'application, est l'une des principales raisons pour lesquelles vous voudriez avoir un **système séparé pour gérer HTTPS** avec un Proxy de terminaison TLS, au lieu d'utiliser directement les certificats TLS avec le serveur d'application (par exemple Uvicorn).
## En-têtes Proxy Forwarded { #proxy-forwarded-headers }
Lorsque vous utilisez un proxy pour gérer HTTPS, votre **serveur d'application** (par exemple Uvicorn via FastAPI CLI) ne connaît rien du processus HTTPS, il communique en HTTP en clair avec le **Proxy de terminaison TLS**.
Ce **proxy** définirait normalement certains en-têtes HTTP à la volée avant de transmettre la requête au **serveur d'application**, pour informer le serveur d'application que la requête est **transmise** par le proxy.
/// note | Détails techniques
Les en-têtes du proxy sont :
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Néanmoins, comme le **serveur d'application** ne sait pas qu'il se trouve derrière un **proxy** de confiance, par défaut, il ne ferait pas confiance à ces en-têtes.
Mais vous pouvez configurer le **serveur d'application** pour qu'il fasse confiance aux en-têtes transmis (*forwarded*) envoyés par le **proxy**. Si vous utilisez FastAPI CLI, vous pouvez utiliser l'*option CLI* `--forwarded-allow-ips` pour lui indiquer à partir de quelles IP il doit faire confiance à ces en-têtes transmis.
Par exemple, si le **serveur d'application** ne reçoit des communications que du **proxy** de confiance, vous pouvez définir `--forwarded-allow-ips="*"` pour lui faire faire confiance à toutes les IP entrantes, puisqu'il ne recevra des requêtes que depuis l'IP utilisée par le **proxy**.
De cette façon, l'application sera en mesure de savoir quelle est sa propre URL publique, si elle utilise HTTPS, le domaine, etc.
Cela serait utile, par exemple, pour gérer correctement les redirections.
/// tip | Astuce
Vous pouvez en savoir plus dans la documentation [Derrière un proxy - Activer les en-têtes transmis par le proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Récapitulatif { #recap }
Disposer de **HTTPS** est très important, et assez **critique** dans la plupart des cas. La majeure partie de l'effort que vous, en tant que développeur, devez fournir autour de HTTPS consiste simplement à **comprendre ces concepts** et leur fonctionnement.
Mais une fois que vous connaissez les informations de base sur **HTTPS pour les développeurs**, vous pouvez facilement combiner et configurer différents outils pour vous aider à tout gérer simplement.
Dans certains des prochains chapitres, je vous montrerai plusieurs exemples concrets de configuration de **HTTPS** pour des applications **FastAPI**. 🔒
================================================
FILE: docs/fr/docs/deployment/index.md
================================================
# Déploiement { #deployment }
Le déploiement d'une application **FastAPI** est relativement simple.
## Que signifie le déploiement { #what-does-deployment-mean }
**Déployer** une application signifie effectuer les étapes nécessaires pour la rendre **disponible pour les
utilisateurs**.
Pour une **API Web**, cela implique normalement de la placer sur une **machine distante**, avec un **programme serveur**
qui offre de bonnes performances, une bonne stabilité, _etc._, afin que vos **utilisateurs** puissent **accéder** à
l'application efficacement et sans interruption ni problème.
Ceci contraste avec les étapes de **développement**, où vous êtes constamment en train de modifier le code, de le casser
et de le réparer, d'arrêter et de redémarrer le serveur de développement, _etc._
## Stratégies de déploiement { #deployment-strategies }
Il existe plusieurs façons de procéder, en fonction de votre cas d'utilisation spécifique et des outils que vous
utilisez.
Vous pouvez **déployer un serveur** vous-même en utilisant une combinaison d'outils, vous pouvez utiliser un **service
cloud** qui fait une partie du travail pour vous, ou encore d'autres options possibles.
Par exemple, nous, l'équipe derrière FastAPI, avons créé [**FastAPI Cloud**](https://fastapicloud.com), pour rendre le déploiement d'applications FastAPI dans le cloud aussi fluide que possible, avec la même expérience développeur que lorsque vous travaillez avec FastAPI.
Je vais vous montrer certains des principaux concepts que vous devriez probablement avoir à l'esprit lors du déploiement
d'une application **FastAPI** (bien que la plupart de ces concepts s'appliquent à tout autre type d'application web).
Vous verrez plus de détails à avoir en tête et certaines des techniques pour le faire dans les sections suivantes. ✨
================================================
FILE: docs/fr/docs/deployment/manually.md
================================================
# Exécuter un serveur manuellement { #run-a-server-manually }
## Utiliser la commande `fastapi run` { #use-the-fastapi-run-command }
En bref, utilisez `fastapi run` pour servir votre application FastAPI :
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Cela fonctionnerait pour la plupart des cas. 😎
Vous pourriez utiliser cette commande par exemple pour démarrer votre application **FastAPI** dans un conteneur, sur un serveur, etc.
## Serveurs ASGI { #asgi-servers }
Allons un peu plus en détail.
FastAPI utilise un standard pour construire des frameworks web Python et des serveurs appelé ASGI. FastAPI est un framework web ASGI.
La principale chose dont vous avez besoin pour exécuter une application **FastAPI** (ou toute autre application ASGI) sur une machine serveur distante est un programme serveur ASGI comme **Uvicorn**, c'est celui utilisé par défaut par la commande `fastapi`.
Il existe plusieurs alternatives, notamment :
* [Uvicorn](https://www.uvicorn.dev/) : un serveur ASGI haute performance.
* [Hypercorn](https://hypercorn.readthedocs.io/) : un serveur ASGI compatible avec HTTP/2 et Trio entre autres fonctionnalités.
* [Daphne](https://github.com/django/daphne) : le serveur ASGI conçu pour Django Channels.
* [Granian](https://github.com/emmett-framework/granian) : un serveur HTTP Rust pour les applications Python.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/) : NGINX Unit est un environnement d'exécution d'applications web léger et polyvalent.
## Machine serveur et programme serveur { #server-machine-and-server-program }
Il y a un petit détail sur les noms à garder à l'esprit. 💡
Le mot « serveur » est couramment utilisé pour désigner à la fois l'ordinateur distant/cloud (la machine physique ou virtuelle) et également le programme qui s'exécute sur cette machine (par exemple, Uvicorn).
Gardez cela à l'esprit lorsque vous lisez « serveur » en général, cela pourrait faire référence à l'une de ces deux choses.
Lorsqu'on se réfère à la machine distante, il est courant de l'appeler **serveur**, mais aussi **machine**, **VM** (machine virtuelle), **nœud**. Tout cela fait référence à un type de machine distante, exécutant normalement Linux, sur laquelle vous exécutez des programmes.
## Installer le programme serveur { #install-the-server-program }
Lorsque vous installez FastAPI, il est fourni avec un serveur de production, Uvicorn, et vous pouvez le démarrer avec la commande `fastapi run`.
Mais vous pouvez également installer un serveur ASGI manuellement.
Vous devez créer un [environnement virtuel](../virtual-environments.md), l'activer, puis vous pouvez installer l'application serveur.
Par exemple, pour installer Uvicorn :
Un processus similaire s'appliquerait à tout autre programme de serveur ASGI.
/// tip | Astuce
En ajoutant `standard`, Uvicorn va installer et utiliser quelques dépendances supplémentaires recommandées.
Cela inclut `uvloop`, le remplaçant hautes performances de `asyncio`, qui fournit le gros gain de performance en matière de concurrence.
Lorsque vous installez FastAPI avec quelque chose comme `pip install "fastapi[standard]"`, vous obtenez déjà `uvicorn[standard]` aussi.
///
## Exécuter le programme serveur { #run-the-server-program }
Si vous avez installé un serveur ASGI manuellement, vous devrez normalement passer une chaîne d'import dans un format spécial pour qu'il importe votre application FastAPI :
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Remarque
La commande `uvicorn main:app` fait référence à :
* `main` : le fichier `main.py` (le « module » Python).
* `app` : l'objet créé dans `main.py` avec la ligne `app = FastAPI()`.
C'est équivalent à :
```Python
from main import app
```
///
Chaque programme de serveur ASGI alternatif aurait une commande similaire, vous pouvez en lire plus dans leur documentation respective.
/// warning | Alertes
Uvicorn et d'autres serveurs prennent en charge une option `--reload` utile pendant le développement.
L'option `--reload` consomme beaucoup plus de ressources, est plus instable, etc.
Cela aide beaucoup pendant le **développement**, mais vous **ne devriez pas** l'utiliser en **production**.
///
## Concepts de déploiement { #deployment-concepts }
Ces exemples exécutent le programme serveur (par exemple Uvicorn), en démarrant **un seul processus**, à l'écoute sur toutes les IP (`0.0.0.0`) sur un port prédéfini (par exemple `80`).
C'est l'idée de base. Mais vous voudrez probablement vous occuper de certaines choses supplémentaires, comme :
* Sécurité - HTTPS
* Exécution au démarrage
* Redémarrages
* Réplication (le nombre de processus en cours d'exécution)
* Mémoire
* Étapes précédant le démarrage
Je vous en dirai plus sur chacun de ces concepts, sur la manière d'y réfléchir, et donnerai quelques exemples concrets avec des stratégies pour les gérer dans les prochains chapitres. 🚀
================================================
FILE: docs/fr/docs/deployment/server-workers.md
================================================
# Workers du serveur - Uvicorn avec workers { #server-workers-uvicorn-with-workers }
Reprenons ces concepts de déploiement vus précédemment :
* Sécurité - HTTPS
* Exécution au démarrage
* Redémarrages
* **Réplication (le nombre de processus en cours d'exécution)**
* Mémoire
* Étapes préalables avant le démarrage
Jusqu'à présent, avec tous les tutoriels dans les documents, vous avez probablement exécuté un programme serveur, par exemple avec la commande `fastapi`, qui lance Uvicorn en exécutant un seul processus.
Lors du déploiement d'applications, vous voudrez probablement avoir une réplication de processus pour tirer parti de plusieurs cœurs et pouvoir gérer davantage de requêtes.
Comme vous l'avez vu dans le chapitre précédent sur les [Concepts de déploiement](concepts.md), il existe plusieurs stratégies possibles.
Ici, je vais vous montrer comment utiliser Uvicorn avec des processus workers en utilisant la commande `fastapi` ou directement la commande `uvicorn`.
/// info | Info
Si vous utilisez des conteneurs, par exemple avec Docker ou Kubernetes, je vous en dirai plus à ce sujet dans le prochain chapitre : [FastAPI dans des conteneurs - Docker](docker.md).
En particulier, lorsque vous exécutez sur **Kubernetes**, vous ne voudrez probablement **pas** utiliser de workers et plutôt exécuter **un seul processus Uvicorn par conteneur**, mais je vous en parlerai plus en détail dans ce chapitre.
///
## Utiliser plusieurs workers { #multiple-workers }
Vous pouvez démarrer plusieurs workers avec l'option de ligne de commande `--workers` :
//// tab | `fastapi`
Si vous utilisez la commande `fastapi` :
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Si vous préférez utiliser directement la commande `uvicorn` :
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
La seule option nouvelle ici est `--workers` qui indique à Uvicorn de démarrer 4 processus workers.
Vous pouvez aussi voir qu'il affiche le **PID** de chaque processus, `27365` pour le processus parent (c'est le **gestionnaire de processus**) et un pour chaque processus worker : `27368`, `27369`, `27370` et `27367`.
## Concepts de déploiement { #deployment-concepts }
Ici, vous avez vu comment utiliser plusieurs workers pour paralléliser l'exécution de l'application, tirer parti de plusieurs cœurs du CPU et être en mesure de servir davantage de requêtes.
Dans la liste des concepts de déploiement ci-dessus, l'utilisation de workers aide principalement à la partie réplication, et un peu aux redémarrages, mais vous devez toujours vous occuper des autres :
* **Sécurité - HTTPS**
* **Exécution au démarrage**
* ***Redémarrages***
* Réplication (le nombre de processus en cours d'exécution)
* **Mémoire**
* **Étapes préalables avant le démarrage**
## Conteneurs et Docker { #containers-and-docker }
Dans le prochain chapitre sur [FastAPI dans des conteneurs - Docker](docker.md), j'expliquerai quelques stratégies que vous pourriez utiliser pour gérer les autres **concepts de déploiement**.
Je vous montrerai comment créer votre propre image à partir de zéro pour exécuter un seul processus Uvicorn. C'est un processus simple et c'est probablement ce que vous voudrez faire lorsque vous utilisez un système distribué de gestion de conteneurs comme **Kubernetes**.
## Récapitulatif { #recap }
Vous pouvez utiliser plusieurs processus workers avec l'option CLI `--workers` des commandes `fastapi` ou `uvicorn` pour tirer parti des **CPU multicœurs**, et exécuter **plusieurs processus en parallèle**.
Vous pourriez utiliser ces outils et idées si vous mettez en place votre propre système de déploiement tout en prenant vous-même en charge les autres concepts de déploiement.
Consultez le prochain chapitre pour en savoir plus sur **FastAPI** avec des conteneurs (par exemple Docker et Kubernetes). Vous verrez que ces outils offrent aussi des moyens simples de résoudre les autres **concepts de déploiement**. ✨
================================================
FILE: docs/fr/docs/deployment/versions.md
================================================
# À propos des versions de FastAPI { #about-fastapi-versions }
**FastAPI** est déjà utilisé en production dans de nombreuses applications et de nombreux systèmes. Et la couverture de tests est maintenue à 100 %. Mais son développement avance toujours rapidement.
De nouvelles fonctionnalités sont ajoutées fréquemment, des bogues sont corrigés régulièrement et le code s'améliore continuellement.
C'est pourquoi les versions actuelles sont toujours `0.x.x`, cela reflète que chaque version pourrait potentiellement comporter des changements non rétrocompatibles. Cela suit les conventions de [versionnage sémantique](https://semver.org/).
Vous pouvez créer des applications de production avec **FastAPI** dès maintenant (et vous le faites probablement depuis un certain temps), vous devez juste vous assurer que vous utilisez une version qui fonctionne correctement avec le reste de votre code.
## Épingler votre version de `fastapi` { #pin-your-fastapi-version }
La première chose que vous devez faire est « épingler » la version de **FastAPI** que vous utilisez à la dernière version spécifique dont vous savez qu’elle fonctionne correctement pour votre application.
Par exemple, disons que vous utilisez la version `0.112.0` dans votre application.
Si vous utilisez un fichier `requirements.txt`, vous pouvez spécifier la version avec :
```txt
fastapi[standard]==0.112.0
```
ce qui signifierait que vous utiliseriez exactement la version `0.112.0`.
Ou vous pourriez aussi l'épingler avec :
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
cela signifierait que vous utiliseriez les versions `0.112.0` ou supérieures, mais inférieures à `0.113.0`, par exemple, une version `0.112.2` serait toujours acceptée.
Si vous utilisez un autre outil pour gérer vos installations, comme `uv`, Poetry, Pipenv, ou autres, ils ont tous un moyen que vous pouvez utiliser pour définir des versions spécifiques pour vos paquets.
## Versions disponibles { #available-versions }
Vous pouvez consulter les versions disponibles (par exemple, pour vérifier quelle est la dernière version en date) dans les [Notes de version](../release-notes.md).
## À propos des versions { #about-versions }
Suivant les conventions de versionnage sémantique, toute version inférieure à `1.0.0` peut potentiellement ajouter des changements non rétrocompatibles.
FastAPI suit également la convention selon laquelle tout changement de version « PATCH » concerne des corrections de bogues et des changements rétrocompatibles.
/// tip | Astuce
Le « PATCH » est le dernier chiffre, par exemple, dans `0.2.3`, la version PATCH est `3`.
///
Donc, vous devriez être en mesure d'épingler une version comme suit :
```txt
fastapi>=0.45.0,<0.46.0
```
Les changements non rétrocompatibles et les nouvelles fonctionnalités sont ajoutés dans les versions « MINOR ».
/// tip | Astuce
Le « MINOR » est le numéro au milieu, par exemple, dans `0.2.3`, la version MINOR est `2`.
///
## Mettre à niveau les versions de FastAPI { #upgrading-the-fastapi-versions }
Vous devez ajouter des tests pour votre application.
Avec **FastAPI** c'est très facile (merci à Starlette), consultez les documents : [Tests](../tutorial/testing.md)
Après avoir des tests, vous pouvez mettre à niveau la version de **FastAPI** vers une version plus récente et vous assurer que tout votre code fonctionne correctement en exécutant vos tests.
Si tout fonctionne, ou après avoir effectué les changements nécessaires, et que tous vos tests passent, vous pouvez alors épingler votre `fastapi` à cette nouvelle version récente.
## À propos de Starlette { #about-starlette }
Vous ne devez pas épingler la version de `starlette`.
Différentes versions de **FastAPI** utiliseront une version spécifique plus récente de Starlette.
Ainsi, vous pouvez simplement laisser **FastAPI** utiliser la bonne version de Starlette.
## À propos de Pydantic { #about-pydantic }
Pydantic inclut les tests pour **FastAPI** avec ses propres tests, ainsi les nouvelles versions de Pydantic (au-dessus de `1.0.0`) sont toujours compatibles avec FastAPI.
Vous pouvez épingler Pydantic à toute version supérieure à `1.0.0` qui fonctionne pour vous.
Par exemple :
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/fr/docs/editor-support.md
================================================
# Prise en charge des éditeurs { #editor-support }
L’extension officielle [Extension FastAPI](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) améliore votre flux de développement FastAPI grâce à la découverte des chemins d'accès, à la navigation, ainsi qu’au déploiement sur FastAPI Cloud et à la diffusion en direct des journaux.
Pour plus de détails sur l’extension, reportez-vous au README sur le [référentiel GitHub](https://github.com/fastapi/fastapi-vscode).
## Configurer et installer { #setup-and-installation }
L’**Extension FastAPI** est disponible pour [VS Code](https://code.visualstudio.com/) et [Cursor](https://www.cursor.com/). Vous pouvez l’installer directement depuis le panneau Extensions de chaque éditeur en recherchant « FastAPI » et en sélectionnant l’extension publiée par **FastAPI Labs**. L’extension fonctionne également dans les éditeurs basés sur le navigateur tels que [vscode.dev](https://vscode.dev) et [github.dev](https://github.dev).
### Découvrir l’application { #application-discovery }
Par défaut, l’extension détecte automatiquement les applications FastAPI dans votre espace de travail en recherchant les fichiers qui instancient `FastAPI()`. Si la détection automatique ne convient pas à la structure de votre projet, vous pouvez spécifier un point d’entrée via `[tool.fastapi]` dans `pyproject.toml` ou le paramètre VS Code `fastapi.entryPoint`, en utilisant la notation de module (par ex. `myapp.main:app`).
## Fonctionnalités { #features }
- **Explorateur des chemins d'accès** — Une vue arborescente latérale de tous les *chemins d'accès* de votre application. Cliquez pour accéder à n’importe quelle définition de route ou de routeur.
- **Recherche de routes** — Recherchez par chemin, méthode ou nom avec Ctrl + Shift + E (sur macOS : Cmd + Shift + E).
- **Navigation CodeLens** — Liens cliquables au-dessus des appels du client de test (par ex. `client.get('/items')`) menant au *chemin d'accès* correspondant, pour naviguer rapidement entre les tests et l’implémentation.
- **Déployer sur FastAPI Cloud** — Déploiement en un clic de votre application sur [FastAPI Cloud](https://fastapicloud.com/).
- **Diffuser les journaux de l’application** — Diffusion en temps réel des journaux de votre application déployée sur FastAPI Cloud, avec filtrage par niveau et recherche textuelle.
Si vous souhaitez vous familiariser avec les fonctionnalités de l’extension, vous pouvez consulter le guide pas à pas de l’extension en ouvrant la palette de commandes (Ctrl + Shift + P ou sur macOS : Cmd + Shift + P) et en sélectionnant « Welcome: Open walkthrough ... » puis en choisissant le guide « Get started with FastAPI ».
================================================
FILE: docs/fr/docs/environment-variables.md
================================================
# Variables d'environnement { #environment-variables }
/// tip | Astuce
Si vous savez déjà ce que sont les « variables d'environnement » et comment les utiliser, vous pouvez passer cette section.
///
Une variable d'environnement (également appelée « env var ») est une variable qui vit en dehors du code Python, dans le système d'exploitation, et qui peut être lue par votre code Python (ou par d'autres programmes également).
Les variables d'environnement peuvent être utiles pour gérer des **paramètres** d'application, dans le cadre de l'**installation** de Python, etc.
## Créer et utiliser des variables d'environnement { #create-and-use-env-vars }
Vous pouvez créer et utiliser des variables d'environnement dans le **shell (terminal)**, sans avoir besoin de Python :
//// tab | Linux, macOS, Windows Bash
```console
// Vous pouvez créer une variable d'environnement MY_NAME avec
$ export MY_NAME="Wade Wilson"
// Vous pouvez ensuite l'utiliser avec d'autres programmes, par exemple
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Créer une variable d'environnement MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// L'utiliser avec d'autres programmes, par exemple
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Lire des variables d'environnement en Python { #read-env-vars-in-python }
Vous pouvez également créer des variables d'environnement **en dehors** de Python, dans le terminal (ou par tout autre moyen), puis les **lire en Python**.
Par exemple, vous pouvez avoir un fichier `main.py` contenant :
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Astuce
Le deuxième argument de [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) est la valeur par défaut à retourner.
S'il n'est pas fourni, c'est `None` par défaut ; ici, nous fournissons `"World"` comme valeur par défaut à utiliser.
///
Vous pouvez ensuite exécuter ce programme Python :
//// tab | Linux, macOS, Windows Bash
```console
// Ici, nous ne définissons pas encore la variable d'environnement
$ python main.py
// Comme nous ne l'avons pas définie, nous obtenons la valeur par défaut
Hello World from Python
// Mais si nous créons d'abord une variable d'environnement
$ export MY_NAME="Wade Wilson"
// Puis que nous relançons le programme
$ python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Ici, nous ne définissons pas encore la variable d'environnement
$ python main.py
// Comme nous ne l'avons pas définie, nous obtenons la valeur par défaut
Hello World from Python
// Mais si nous créons d'abord une variable d'environnement
$ $Env:MY_NAME = "Wade Wilson"
// Puis que nous relançons le programme
$ python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
```
////
Comme les variables d'environnement peuvent être définies en dehors du code, mais lues par le code, et qu'elles n'ont pas besoin d'être stockées (validées dans `git`) avec le reste des fichiers, il est courant de les utiliser pour les configurations ou les **paramètres**.
Vous pouvez également créer une variable d'environnement uniquement pour l'**invocation d'un programme spécifique**, qui ne sera disponible que pour ce programme et uniquement pendant sa durée d'exécution.
Pour cela, créez-la juste avant le programme, sur la même ligne :
```console
// Créer en ligne une variable d'environnement MY_NAME pour cet appel de programme
$ MY_NAME="Wade Wilson" python main.py
// Il peut maintenant lire la variable d'environnement
Hello Wade Wilson from Python
// La variable d'environnement n'existe plus ensuite
$ python main.py
Hello World from Python
```
/// tip | Astuce
Vous pouvez en lire davantage sur [The Twelve-Factor App : Config](https://12factor.net/config).
///
## Gérer les types et la validation { #types-and-validation }
Ces variables d'environnement ne peuvent gérer que des **chaînes de texte**, car elles sont externes à Python et doivent être compatibles avec les autres programmes et le reste du système (et même avec différents systèmes d'exploitation, comme Linux, Windows, macOS).
Cela signifie que **toute valeur** lue en Python à partir d'une variable d'environnement **sera une `str`**, et que toute conversion vers un autre type ou toute validation doit être effectuée dans le code.
Vous en apprendrez davantage sur l'utilisation des variables d'environnement pour gérer les **paramètres d'application** dans le [Guide utilisateur avancé - Paramètres et variables d'environnement](./advanced/settings.md).
## Variable d'environnement `PATH` { #path-environment-variable }
Il existe une **variable d'environnement spéciale** appelée **`PATH`** qui est utilisée par les systèmes d'exploitation (Linux, macOS, Windows) pour trouver les programmes à exécuter.
La valeur de la variable `PATH` est une longue chaîne composée de répertoires séparés par deux-points `:` sous Linux et macOS, et par point-virgule `;` sous Windows.
Par exemple, la variable d'environnement `PATH` peut ressembler à ceci :
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Cela signifie que le système doit rechercher les programmes dans les répertoires :
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Cela signifie que le système doit rechercher les programmes dans les répertoires :
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Lorsque vous tapez une **commande** dans le terminal, le système d'exploitation **cherche** le programme dans **chacun de ces répertoires** listés dans la variable d'environnement `PATH`.
Par exemple, lorsque vous tapez `python` dans le terminal, le système d'exploitation cherche un programme nommé `python` dans le **premier répertoire** de cette liste.
S'il le trouve, alors il **l'utilise**. Sinon, il continue à chercher dans les **autres répertoires**.
### Installer Python et mettre à jour `PATH` { #installing-python-and-updating-the-path }
Lorsque vous installez Python, il est possible que l'on vous demande si vous souhaitez mettre à jour la variable d'environnement `PATH`.
//// tab | Linux, macOS
Supposons que vous installiez Python et qu'il se retrouve dans un répertoire `/opt/custompython/bin`.
Si vous acceptez de mettre à jour la variable d'environnement `PATH`, l'installateur ajoutera `/opt/custompython/bin` à la variable d'environnement `PATH`.
Cela pourrait ressembler à ceci :
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans `/opt/custompython/bin` (le dernier répertoire) et utilisera celui-là.
////
//// tab | Windows
Supposons que vous installiez Python et qu'il se retrouve dans un répertoire `C:\opt\custompython\bin`.
Si vous acceptez de mettre à jour la variable d'environnement `PATH`, l'installateur ajoutera `C:\opt\custompython\bin` à la variable d'environnement `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans `C:\opt\custompython\bin` (le dernier répertoire) et utilisera celui-là.
////
Ainsi, si vous tapez :
```console
$ python
```
//// tab | Linux, macOS
Le système va **trouver** le programme `python` dans `/opt/custompython/bin` et l'exécuter.
Cela reviendrait à peu près à taper :
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
Le système va **trouver** le programme `python` dans `C:\opt\custompython\bin\python` et l'exécuter.
Cela reviendrait à peu près à taper :
```console
$ C:\opt\custompython\bin\python
```
////
Ces informations vous seront utiles lors de l'apprentissage des [Environnements virtuels](virtual-environments.md).
## Conclusion { #conclusion }
Avec cela, vous devriez avoir une compréhension de base de ce que sont les **variables d'environnement** et de la façon de les utiliser en Python.
Vous pouvez également en lire davantage sur la [page Wikipédia dédiée aux variables d'environnement](https://en.wikipedia.org/wiki/Environment_variable).
Dans de nombreux cas, il n'est pas évident de voir immédiatement en quoi les variables d'environnement seraient utiles et applicables. Mais elles réapparaissent dans de nombreux scénarios lorsque vous développez, il est donc bon de les connaître.
Par exemple, vous aurez besoin de ces informations dans la section suivante, sur les [Environnements virtuels](virtual-environments.md).
================================================
FILE: docs/fr/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** est un programme en ligne de commande que vous pouvez utiliser pour servir votre application FastAPI, gérer votre projet FastAPI, et plus encore.
Lorsque vous installez FastAPI (par exemple avec `pip install "fastapi[standard]"`), il est fourni avec un programme en ligne de commande que vous pouvez exécuter dans le terminal.
Pour exécuter votre application FastAPI en développement, vous pouvez utiliser la commande `fastapi dev` :
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Astuce
Pour la production, utilisez `fastapi run` plutôt que `fastapi dev`. 🚀
///
En interne, **FastAPI CLI** utilise [Uvicorn](https://www.uvicorn.dev), un serveur ASGI haute performance, prêt pour la production. 😎
La CLI `fastapi` tentera de détecter automatiquement l’application FastAPI à exécuter, en supposant qu’il s’agit d’un objet nommé `app` dans un fichier `main.py` (ou quelques autres variantes).
Mais vous pouvez configurer explicitement l’application à utiliser.
## Configurer le `entrypoint` de l’application dans `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Vous pouvez configurer l’endroit où se trouve votre application dans un fichier `pyproject.toml` comme suit :
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Cet `entrypoint` indiquera à la commande `fastapi` qu’elle doit importer l’application comme ceci :
```python
from main import app
```
Si votre code était structuré comme ceci :
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Vous définiriez alors le `entrypoint` comme suit :
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
ce qui serait équivalent à :
```python
from backend.main import app
```
### `fastapi dev` avec un chemin { #fastapi-dev-with-path }
Vous pouvez également passer le chemin du fichier à la commande `fastapi dev`, et elle devinera l’objet d’application FastAPI à utiliser :
```console
$ fastapi dev main.py
```
Mais vous devez vous rappeler de passer le bon chemin à chaque fois que vous appelez la commande `fastapi`.
De plus, d’autres outils pourraient ne pas pouvoir le trouver, par exemple l’[extension VS Code](editor-support.md) ou [FastAPI Cloud](https://fastapicloud.com), il est donc recommandé d’utiliser le `entrypoint` dans `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
L’exécution de `fastapi dev` lance le mode développement.
Par défaut, l’**auto-reload** est activé et recharge automatiquement le serveur lorsque vous modifiez votre code. Cela consomme des ressources et peut être moins stable que lorsqu’il est désactivé. Vous devez l’utiliser uniquement pour le développement. Il écoute aussi sur l’adresse IP `127.0.0.1`, qui est l’adresse IP permettant à votre machine de communiquer uniquement avec elle‑même (`localhost`).
## `fastapi run` { #fastapi-run }
Exécuter `fastapi run` démarre FastAPI en mode production par défaut.
Par défaut, l’**auto-reload** est désactivé. Il écoute aussi sur l’adresse IP `0.0.0.0`, ce qui signifie toutes les adresses IP disponibles ; de cette manière, il sera accessible publiquement à toute personne pouvant communiquer avec la machine. C’est ainsi que vous l’exécutez normalement en production, par exemple dans un conteneur.
Dans la plupart des cas, vous avez (et devez avoir) un « termination proxy » au‑dessus qui gère le HTTPS pour vous ; cela dépend de la façon dont vous déployez votre application : votre fournisseur peut le faire pour vous, ou vous devrez le configurer vous‑même.
/// tip | Astuce
Vous pouvez en savoir plus à ce sujet dans la [documentation de déploiement](deployment/index.md).
///
================================================
FILE: docs/fr/docs/features.md
================================================
# Fonctionnalités { #features }
## Fonctionnalités de FastAPI { #fastapi-features }
**FastAPI** vous offre les éléments suivants :
### Basé sur des standards ouverts { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) pour la création d'API, incluant la déclaration de chemin opérations, paramètres, corps de requêtes, sécurité, etc.
* Documentation automatique des modèles de données avec [**JSON Schema**](https://json-schema.org/) (puisque OpenAPI est lui-même basé sur JSON Schema).
* Conçu autour de ces standards, après une étude méticuleuse. Plutôt qu'une couche ajoutée après coup.
* Cela permet également d'utiliser la **génération automatique de code client** dans de nombreux langages.
### Documentation automatique { #automatic-docs }
Documentation d'API interactive et interfaces web d'exploration. Comme le framework est basé sur OpenAPI, plusieurs options existent, 2 incluses par défaut.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), avec exploration interactive, appelez et testez votre API directement depuis le navigateur.

* Documentation d'API alternative avec [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Uniquement du Python moderne { #just-modern-python }
Tout est basé sur les déclarations de **types Python** standard (grâce à Pydantic). Aucune nouvelle syntaxe à apprendre. Juste du Python moderne standard.
Si vous avez besoin d'un rappel de 2 minutes sur l'utilisation des types en Python (même si vous n'utilisez pas FastAPI), consultez le court tutoriel : [Types Python](python-types.md).
Vous écrivez du Python standard avec des types :
```Python
from datetime import date
from pydantic import BaseModel
# Déclarez une variable comme étant une str
# et profitez de l'aide de l'éditeur dans cette fonction
def main(user_id: str):
return user_id
# Un modèle Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Qui peuvent ensuite être utilisés comme ceci :
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info
`**second_user_data` signifie :
Passez les clés et valeurs du dictionnaire `second_user_data` directement comme arguments clé-valeur, équivalent à : `User(id=4, name="Mary", joined="2018-11-30")`
///
### Support des éditeurs { #editor-support }
Tout le framework a été conçu pour être facile et intuitif à utiliser, toutes les décisions ont été testées sur plusieurs éditeurs avant même de commencer le développement, pour assurer la meilleure expérience de développement.
Dans les enquêtes auprès des développeurs Python, il est clair [que l’une des fonctionnalités les plus utilisées est « autocomplétion »](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
L'ensemble du framework **FastAPI** est conçu pour satisfaire cela. L'autocomplétion fonctionne partout.
Vous aurez rarement besoin de revenir aux documents.
Voici comment votre éditeur peut vous aider :
* dans [Visual Studio Code](https://code.visualstudio.com/) :

* dans [PyCharm](https://www.jetbrains.com/pycharm/) :

Vous obtiendrez de l'autocomplétion dans du code que vous auriez pu considérer impossible auparavant. Par exemple, la clé `price` à l'intérieur d'un corps JSON (qui aurait pu être imbriqué) provenant d'une requête.
Fini de taper des noms de clés erronés, de faire des allers-retours entre les documents, ou de faire défiler vers le haut et vers le bas pour savoir si vous avez finalement utilisé `username` ou `user_name`.
### Court { #short }
Des **valeurs par défaut** sensées pour tout, avec des configurations optionnelles partout. Tous les paramètres peuvent être ajustés finement pour faire ce dont vous avez besoin et définir l'API dont vous avez besoin.
Mais par défaut, tout **« just works »**.
### Validation { #validation }
* Validation pour la plupart (ou tous ?) des **types de données** Python, y compris :
* objets JSON (`dict`).
* tableaux JSON (`list`) définissant les types d'éléments.
* champs String (`str`), définition des longueurs minimale et maximale.
* nombres (`int`, `float`) avec valeurs minimale et maximale, etc.
* Validation pour des types plus exotiques, comme :
* URL.
* Email.
* UUID.
* ... et autres.
Toutes les validations sont gérées par le **Pydantic** bien établi et robuste.
### Sécurité et authentification { #security-and-authentication }
Sécurité et authentification intégrées. Sans aucun compromis avec les bases de données ou les modèles de données.
Tous les schémas de sécurité définis dans OpenAPI, y compris :
* HTTP Basic.
* **OAuth2** (également avec des **tokens JWT**). Consultez le tutoriel [OAuth2 avec JWT](tutorial/security/oauth2-jwt.md).
* Clés d'API dans :
* les en-têtes.
* les paramètres de requête.
* les cookies, etc.
Plus toutes les fonctionnalités de sécurité de Starlette (y compris les **cookies de session**).
Le tout construit comme des outils et composants réutilisables, faciles à intégrer à vos systèmes, magasins de données, bases de données relationnelles et NoSQL, etc.
### Injection de dépendances { #dependency-injection }
FastAPI inclut un système d’Injection de dépendances extrêmement simple à utiliser, mais extrêmement puissant.
* Même les dépendances peuvent avoir des dépendances, créant une hiérarchie ou un **« graphe » de dépendances**.
* Le tout **géré automatiquement** par le framework.
* Toutes les dépendances peuvent exiger des données des requêtes et **augmenter les contraintes du chemin d'accès** ainsi que la documentation automatique.
* **Validation automatique** même pour les paramètres de *chemin d'accès* définis dans les dépendances.
* Prise en charge des systèmes d'authentification d'utilisateurs complexes, des **connexions de base de données**, etc.
* **Aucun compromis** avec les bases de données, les frontends, etc. Mais une intégration facile avec tous.
### « Plug-ins » illimités { #unlimited-plug-ins }
Ou, autrement dit, pas besoin d'eux, importez et utilisez le code dont vous avez besoin.
Toute intégration est conçue pour être si simple à utiliser (avec des dépendances) que vous pouvez créer un « plug-in » pour votre application en 2 lignes de code en utilisant la même structure et la même syntaxe que pour vos *chemins d'accès*.
### Testé { #tested }
* 100 % de couverture de test.
* 100 % de base de code annotée avec des types.
* Utilisé dans des applications en production.
## Fonctionnalités de Starlette { #starlette-features }
**FastAPI** est entièrement compatible avec (et basé sur) [**Starlette**](https://www.starlette.dev/). Donc, tout code Starlette additionnel que vous avez fonctionnera aussi.
`FastAPI` est en fait une sous-classe de `Starlette`. Ainsi, si vous connaissez ou utilisez déjà Starlette, la plupart des fonctionnalités fonctionneront de la même manière.
Avec **FastAPI** vous obtenez toutes les fonctionnalités de **Starlette** (puisque FastAPI est juste Starlette sous stéroïdes) :
* Des performances vraiment impressionnantes. C'est [l’un des frameworks Python les plus rapides disponibles, à l’égal de **NodeJS** et **Go**](https://github.com/encode/starlette#performance).
* Prise en charge des **WebSocket**.
* Tâches d'arrière-plan dans le processus.
* Évènements de démarrage et d'arrêt.
* Client de test basé sur HTTPX.
* **CORS**, GZip, fichiers statiques, réponses en streaming.
* Prise en charge des **Sessions et Cookies**.
* Couverture de test à 100 %.
* Base de code annotée à 100 % avec des types.
## Fonctionnalités de Pydantic { #pydantic-features }
**FastAPI** est entièrement compatible avec (et basé sur) [**Pydantic**](https://docs.pydantic.dev/). Donc, tout code Pydantic additionnel que vous avez fonctionnera aussi.
Y compris des bibliothèques externes également basées sur Pydantic, servant d’ORM, d’ODM pour les bases de données.
Cela signifie également que, dans de nombreux cas, vous pouvez passer l'objet que vous recevez d'une requête **directement à la base de données**, puisque tout est validé automatiquement.
L’inverse est également vrai, dans de nombreux cas, vous pouvez simplement passer l'objet que vous récupérez de la base de données **directement au client**.
Avec **FastAPI** vous obtenez toutes les fonctionnalités de **Pydantic** (puisque FastAPI est basé sur Pydantic pour toute la gestion des données) :
* **Pas de prise de tête** :
* Pas de micro-langage de définition de schéma à apprendre.
* Si vous connaissez les types Python vous savez utiliser Pydantic.
* Fonctionne bien avec votre **IDE/linter/cerveau** :
* Parce que les structures de données de Pydantic sont simplement des instances de classes que vous définissez ; l'autocomplétion, le linting, mypy et votre intuition devraient tous bien fonctionner avec vos données validées.
* Valider des **structures complexes** :
* Utilisation de modèles Pydantic hiérarchiques, de `List` et `Dict` du `typing` Python, etc.
* Et les validateurs permettent de définir, vérifier et documenter clairement et facilement des schémas de données complexes en tant que JSON Schema.
* Vous pouvez avoir des objets **JSON fortement imbriqués** et les faire tous valider et annoter.
* **Extensible** :
* Pydantic permet de définir des types de données personnalisés ou vous pouvez étendre la validation avec des méthodes sur un modèle décoré avec le décorateur de validation.
* Couverture de test à 100 %.
================================================
FILE: docs/fr/docs/help-fastapi.md
================================================
# Aider FastAPI - Obtenir de l'aide { #help-fastapi-get-help }
Aimez-vous **FastAPI** ?
Souhaitez-vous aider FastAPI, les autres utilisateurs et l'auteur ?
Ou souhaitez-vous obtenir de l'aide avec **FastAPI** ?
Il existe des moyens très simples d'aider (plusieurs ne nécessitent qu'un ou deux clics).
Et il existe aussi plusieurs façons d'obtenir de l'aide.
## S'abonner à la newsletter { #subscribe-to-the-newsletter }
Vous pouvez vous abonner à la (peu fréquente) [newsletter **FastAPI and friends**](newsletter.md) pour rester informé à propos :
* Nouvelles sur FastAPI et ses amis 🚀
* Guides 📝
* Fonctionnalités ✨
* Changements majeurs 🚨
* Astuces et conseils ✅
## Suivre FastAPI sur X (Twitter) { #follow-fastapi-on-x-twitter }
[Suivez @fastapi sur **X (Twitter)**](https://x.com/fastapi) pour obtenir les dernières nouvelles sur **FastAPI**. 🐦
## Mettre une étoile à **FastAPI** sur GitHub { #star-fastapi-in-github }
Vous pouvez « star » FastAPI sur GitHub (en cliquant sur le bouton étoile en haut à droite) : [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
En ajoutant une étoile, les autres utilisateurs pourront le trouver plus facilement et voir qu'il a déjà été utile à d'autres.
## Suivre le dépôt GitHub pour les releases { #watch-the-github-repository-for-releases }
Vous pouvez « watch » FastAPI sur GitHub (en cliquant sur le bouton « watch » en haut à droite) : [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Vous pouvez y sélectionner « Releases only ».
Ainsi, vous recevrez des notifications (par e‑mail) chaque fois qu'il y aura une nouvelle release (une nouvelle version) de **FastAPI** avec des corrections de bugs et de nouvelles fonctionnalités.
## Entrer en contact avec l'auteur { #connect-with-the-author }
Vous pouvez entrer en contact avec [moi (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), l'auteur.
Vous pouvez :
* [Me suivre sur **GitHub**](https://github.com/tiangolo).
* Voir d'autres projets Open Source que j'ai créés et qui pourraient vous aider.
* Me suivre pour voir quand je crée un nouveau projet Open Source.
* [Me suivre sur **X (Twitter)**](https://x.com/tiangolo) ou sur [Mastodon](https://fosstodon.org/@tiangolo).
* Me dire comment vous utilisez FastAPI (j'adore l'entendre).
* Être informé quand je fais des annonces ou publie de nouveaux outils.
* Vous pouvez aussi [suivre @fastapi sur X (Twitter)](https://x.com/fastapi) (un compte séparé).
* [Me suivre sur **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Être informé quand je fais des annonces ou publie de nouveaux outils (même si j'utilise plus souvent X (Twitter) 🤷♂).
* Lire ce que j'écris (ou me suivre) sur [**Dev.to**](https://dev.to/tiangolo) ou [**Medium**](https://medium.com/@tiangolo).
* Lire d'autres idées, des articles, et découvrir des outils que j'ai créés.
* Me suivre pour lire quand je publie quelque chose de nouveau.
## Tweeter à propos de **FastAPI** { #tweet-about-fastapi }
[Tweetez à propos de **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) et faites savoir à moi et aux autres pourquoi vous l'appréciez. 🎉
J'adore entendre comment **FastAPI** est utilisé, ce que vous avez aimé, dans quel projet/quelle entreprise vous l'utilisez, etc.
## Voter pour FastAPI { #vote-for-fastapi }
* [Votez pour **FastAPI** sur Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Votez pour **FastAPI** sur AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Indiquez que vous utilisez **FastAPI** sur StackShare](https://stackshare.io/pypi-fastapi).
## Aider les autres avec des questions sur GitHub { #help-others-with-questions-in-github }
Vous pouvez essayer d'aider les autres avec leurs questions dans :
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
Dans de nombreux cas, vous connaissez peut-être déjà la réponse à ces questions. 🤓
Si vous aidez beaucoup de personnes avec leurs questions, vous deviendrez un [Expert FastAPI](fastapi-people.md#fastapi-experts) officiel. 🎉
N'oubliez pas, le point le plus important est : essayez d'être aimable. Les gens viennent avec leurs frustrations et, dans bien des cas, ne posent pas la question de la meilleure façon, mais faites de votre mieux pour rester aimable. 🤗
L'idée est que la communauté **FastAPI** soit bienveillante et accueillante. En même temps, n'acceptez pas l'intimidation ni les comportements irrespectueux envers les autres. Nous devons prendre soin les uns des autres.
---
Voici comment aider les autres avec des questions (dans les discussions ou les issues) :
### Comprendre la question { #understand-the-question }
* Vérifiez si vous comprenez quel est l’**objectif** et le cas d'utilisation de la personne qui pose la question.
* Ensuite, vérifiez si la question (la grande majorité sont des questions) est **claire**.
* Dans de nombreux cas, la question porte sur une solution imaginaire de l'utilisateur, mais il pourrait y en avoir une **meilleure**. Si vous comprenez mieux le problème et le cas d'utilisation, vous pourriez suggérer une **solution alternative** plus adaptée.
* Si vous ne comprenez pas la question, demandez plus de **détails**.
### Reproduire le problème { #reproduce-the-problem }
Dans la plupart des cas et pour la plupart des questions, il y a quelque chose lié au **code original** de la personne.
Dans de nombreux cas, elle ne copiera qu'un fragment de code, mais ce n'est pas suffisant pour **reproduire le problème**.
* Vous pouvez leur demander de fournir un [exemple minimal, complet et vérifiable](https://stackoverflow.com/help/minimal-reproducible-example), que vous pouvez **copier‑coller** et exécuter localement pour voir la même erreur ou le même comportement qu'ils observent, ou pour mieux comprendre leur cas d'utilisation.
* Si vous vous sentez très généreux, vous pouvez essayer de **créer un tel exemple** vous‑même, simplement à partir de la description du problème. Gardez simplement à l'esprit que cela peut prendre beaucoup de temps et qu'il peut être préférable de leur demander d'abord de clarifier le problème.
### Suggérer des solutions { #suggest-solutions }
* Après avoir compris la question, vous pouvez leur donner une **réponse** possible.
* Dans de nombreux cas, il est préférable de comprendre leur **problème sous‑jacent ou cas d'utilisation**, car il pourrait exister une meilleure façon de le résoudre que ce qu'ils essaient de faire.
### Demander la clôture { #ask-to-close }
S'ils répondent, il y a de fortes chances que vous ayez résolu leur problème, bravo, **vous êtes un héros** ! 🦸
* Maintenant, si cela a résolu leur problème, vous pouvez leur demander de :
* Dans GitHub Discussions : marquer le commentaire comme **réponse**.
* Dans GitHub Issues : **fermer** l'issue.
## Suivre le dépôt GitHub { #watch-the-github-repository }
Vous pouvez « watch » FastAPI sur GitHub (en cliquant sur le bouton « watch » en haut à droite) : [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Si vous sélectionnez « Watching » au lieu de « Releases only », vous recevrez des notifications lorsque quelqu'un crée une nouvelle issue ou question. Vous pouvez aussi préciser que vous ne souhaitez être notifié que pour les nouvelles issues, ou les discussions, ou les PR, etc.
Vous pouvez alors essayer de les aider à résoudre ces questions.
## Poser des questions { #ask-questions }
Vous pouvez [créer une nouvelle question](https://github.com/fastapi/fastapi/discussions/new?category=questions) dans le dépôt GitHub, par exemple pour :
* Poser une **question** ou demander à propos d'un **problème**.
* Suggérer une nouvelle **fonctionnalité**.
**Remarque** : si vous le faites, je vais vous demander d'aider aussi les autres. 😉
## Relire des Pull Requests { #review-pull-requests }
Vous pouvez m'aider à relire les pull requests des autres.
Encore une fois, essayez autant que possible d'être aimable. 🤗
---
Voici ce à garder à l'esprit et comment relire une pull request :
### Comprendre le problème { #understand-the-problem }
* D'abord, assurez‑vous de **comprendre le problème** que la pull request essaie de résoudre. Il peut y avoir une discussion plus longue dans une GitHub Discussion ou une issue.
* Il y a aussi de bonnes chances que la pull request ne soit pas réellement nécessaire parce que le problème peut être résolu d'une **autre manière**. Vous pouvez alors le suggérer ou poser la question.
### Ne pas s'inquiéter du style { #dont-worry-about-style }
* Ne vous souciez pas trop des choses comme les styles de messages de commit, je ferai un squash and merge en personnalisant le commit manuellement.
* Ne vous inquiétez pas non plus des règles de style, il existe déjà des outils automatisés qui vérifient cela.
Et s'il y a d'autres besoins de style ou de cohérence, je le demanderai directement, ou j'ajouterai des commits par‑dessus avec les changements nécessaires.
### Vérifier le code { #check-the-code }
* Vérifiez et lisez le code, voyez s'il a du sens, **exécutez‑le localement** et voyez s'il résout effectivement le problème.
* Ensuite, **commentez** en disant que vous l'avez fait, c'est ainsi que je saurai que vous l'avez vraiment vérifié.
/// info
Malheureusement, je ne peux pas simplement faire confiance aux PR qui ont juste plusieurs approbations.
Plusieurs fois, il est arrivé qu'il y ait des PR avec 3, 5 ou plus approbations, probablement parce que la description est attrayante, mais lorsque je vérifie les PR, elles sont en fait cassées, ont un bug, ou ne résolvent pas le problème qu'elles prétendent résoudre. 😅
Donc, il est vraiment important que vous lisiez et exécutiez le code, et que vous me le disiez dans les commentaires. 🤓
///
* Si la PR peut être simplifiée d'une certaine manière, vous pouvez le demander, mais il n'est pas nécessaire d'être trop pointilleux, il peut y avoir beaucoup de points de vue subjectifs (et j'aurai les miens aussi 🙈), donc il est préférable de vous concentrer sur les choses fondamentales.
### Tests { #tests }
* Aidez‑moi à vérifier que la PR a des **tests**.
* Vérifiez que les tests **échouent** avant la PR. 🚨
* Puis vérifiez que les tests **réussissent** après la PR. ✅
* Beaucoup de PR n'ont pas de tests, vous pouvez leur **rappeler** d'ajouter des tests, ou même **suggérer** des tests vous‑même. C'est l'une des choses qui consomment le plus de temps et vous pouvez beaucoup aider.
* Commentez aussi ce que vous avez essayé, ainsi je saurai que vous l'avez vérifié. 🤓
## Créer une Pull Request { #create-a-pull-request }
Vous pouvez [contribuer](contributing.md) au code source avec des Pull Requests, par exemple :
* Corriger une coquille que vous avez trouvée dans la documentation.
* Partager un article, une vidéo ou un podcast que vous avez créé ou trouvé à propos de FastAPI en [modifiant ce fichier](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Vous devez vous assurer d'ajouter votre lien au début de la section correspondante.
* Aider à [traduire la documentation](contributing.md#translations) dans votre langue.
* Vous pouvez aussi aider à relire les traductions créées par d'autres.
* Proposer de nouvelles sections de documentation.
* Corriger une issue/un bug existant.
* Vous devez ajouter des tests.
* Ajouter une nouvelle fonctionnalité.
* Vous devez ajouter des tests.
* Vous devez ajouter de la documentation si c'est pertinent.
## Aider à maintenir FastAPI { #help-maintain-fastapi }
Aidez‑moi à maintenir **FastAPI** ! 🤓
Il y a beaucoup de travail à faire, et pour la plupart, **VOUS** pouvez le faire.
Les principales tâches que vous pouvez faire dès maintenant sont :
* [Aider les autres avec des questions sur GitHub](#help-others-with-questions-in-github) (voir la section ci‑dessus).
* [Relire des Pull Requests](#review-pull-requests) (voir la section ci‑dessus).
Ces deux tâches sont celles qui **consomment le plus de temps**. C'est le travail principal de la maintenance de FastAPI.
Si vous pouvez m'aider avec cela, **vous m'aidez à maintenir FastAPI** et à vous assurer qu'il continue **d'avancer plus vite et mieux**. 🚀
## Rejoindre le chat { #join-the-chat }
Rejoignez le 👥 [serveur Discord](https://discord.gg/VQjSZaeJmf) 👥 et échangez avec d'autres membres de la communauté FastAPI.
/// tip | Astuce
Pour les questions, posez‑les dans [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), vous avez bien plus de chances de recevoir de l'aide par les [Experts FastAPI](fastapi-people.md#fastapi-experts).
Utilisez le chat uniquement pour d'autres conversations générales.
///
### N'utilisez pas le chat pour les questions { #dont-use-the-chat-for-questions }
Gardez à l'esprit que, comme les chats permettent une « conversation libre », il est facile de poser des questions trop générales et plus difficiles à répondre ; vous pourriez donc ne pas recevoir de réponses.
Sur GitHub, le modèle vous guidera pour rédiger la bonne question afin que vous puissiez plus facilement obtenir une bonne réponse, ou même résoudre le problème vous‑même avant de demander. Et sur GitHub, je peux m'assurer de toujours tout répondre, même si cela prend du temps. Je ne peux pas personnellement faire cela avec les systèmes de chat. 😅
Les conversations dans les systèmes de chat ne sont pas non plus aussi facilement recherchables que sur GitHub, donc les questions et réponses peuvent se perdre dans la conversation. Et seules celles sur GitHub comptent pour devenir un [Expert FastAPI](fastapi-people.md#fastapi-experts), vous aurez donc très probablement plus d'attention sur GitHub.
D'un autre côté, il y a des milliers d'utilisateurs dans les systèmes de chat, il y a donc de fortes chances que vous trouviez presque toujours quelqu'un avec qui parler. 😄
## Sponsoriser l'auteur { #sponsor-the-author }
Si votre **produit/entreprise** dépend de **FastAPI** ou y est lié et que vous souhaitez atteindre ses utilisateurs, vous pouvez sponsoriser l'auteur (moi) via [GitHub sponsors](https://github.com/sponsors/tiangolo). Selon le niveau, vous pourriez obtenir des avantages supplémentaires, comme un badge dans les documents. 🎁
---
Merci ! 🚀
================================================
FILE: docs/fr/docs/history-design-future.md
================================================
# Histoire, conception et avenir { #history-design-and-future }
Il y a quelque temps, [un utilisateur de **FastAPI** a demandé](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920) :
> Quelle est l'histoire de ce projet ? Il semble être sorti de nulle part et est devenu génial en quelques semaines [...].
Voici un petit bout de cette histoire.
## Alternatives { #alternatives }
Je crée des API avec des exigences complexes depuis plusieurs années (Machine Learning, systèmes distribués, jobs asynchrones, bases de données NoSQL, etc), en dirigeant plusieurs équipes de développeurs.
Dans ce cadre, j'ai dû étudier, tester et utiliser de nombreuses alternatives.
L'histoire de **FastAPI** est en grande partie l'histoire de ses prédécesseurs.
Comme dit dans la section [Alternatives](alternatives.md) :
**FastAPI** n'existerait pas sans le travail antérieur d'autres personnes.
Il y a eu de nombreux outils créés auparavant qui ont contribué à inspirer sa création.
J'ai évité la création d'un nouveau framework pendant plusieurs années. J'ai d'abord essayé de résoudre toutes les fonctionnalités couvertes par **FastAPI** en utilisant de nombreux frameworks, plug-ins et outils différents.
Mais à un moment donné, il n'y avait pas d'autre option que de créer quelque chose qui offre toutes ces fonctionnalités, en prenant les meilleures idées des outils précédents, et en les combinant de la meilleure façon possible, en utilisant des fonctionnalités du langage qui n'étaient même pas disponibles auparavant (annotations de type pour Python 3.6+).
## Recherche { #investigation }
En utilisant toutes les alternatives précédentes, j'ai eu la chance d'apprendre de toutes, de prendre des idées, et de les combiner de la meilleure façon que j'ai pu trouver pour moi-même et les équipes de développeurs avec lesquelles j'ai travaillé.
Par exemple, il était clair que l'idéal était de se baser sur les annotations de type Python standard.
De plus, la meilleure approche était d'utiliser des normes déjà existantes.
Ainsi, avant même de commencer à coder **FastAPI**, j'ai passé plusieurs mois à étudier les spécifications d'OpenAPI, JSON Schema, OAuth2, etc. Comprendre leurs relations, leurs similarités et leurs différences.
## Conception { #design }
Ensuite, j'ai passé du temps à concevoir l'« API » de développeur que je voulais avoir en tant qu'utilisateur (en tant que développeur utilisant FastAPI).
J'ai testé plusieurs idées dans les éditeurs Python les plus populaires : PyCharm, VS Code, les éditeurs basés sur Jedi.
D'après la dernière [Enquête Développeurs Python](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), cela couvre environ 80% des utilisateurs.
Cela signifie que **FastAPI** a été spécifiquement testé avec les éditeurs utilisés par 80% des développeurs Python. Et comme la plupart des autres éditeurs ont tendance à fonctionner de façon similaire, tous ses avantages devraient fonctionner pour pratiquement tous les éditeurs.
Ainsi, j'ai pu trouver les meilleurs moyens de réduire autant que possible la duplication du code, d'avoir l'autocomplétion partout, les contrôles de type et d'erreur, etc.
Le tout de manière à offrir la meilleure expérience de développement à tous les développeurs.
## Exigences { #requirements }
Après avoir testé plusieurs alternatives, j'ai décidé que j'allais utiliser [**Pydantic**](https://docs.pydantic.dev/) pour ses avantages.
J'y ai ensuite contribué, pour le rendre entièrement compatible avec JSON Schema, pour supporter différentes manières de définir les déclarations de contraintes, et pour améliorer le support des éditeurs (vérifications de type, autocomplétion) sur la base des tests effectués dans plusieurs éditeurs.
Pendant le développement, j'ai également contribué à [**Starlette**](https://www.starlette.dev/), l'autre exigence clé.
## Développement { #development }
Au moment où j'ai commencé à créer **FastAPI** lui-même, la plupart des pièces étaient déjà en place, la conception était définie, les exigences et les outils étaient prêts, et la connaissance des normes et des spécifications était claire et fraîche.
## Futur { #future }
À ce stade, il est déjà clair que **FastAPI** et ses idées sont utiles pour de nombreuses personnes.
Elle a été préférée aux solutions précédentes parce qu'elle convient mieux à de nombreux cas d'utilisation.
De nombreux développeurs et équipes dépendent déjà de **FastAPI** pour leurs projets (y compris moi et mon équipe).
Mais il y a encore de nombreuses améliorations et fonctionnalités à venir.
**FastAPI** a un grand avenir devant lui.
Et [votre aide](help-fastapi.md) est grandement appréciée.
================================================
FILE: docs/fr/docs/how-to/authentication-error-status-code.md
================================================
# Utiliser les anciens codes d'erreur d'authentification 403 { #use-old-403-authentication-error-status-codes }
Avant FastAPI version `0.122.0`, lorsque les utilitaires de sécurité intégrés renvoyaient une erreur au client après un échec d'authentification, ils utilisaient le code d'état HTTP `403 Forbidden`.
À partir de FastAPI version `0.122.0`, ils utilisent le code d'état HTTP plus approprié `401 Unauthorized`, et renvoient un en-tête `WWW-Authenticate` pertinent dans la réponse, conformément aux spécifications HTTP, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Mais si, pour une raison quelconque, vos clients dépendent de l'ancien comportement, vous pouvez y revenir en surchargeant la méthode `make_not_authenticated_error` dans vos classes de sécurité.
Par exemple, vous pouvez créer une sous-classe de `HTTPBearer` qui renvoie une erreur `403 Forbidden` au lieu de l'erreur par défaut `401 Unauthorized` :
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Astuce
Remarquez que la fonction renvoie l'instance de l'exception, elle ne la lève pas. La levée est effectuée dans le reste du code interne.
///
================================================
FILE: docs/fr/docs/how-to/conditional-openapi.md
================================================
# Configurer OpenAPI de manière conditionnelle { #conditional-openapi }
Si nécessaire, vous pouvez utiliser des paramètres et des variables d'environnement pour configurer OpenAPI de manière conditionnelle selon l'environnement, et même le désactiver complètement.
## À propos de la sécurité, des API et des documents { #about-security-apis-and-docs }
Masquer vos interfaces utilisateur des documents en production ne devrait pas être la manière de protéger votre API.
Cela n'ajoute aucune sécurité supplémentaire à votre API, les *chemins d'accès* resteront disponibles là où ils se trouvent.
S'il y a une faille de sécurité dans votre code, elle existera toujours.
Masquer les documents rend simplement plus difficile la compréhension de la manière d'interagir avec votre API et pourrait aussi rendre son débogage en production plus difficile. Cela pourrait être considéré simplement comme une forme de [Sécurité par l'obscurité](https://en.wikipedia.org/wiki/Security_through_obscurity).
Si vous voulez sécuriser votre API, il y a plusieurs meilleures approches possibles, par exemple :
* Vous devez vous assurer d'avoir des modèles Pydantic bien définis pour le corps de la requête et la réponse.
* Configurez toutes les autorisations et tous les rôles nécessaires à l'aide de dépendances.
* Ne stockez jamais de mots de passe en clair, seulement des hachages de mots de passe.
* Implémentez et utilisez des outils cryptographiques reconnus, comme pwdlib et des jetons JWT, ... etc.
* Ajoutez des contrôles d'autorisation plus granulaires avec des scopes OAuth2 lorsque nécessaire.
* ... etc.
Néanmoins, vous pourriez avoir un cas d'utilisation très spécifique où vous devez vraiment désactiver les documents de l'API pour un certain environnement (par exemple pour la production) ou selon des configurations provenant de variables d'environnement.
## Configurer OpenAPI de manière conditionnelle avec des paramètres et des variables d'environnement { #conditional-openapi-from-settings-and-env-vars }
Vous pouvez facilement utiliser les mêmes paramètres Pydantic pour configurer votre OpenAPI généré et les interfaces utilisateur des documents.
Par exemple :
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Ici nous déclarons le paramètre `openapi_url` avec la même valeur par défaut `"/openapi.json"`.
Nous l'utilisons ensuite lors de la création de l'application `FastAPI`.
Vous pouvez alors désactiver OpenAPI (y compris les interfaces utilisateur des documents) en définissant la variable d'environnement `OPENAPI_URL` sur la chaîne vide, comme ceci :
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Ensuite, si vous allez aux URL `/openapi.json`, `/docs` ou `/redoc`, vous obtiendrez simplement une erreur `404 Not Found` comme :
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/fr/docs/how-to/configure-swagger-ui.md
================================================
# Configurer Swagger UI { #configure-swagger-ui }
Vous pouvez configurer des [paramètres supplémentaires de Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
Pour les configurer, passez l'argument `swagger_ui_parameters` lors de la création de l'objet d'application `FastAPI()` ou à la fonction `get_swagger_ui_html()`.
`swagger_ui_parameters` reçoit un dictionnaire avec les configurations passées directement à Swagger UI.
FastAPI convertit les configurations en **JSON** pour les rendre compatibles avec JavaScript, car c'est ce dont Swagger UI a besoin.
## Désactiver la coloration syntaxique { #disable-syntax-highlighting }
Par exemple, vous pourriez désactiver la coloration syntaxique dans Swagger UI.
Sans modifier les paramètres, la coloration syntaxique est activée par défaut :
Mais vous pouvez la désactiver en définissant `syntaxHighlight` à `False` :
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
... et ensuite Swagger UI n'affichera plus la coloration syntaxique :
## Modifier le thème { #change-the-theme }
De la même manière, vous pouvez définir le thème de la coloration syntaxique avec la clé « syntaxHighlight.theme » (remarquez le point au milieu) :
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Cette configuration modifierait le thème de couleurs de la coloration syntaxique :
## Modifier les paramètres Swagger UI par défaut { #change-default-swagger-ui-parameters }
FastAPI inclut des paramètres de configuration par défaut adaptés à la plupart des cas d'utilisation.
Il inclut ces configurations par défaut :
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Vous pouvez remplacer n'importe lequel d'entre eux en définissant une valeur différente dans l'argument `swagger_ui_parameters`.
Par exemple, pour désactiver `deepLinking`, vous pourriez passer ces paramètres à `swagger_ui_parameters` :
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Autres paramètres de Swagger UI { #other-swagger-ui-parameters }
Pour voir toutes les autres configurations possibles que vous pouvez utiliser, lisez les [documents officiels pour les paramètres de Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Paramètres JavaScript uniquement { #javascript-only-settings }
Swagger UI permet également d'autres configurations qui sont des objets réservés à JavaScript (par exemple, des fonctions JavaScript).
FastAPI inclut aussi ces paramètres `presets` réservés à JavaScript :
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Ce sont des objets **JavaScript**, pas des chaînes, vous ne pouvez donc pas les passer directement depuis du code Python.
Si vous devez utiliser des configurations réservées à JavaScript comme celles-ci, vous pouvez utiliser l'une des méthodes ci-dessus. Surchargez entièrement le *chemin d'accès* Swagger UI et écrivez manuellement tout JavaScript nécessaire.
================================================
FILE: docs/fr/docs/how-to/custom-docs-ui-assets.md
================================================
# Héberger en propre les ressources statiques de l’UI des docs personnalisées { #custom-docs-ui-static-assets-self-hosting }
Les documents de l’API utilisent **Swagger UI** et **ReDoc**, et chacune nécessite des fichiers JavaScript et CSS.
Par défaut, ces fichiers sont servis depuis un CDN.
Mais il est possible de le personnaliser : vous pouvez définir un CDN spécifique, ou servir vous‑même les fichiers.
## Configurer un CDN personnalisé pour JavaScript et CSS { #custom-cdn-for-javascript-and-css }
Supposons que vous souhaitiez utiliser un autre CDN, par exemple vous voulez utiliser `https://unpkg.com/`.
Cela peut être utile si, par exemple, vous vivez dans un pays qui restreint certaines URL.
### Désactiver les docs automatiques { #disable-the-automatic-docs }
La première étape consiste à désactiver les docs automatiques, car par défaut elles utilisent le CDN par défaut.
Pour les désactiver, définissez leurs URL sur `None` lors de la création de votre application `FastAPI` :
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Inclure les docs personnalisées { #include-the-custom-docs }
Vous pouvez maintenant créer les chemins d'accès pour les docs personnalisées.
Vous pouvez réutiliser les fonctions internes de FastAPI pour créer les pages HTML de la documentation et leur passer les arguments nécessaires :
- `openapi_url` : l’URL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici l’attribut `app.openapi_url`.
- `title` : le titre de votre API.
- `oauth2_redirect_url` : vous pouvez utiliser `app.swagger_ui_oauth2_redirect_url` ici pour utiliser la valeur par défaut.
- `swagger_js_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. C’est l’URL du CDN personnalisé.
- `swagger_css_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. C’est l’URL du CDN personnalisé.
Et de même pour ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Astuce
Le chemin d'accès pour `swagger_ui_redirect` est un assistant lorsque vous utilisez OAuth2.
Si vous intégrez votre API à un fournisseur OAuth2, vous pourrez vous authentifier et revenir aux docs de l’API avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
Swagger UI s’en chargera en arrière‑plan pour vous, mais il a besoin de cet assistant « redirect ».
///
### Créer un chemin d'accès pour tester { #create-a-path-operation-to-test-it }
Maintenant, pour pouvoir vérifier que tout fonctionne, créez un chemin d'accès :
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Tester { #test-it }
Vous devriez maintenant pouvoir aller à vos docs sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), puis recharger la page : elle chargera ces ressources depuis le nouveau CDN.
## Héberger en propre JavaScript et CSS pour les docs { #self-hosting-javascript-and-css-for-docs }
Héberger vous‑même le JavaScript et le CSS peut être utile si, par exemple, votre application doit continuer de fonctionner même hors ligne, sans accès Internet ouvert, ou sur un réseau local.
Vous verrez ici comment servir ces fichiers vous‑même, dans la même application FastAPI, et configurer les docs pour les utiliser.
### Structure des fichiers du projet { #project-file-structure }
Supposons que la structure de vos fichiers de projet ressemble à ceci :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Créez maintenant un répertoire pour stocker ces fichiers statiques.
Votre nouvelle structure de fichiers pourrait ressembler à ceci :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Télécharger les fichiers { #download-the-files }
Téléchargez les fichiers statiques nécessaires aux docs et placez‑les dans ce répertoire `static/`.
Vous pouvez probablement cliquer avec le bouton droit sur chaque lien et choisir une option similaire à « Enregistrer le lien sous ... ».
**Swagger UI** utilise les fichiers :
- [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
- [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
Et **ReDoc** utilise le fichier :
- [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Après cela, votre structure de fichiers pourrait ressembler à :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Servir les fichiers statiques { #serve-the-static-files }
- Importer `StaticFiles`.
- « Monter » une instance `StaticFiles()` sur un chemin spécifique.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Tester les fichiers statiques { #test-the-static-files }
Démarrez votre application et rendez‑vous sur [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Vous devriez voir un très long fichier JavaScript pour **ReDoc**.
Il pourrait commencer par quelque chose comme :
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Cela confirme que vous parvenez à servir des fichiers statiques depuis votre application et que vous avez placé les fichiers statiques des docs au bon endroit.
Nous pouvons maintenant configurer l’application pour utiliser ces fichiers statiques pour les docs.
### Désactiver les docs automatiques pour les fichiers statiques { #disable-the-automatic-docs-for-static-files }
Comme lors de l’utilisation d’un CDN personnalisé, la première étape consiste à désactiver les docs automatiques, car elles utilisent un CDN par défaut.
Pour les désactiver, définissez leurs URL sur `None` lors de la création de votre application `FastAPI` :
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Inclure les docs personnalisées pour les fichiers statiques { #include-the-custom-docs-for-static-files }
Et comme avec un CDN personnalisé, vous pouvez maintenant créer les chemins d'accès pour les docs personnalisées.
Là encore, vous pouvez réutiliser les fonctions internes de FastAPI pour créer les pages HTML de la documentation et leur passer les arguments nécessaires :
- `openapi_url` : l’URL où la page HTML des docs peut récupérer le schéma OpenAPI de votre API. Vous pouvez utiliser ici l’attribut `app.openapi_url`.
- `title` : le titre de votre API.
- `oauth2_redirect_url` : vous pouvez utiliser `app.swagger_ui_oauth2_redirect_url` ici pour utiliser la valeur par défaut.
- `swagger_js_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **JavaScript**. **C’est celui que votre propre application sert désormais**.
- `swagger_css_url` : l’URL où la page HTML de Swagger UI peut récupérer le fichier **CSS**. **C’est celui que votre propre application sert désormais**.
Et de même pour ReDoc ...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Astuce
Le chemin d'accès pour `swagger_ui_redirect` est un assistant lorsque vous utilisez OAuth2.
Si vous intégrez votre API à un fournisseur OAuth2, vous pourrez vous authentifier et revenir aux docs de l’API avec les identifiants acquis. Et interagir avec elle en utilisant la véritable authentification OAuth2.
Swagger UI s’en chargera en arrière‑plan pour vous, mais il a besoin de cet assistant « redirect ».
///
### Créer un chemin d'accès pour tester les fichiers statiques { #create-a-path-operation-to-test-static-files }
Maintenant, pour pouvoir vérifier que tout fonctionne, créez un chemin d'accès :
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Tester l’UI avec des fichiers statiques { #test-static-files-ui }
Vous devriez maintenant pouvoir couper votre Wi‑Fi, aller à vos docs sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) et recharger la page.
Et même sans Internet, vous pourrez voir la documentation de votre API et interagir avec elle.
================================================
FILE: docs/fr/docs/how-to/custom-request-and-route.md
================================================
# Personnaliser les classes Request et APIRoute { #custom-request-and-apiroute-class }
Dans certains cas, vous pouvez vouloir surcharger la logique utilisée par les classes `Request` et `APIRoute`.
En particulier, cela peut être une bonne alternative à une logique dans un middleware.
Par exemple, si vous voulez lire ou manipuler le corps de la requête avant qu'il ne soit traité par votre application.
/// danger | Danger
Ceci est une fonctionnalité « avancée ».
Si vous débutez avec **FastAPI**, vous pouvez ignorer cette section.
///
## Cas d'utilisation { #use-cases }
Voici quelques cas d'utilisation :
* Convertir des corps de requête non JSON en JSON (par exemple [`msgpack`](https://msgpack.org/index.html)).
* Décompresser des corps de requête compressés en gzip.
* Journaliser automatiquement tous les corps de requête.
## Gérer les encodages personnalisés du corps de la requête { #handling-custom-request-body-encodings }
Voyons comment utiliser une sous-classe personnalisée de `Request` pour décompresser des requêtes gzip.
Et une sous-classe d'`APIRoute` pour utiliser cette classe de requête personnalisée.
### Créer une classe `GzipRequest` personnalisée { #create-a-custom-gziprequest-class }
/// tip | Astuce
Il s'agit d'un exemple simplifié pour montrer le fonctionnement ; si vous avez besoin de la prise en charge de Gzip, vous pouvez utiliser le [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) fourni.
///
Commencez par créer une classe `GzipRequest`, qui va surcharger la méthode `Request.body()` pour décompresser le corps en présence d'un en-tête approprié.
S'il n'y a pas `gzip` dans l'en-tête, elle n'essaiera pas de décompresser le corps.
De cette manière, la même classe de route peut gérer des requêtes gzip compressées ou non compressées.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Créer une classe `GzipRoute` personnalisée { #create-a-custom-gziproute-class }
Ensuite, nous créons une sous-classe personnalisée de `fastapi.routing.APIRoute` qui utilisera `GzipRequest`.
Cette fois, elle va surcharger la méthode `APIRoute.get_route_handler()`.
Cette méthode renvoie une fonction. Et c'est cette fonction qui recevra une requête et retournera une réponse.
Ici, nous l'utilisons pour créer une `GzipRequest` à partir de la requête originale.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Détails techniques
Un `Request` possède un attribut `request.scope`, qui n'est qu'un `dict` Python contenant les métadonnées liées à la requête.
Un `Request` a également un `request.receive`, qui est une fonction pour « recevoir » le corps de la requête.
Le `dict` `scope` et la fonction `receive` font tous deux partie de la spécification ASGI.
Et ces deux éléments, `scope` et `receive`, sont ce dont on a besoin pour créer une nouvelle instance de `Request`.
Pour en savoir plus sur `Request`, consultez [la documentation de Starlette sur les requêtes](https://www.starlette.dev/requests/).
///
La seule chose que fait différemment la fonction renvoyée par `GzipRequest.get_route_handler`, c'est de convertir la `Request` en `GzipRequest`.
Ce faisant, notre `GzipRequest` se chargera de décompresser les données (si nécessaire) avant de les transmettre à nos *chemins d'accès*.
Après cela, toute la logique de traitement est identique.
Mais grâce à nos modifications dans `GzipRequest.body`, le corps de la requête sera automatiquement décompressé lorsque **FastAPI** le chargera, si nécessaire.
## Accéder au corps de la requête dans un gestionnaire d'exceptions { #accessing-the-request-body-in-an-exception-handler }
/// tip | Astuce
Pour résoudre ce même problème, il est probablement beaucoup plus simple d'utiliser `body` dans un gestionnaire personnalisé pour `RequestValidationError` ([Gérer les erreurs](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Mais cet exemple reste valable et montre comment interagir avec les composants internes.
///
Nous pouvons également utiliser cette même approche pour accéder au corps de la requête dans un gestionnaire d'exceptions.
Il suffit de traiter la requête dans un bloc `try`/`except` :
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Si une exception se produit, l'instance de `Request` sera toujours dans la portée, ce qui nous permet de lire et d'utiliser le corps de la requête lors du traitement de l'erreur :
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Utiliser une classe `APIRoute` personnalisée dans un routeur { #custom-apiroute-class-in-a-router }
Vous pouvez également définir le paramètre `route_class` d'un `APIRouter` :
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
Dans cet exemple, les *chemins d'accès* sous le `router` utiliseront la classe personnalisée `TimedRoute`, et auront un en-tête supplémentaire `X-Response-Time` dans la réponse avec le temps nécessaire pour générer la réponse :
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/fr/docs/how-to/extending-openapi.md
================================================
# Étendre OpenAPI { #extending-openapi }
Il existe des cas où vous pouvez avoir besoin de modifier le schéma OpenAPI généré.
Dans cette section, vous verrez comment faire.
## Le processus normal { #the-normal-process }
Le processus normal (par défaut) est le suivant.
Une application (instance) `FastAPI` a une méthode `.openapi()` censée retourner le schéma OpenAPI.
Lors de la création de l'objet application, un *chemin d'accès* pour `/openapi.json` (ou pour l'URL que vous avez définie dans votre `openapi_url`) est enregistré.
Il renvoie simplement une réponse JSON avec le résultat de la méthode `.openapi()` de l'application.
Par défaut, la méthode `.openapi()` vérifie la propriété `.openapi_schema` pour voir si elle contient des données et les renvoie.
Sinon, elle les génère à l'aide de la fonction utilitaire `fastapi.openapi.utils.get_openapi`.
Et cette fonction `get_openapi()` reçoit comme paramètres :
* `title` : Le titre OpenAPI, affiché dans les documents.
* `version` : La version de votre API, p. ex. `2.5.0`.
* `openapi_version` : La version de la spécification OpenAPI utilisée. Par défaut, la plus récente : `3.1.0`.
* `summary` : Un court résumé de l'API.
* `description` : La description de votre API ; elle peut inclure du markdown et sera affichée dans la documentation.
* `routes` : Une liste de routes ; chacune correspond à un *chemin d'accès* enregistré. Elles sont extraites de `app.routes`.
/// info
Le paramètre `summary` est disponible à partir d'OpenAPI 3.1.0, pris en charge par FastAPI 0.99.0 et versions ultérieures.
///
## Remplacer les valeurs par défaut { #overriding-the-defaults }
En vous appuyant sur les informations ci-dessus, vous pouvez utiliser la même fonction utilitaire pour générer le schéma OpenAPI et remplacer chaque partie dont vous avez besoin.
Par exemple, ajoutons [l’extension OpenAPI de ReDoc pour inclure un logo personnalisé](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### **FastAPI** normal { #normal-fastapi }
Tout d’abord, écrivez votre application **FastAPI** comme d’habitude :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Générer le schéma OpenAPI { #generate-the-openapi-schema }
Ensuite, utilisez la même fonction utilitaire pour générer le schéma OpenAPI, dans une fonction `custom_openapi()` :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modifier le schéma OpenAPI { #modify-the-openapi-schema }
Vous pouvez maintenant ajouter l’extension ReDoc, en ajoutant un `x-logo` personnalisé à l’« objet » `info` dans le schéma OpenAPI :
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Mettre en cache le schéma OpenAPI { #cache-the-openapi-schema }
Vous pouvez utiliser la propriété `.openapi_schema` comme « cache » pour stocker votre schéma généré.
Ainsi, votre application n’aura pas à générer le schéma à chaque fois qu’un utilisateur ouvre les documents de votre API.
Il ne sera généré qu’une seule fois, puis le même schéma en cache sera utilisé pour les requêtes suivantes.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Remplacer la méthode { #override-the-method }
Vous pouvez maintenant remplacer la méthode `.openapi()` par votre nouvelle fonction.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Vérifier { #check-it }
Une fois que vous allez sur [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc), vous verrez que vous utilisez votre logo personnalisé (dans cet exemple, le logo de **FastAPI**) :
================================================
FILE: docs/fr/docs/how-to/general.md
================================================
# Général - Guides pratiques - Recettes { #general-how-to-recipes }
Voici plusieurs renvois vers d'autres endroits dans les documents, pour des questions générales ou fréquentes.
## Filtrer des données - Sécurité { #filter-data-security }
Pour vous assurer que vous ne renvoyez pas plus de données que nécessaire, lisez les documents [Tutoriel - Modèle de réponse - Type de retour](../tutorial/response-model.md).
## Optimiser la performance des réponses - Modèle de réponse - Type de retour { #optimize-response-performance-response-model-return-type }
Pour optimiser la performance lors du renvoi de données JSON, utilisez un type de retour ou un modèle de réponse ; de cette façon, Pydantic prendra en charge la sérialisation en JSON côté Rust, sans passer par Python. Pour en savoir plus, lisez les documents [Tutoriel - Modèle de réponse - Type de retour](../tutorial/response-model.md).
## Étiquettes de documentation - OpenAPI { #documentation-tags-openapi }
Pour ajouter des étiquettes à vos *chemins d'accès* et les regrouper dans l'interface utilisateur de la documentation, lisez les documents [Tutoriel - Configurations de chemin d'accès - Tags](../tutorial/path-operation-configuration.md#tags).
## Résumé et description de la documentation - OpenAPI { #documentation-summary-and-description-openapi }
Pour ajouter un résumé et une description à vos *chemins d'accès* et les afficher dans l'interface utilisateur de la documentation, lisez les documents [Tutoriel - Configurations de chemin d'accès - Résumé et description](../tutorial/path-operation-configuration.md#summary-and-description).
## Description de la réponse dans la documentation - OpenAPI { #documentation-response-description-openapi }
Pour définir la description de la réponse, affichée dans l'interface utilisateur de la documentation, lisez les documents [Tutoriel - Configurations de chemin d'accès - Description de la réponse](../tutorial/path-operation-configuration.md#response-description).
## Déprécier un *chemin d'accès* dans la documentation - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Pour déprécier un *chemin d'accès* et l'indiquer dans l'interface utilisateur de la documentation, lisez les documents [Tutoriel - Configurations de chemin d'accès - Dépréciation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Convertir n'importe quelles données au format compatible JSON { #convert-any-data-to-json-compatible }
Pour convertir des données vers un format compatible JSON, lisez les documents [Tutoriel - Encodeur compatible JSON](../tutorial/encoder.md).
## Métadonnées OpenAPI - Documentation { #openapi-metadata-docs }
Pour ajouter des métadonnées à votre schéma OpenAPI, y compris une licence, une version, un contact, etc., lisez les documents [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md).
## URL OpenAPI personnalisée { #openapi-custom-url }
Pour personnaliser l'URL OpenAPI (ou la supprimer), lisez les documents [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md#openapi-url).
## URL de la documentation OpenAPI { #openapi-docs-urls }
Pour mettre à jour les URL utilisées pour les interfaces utilisateur de documentation générées automatiquement, lisez les documents [Tutoriel - Métadonnées et URLs de la documentation](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/fr/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Comme **FastAPI** est basé sur la norme **ASGI**, il est très facile d'intégrer toute bibliothèque **GraphQL** également compatible avec ASGI.
Vous pouvez combiner des *chemins d'accès* FastAPI classiques avec GraphQL dans la même application.
/// tip | Astuce
**GraphQL** résout des cas d'utilisation très spécifiques.
Il présente des **avantages** et des **inconvénients** par rapport aux **API web** classiques.
Assurez-vous d'évaluer si les **bénéfices** pour votre cas d'utilisation compensent les **inconvénients**. 🤓
///
## Bibliothèques GraphQL { #graphql-libraries }
Voici quelques bibliothèques **GraphQL** qui prennent en charge **ASGI**. Vous pouvez les utiliser avec **FastAPI** :
* [Strawberry](https://strawberry.rocks/) 🍓
* Avec [la documentation pour FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* Avec [la documentation pour FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* Avec [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) pour fournir l'intégration ASGI
* [Graphene](https://graphene-python.org/)
* Avec [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL avec Strawberry { #graphql-with-strawberry }
Si vous avez besoin ou souhaitez travailler avec **GraphQL**, [**Strawberry**](https://strawberry.rocks/) est la bibliothèque **recommandée** car sa conception est la plus proche de celle de **FastAPI**, tout est basé sur des **annotations de type**.
Selon votre cas d'utilisation, vous pourriez préférer une autre bibliothèque, mais si vous me le demandiez, je vous suggérerais probablement d'essayer **Strawberry**.
Voici un petit aperçu de la manière dont vous pouvez intégrer Strawberry avec FastAPI :
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Vous pouvez en apprendre davantage sur Strawberry dans la [documentation de Strawberry](https://strawberry.rocks/).
Et également la documentation sur [Strawberry avec FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Ancien `GraphQLApp` de Starlette { #older-graphqlapp-from-starlette }
Les versions précédentes de Starlette incluaient une classe `GraphQLApp` pour s'intégrer à [Graphene](https://graphene-python.org/).
Elle a été dépréciée dans Starlette, mais si vous avez du code qui l'utilisait, vous pouvez facilement **migrer** vers [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), qui couvre le même cas d'utilisation et propose une **interface presque identique**.
/// tip | Astuce
Si vous avez besoin de GraphQL, je vous recommande tout de même de regarder [Strawberry](https://strawberry.rocks/), car il est basé sur des annotations de type plutôt que sur des classes et types personnalisés.
///
## En savoir plus { #learn-more }
Vous pouvez en apprendre davantage sur **GraphQL** dans la [documentation officielle de GraphQL](https://graphql.org/).
Vous pouvez également en lire davantage sur chacune des bibliothèques décrites ci-dessus via leurs liens.
================================================
FILE: docs/fr/docs/how-to/index.md
================================================
# Comment faire - Recettes { #how-to-recipes }
Vous trouverez ici différentes recettes ou des guides « comment faire » pour **plusieurs sujets**.
La plupart de ces idées sont plus ou moins **indépendantes**, et dans la plupart des cas vous n'avez besoin de les étudier que si elles s'appliquent directement à **votre projet**.
Si quelque chose vous paraît intéressant et utile pour votre projet, allez-y et consultez-le ; sinon, vous pouvez probablement simplement les ignorer.
/// tip | Astuce
Si vous voulez **apprendre FastAPI** de façon structurée (recommandé), allez lire le [Tutoriel - Guide utilisateur](../tutorial/index.md) chapitre par chapitre à la place.
///
================================================
FILE: docs/fr/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Migrer de Pydantic v1 à Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Si vous avez une ancienne application FastAPI, vous utilisez peut-être Pydantic version 1.
FastAPI version 0.100.0 prenait en charge soit Pydantic v1 soit v2. Il utilisait celle que vous aviez installée.
FastAPI version 0.119.0 a introduit une prise en charge partielle de Pydantic v1 depuis l'intérieur de Pydantic v2 (comme `pydantic.v1`), pour faciliter la migration vers v2.
FastAPI 0.126.0 a supprimé la prise en charge de Pydantic v1, tout en continuant à prendre en charge `pydantic.v1` pendant un certain temps.
/// warning | Alertes
L'équipe Pydantic a arrêté la prise en charge de Pydantic v1 pour les dernières versions de Python, à partir de **Python 3.14**.
Cela inclut `pydantic.v1`, qui n'est plus pris en charge en Python 3.14 et versions ultérieures.
Si vous souhaitez utiliser les dernières fonctionnalités de Python, vous devez vous assurer que vous utilisez Pydantic v2.
///
Si vous avez une ancienne application FastAPI avec Pydantic v1, je vais vous montrer comment la migrer vers Pydantic v2, et les **fonctionnalités de FastAPI 0.119.0** pour vous aider à une migration progressive.
## Guide officiel { #official-guide }
Pydantic propose un [Guide de migration](https://docs.pydantic.dev/latest/migration/) officiel de la v1 à la v2.
Il inclut aussi ce qui a changé, comment les validations sont désormais plus correctes et strictes, les pièges possibles, etc.
Vous pouvez le lire pour mieux comprendre ce qui a changé.
## Tests { #tests }
Vous devez vous assurer d'avoir des [tests](../tutorial/testing.md) pour votre application et de les exécuter en intégration continue (CI).
De cette façon, vous pouvez effectuer la mise à niveau et vous assurer que tout fonctionne toujours comme prévu.
## `bump-pydantic` { #bump-pydantic }
Dans de nombreux cas, lorsque vous utilisez des modèles Pydantic classiques sans personnalisations, vous pourrez automatiser la majeure partie du processus de migration de Pydantic v1 à Pydantic v2.
Vous pouvez utiliser [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) de la même équipe Pydantic.
Cet outil vous aidera à modifier automatiquement la majeure partie du code à adapter.
Après cela, vous pouvez exécuter les tests et vérifier que tout fonctionne. Si c'est le cas, vous avez terminé. 😎
## Pydantic v1 dans v2 { #pydantic-v1-in-v2 }
Pydantic v2 inclut tout Pydantic v1 sous la forme du sous-module `pydantic.v1`. Mais cela n'est plus pris en charge dans les versions au-delà de Python 3.13.
Cela signifie que vous pouvez installer la dernière version de Pydantic v2 et importer/utiliser les anciens composants de Pydantic v1 depuis ce sous-module, comme si vous aviez l'ancien Pydantic v1 installé.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Prise en charge de FastAPI pour Pydantic v1 dans v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Depuis FastAPI 0.119.0, il existe également une prise en charge partielle de Pydantic v1 depuis l'intérieur de Pydantic v2, pour faciliter la migration vers v2.
Vous pouvez donc mettre à niveau Pydantic vers la dernière version 2 et modifier les imports pour utiliser le sous-module `pydantic.v1`, et dans de nombreux cas cela fonctionnera tel quel.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Alertes
Gardez à l'esprit que, puisque l'équipe Pydantic ne prend plus en charge Pydantic v1 dans les versions récentes de Python à partir de Python 3.14, l'utilisation de `pydantic.v1` n'est pas non plus prise en charge en Python 3.14 et versions ultérieures.
///
### Pydantic v1 et v2 dans la même application { #pydantic-v1-and-v2-on-the-same-app }
Pydantic ne prend pas en charge le fait d'avoir un modèle Pydantic v2 contenant des champs eux-mêmes définis comme des modèles Pydantic v1, et inversement.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
... mais vous pouvez avoir des modèles séparés utilisant Pydantic v1 et v2 dans la même application.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
Dans certains cas, il est même possible d'avoir des modèles Pydantic v1 et v2 dans le même **chemin d'accès** de votre application FastAPI :
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
Dans l'exemple ci-dessus, le modèle d'entrée est un modèle Pydantic v1 et le modèle de sortie (défini dans `response_model=ItemV2`) est un modèle Pydantic v2.
### Paramètres Pydantic v1 { #pydantic-v1-parameters }
Si vous devez utiliser certains des outils spécifiques à FastAPI pour les paramètres comme `Body`, `Query`, `Form`, etc., avec des modèles Pydantic v1, vous pouvez les importer depuis `fastapi.temp_pydantic_v1_params` le temps de terminer la migration vers Pydantic v2 :
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migrer par étapes { #migrate-in-steps }
/// tip | Astuce
Essayez d'abord avec `bump-pydantic` ; si vos tests passent et que cela fonctionne, vous avez tout terminé en une seule commande. ✨
///
Si `bump-pydantic` ne fonctionne pas pour votre cas d'usage, vous pouvez utiliser la prise en charge des modèles Pydantic v1 et v2 dans la même application pour effectuer la migration vers Pydantic v2 progressivement.
Vous pouvez d'abord mettre à niveau Pydantic pour utiliser la dernière version 2 et modifier les imports pour utiliser `pydantic.v1` pour tous vos modèles.
Ensuite, vous pouvez commencer à migrer vos modèles de Pydantic v1 vers v2 par groupes, par étapes progressives. 🚶
================================================
FILE: docs/fr/docs/how-to/separate-openapi-schemas.md
================================================
# Séparer les schémas OpenAPI pour l'entrée et la sortie ou non { #separate-openapi-schemas-for-input-and-output-or-not }
Depuis la sortie de **Pydantic v2**, l'OpenAPI généré est un peu plus précis et **correct** qu'avant. 😎
En fait, dans certains cas, il y aura même **deux schémas JSON** dans OpenAPI pour le même modèle Pydantic, pour l'entrée et pour la sortie, selon s'ils ont des **valeurs par défaut**.
Voyons comment cela fonctionne et comment le modifier si vous devez le faire.
## Utiliser des modèles Pydantic pour l'entrée et la sortie { #pydantic-models-for-input-and-output }
Supposons que vous ayez un modèle Pydantic avec des valeurs par défaut, comme celui‑ci :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modèle pour l'entrée { #model-for-input }
Si vous utilisez ce modèle en entrée comme ici :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
... alors, le champ `description` ne sera **pas requis**. Parce qu'il a une valeur par défaut de `None`.
### Modèle d'entrée dans les documents { #input-model-in-docs }
Vous pouvez le confirmer dans les documents, le champ `description` n'a pas d'**astérisque rouge**, il n'est pas indiqué comme requis :
### Modèle pour la sortie { #model-for-output }
Mais si vous utilisez le même modèle en sortie, comme ici :
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
... alors, comme `description` a une valeur par défaut, si vous ne retournez rien pour ce champ, il aura tout de même cette **valeur par défaut**.
### Modèle pour les données de réponse en sortie { #model-for-output-response-data }
Si vous interagissez avec les documents et vérifiez la réponse, même si le code n'a rien ajouté dans l'un des champs `description`, la réponse JSON contient la valeur par défaut (`null`) :
Cela signifie qu'il aura **toujours une valeur**, simplement, parfois la valeur pourra être `None` (ou `null` en JSON).
Cela signifie que les clients utilisant votre API n'ont pas à vérifier si la valeur existe ou non, ils peuvent **supposer que le champ sera toujours présent**, mais que, dans certains cas, il aura la valeur par défaut `None`.
La manière de décrire cela dans OpenAPI est de marquer ce champ comme **requis**, car il sera toujours présent.
Pour cette raison, le schéma JSON d'un modèle peut être différent selon qu'il est utilisé pour **l'entrée ou la sortie** :
- pour **l'entrée**, `description` ne sera **pas requis**
- pour **la sortie**, il sera **requis** (et éventuellement `None`, ou en termes JSON, `null`)
### Modèle de sortie dans les documents { #model-for-output-in-docs }
Vous pouvez également vérifier le modèle de sortie dans les documents, **à la fois** `name` et `description` sont marqués comme **requis** avec un **astérisque rouge** :
### Modèle pour l'entrée et la sortie dans les documents { #model-for-input-and-output-in-docs }
Et si vous consultez tous les schémas disponibles (schémas JSON) dans OpenAPI, vous verrez qu'il y en a deux, un `Item-Input` et un `Item-Output`.
Pour `Item-Input`, `description` n'est **pas requis**, il n'a pas d'astérisque rouge.
Mais pour `Item-Output`, `description` est **requis**, il a un astérisque rouge.
Avec cette fonctionnalité de **Pydantic v2**, la documentation de votre API est plus **précise**, et si vous avez des clients et SDKs générés automatiquement, ils seront eux aussi plus précis, avec une meilleure **expérience développeur** et davantage de cohérence. 🎉
## Ne pas séparer les schémas { #do-not-separate-schemas }
Il existe des cas où vous pourriez vouloir avoir le **même schéma pour l'entrée et la sortie**.
Le cas d'usage principal est probablement que vous avez déjà du code client/SDKs générés automatiquement et que vous ne souhaitez pas encore mettre à jour tout ce code client/ces SDKs générés automatiquement ; vous le ferez sans doute à un moment donné, mais peut‑être pas tout de suite.
Dans ce cas, vous pouvez désactiver cette fonctionnalité dans **FastAPI**, avec le paramètre `separate_input_output_schemas=False`.
/// info | info
La prise en charge de `separate_input_output_schemas` a été ajoutée dans FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Utiliser le même schéma pour les modèles d'entrée et de sortie dans les documents { #same-schema-for-input-and-output-models-in-docs }
Désormais, il n'y aura qu'un seul schéma pour l'entrée et la sortie du modèle, uniquement `Item`, et `description` ne sera pas requis :
================================================
FILE: docs/fr/docs/how-to/testing-database.md
================================================
# Tester une base de données { #testing-a-database }
Vous pouvez étudier les bases de données, SQL et SQLModel dans les [documents SQLModel](https://sqlmodel.tiangolo.com/). 🤓
Il existe un mini [tutoriel sur l'utilisation de SQLModel avec FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
Ce tutoriel comprend une section sur les [tests des bases de données SQL](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/fr/docs/index.md
================================================
# FastAPI { #fastapi }
Framework FastAPI, haute performance, facile à apprendre, rapide à coder, prêt pour la production
---
**Documentation** : [https://fastapi.tiangolo.com/fr](https://fastapi.tiangolo.com/fr)
**Code Source** : [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI est un framework web moderne et rapide (haute performance) pour la création d'API avec Python, basé sur les annotations de type standard de Python.
Les principales fonctionnalités sont :
* **Rapide** : très hautes performances, au niveau de **NodeJS** et **Go** (grâce à Starlette et Pydantic). [L'un des frameworks Python les plus rapides](#performance).
* **Rapide à coder** : augmente la vitesse de développement des fonctionnalités d'environ 200 % à 300 %. *
* **Moins de bugs** : réduit d'environ 40 % les erreurs induites par le développeur. *
* **Intuitif** : excellente compatibilité avec les éditeurs. Autocomplétion partout. Moins de temps passé à déboguer.
* **Facile** : conçu pour être facile à utiliser et à apprendre. Moins de temps passé à lire les documents.
* **Concis** : diminue la duplication de code. Plusieurs fonctionnalités à partir de chaque déclaration de paramètre. Moins de bugs.
* **Robuste** : obtenez un code prêt pour la production. Avec une documentation interactive automatique.
* **Basé sur des normes** : basé sur (et entièrement compatible avec) les standards ouverts pour les APIs : [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (précédemment connu sous le nom de Swagger) et [JSON Schema](https://json-schema.org/).
* estimation basée sur des tests d'une équipe de développement interne, construisant des applications de production.
## Sponsors { #sponsors }
### Sponsor clé de voûte { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Sponsors Or et Argent { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Autres sponsors](https://fastapi.tiangolo.com/fr/fastapi-people/#sponsors)
## Opinions { #opinions }
« _[...] J'utilise beaucoup **FastAPI** ces derniers temps. [...] Je prévois de l'utiliser dans mon équipe pour tous les **services de ML chez Microsoft**. Certains d'entre eux sont intégrés au cœur de **Windows** et à certains produits **Office**._ »
---
« _Nous avons adopté la bibliothèque **FastAPI** pour créer un serveur **REST** qui peut être interrogé pour obtenir des **prédictions**. [pour Ludwig]_ »
Piero Molino, Yaroslav Dudin, et Sai Sumanth Miryala - Uber(ref)
---
« _**Netflix** est heureux d'annoncer la publication en open source de notre framework d'orchestration de **gestion de crise** : **Dispatch** ! [construit avec **FastAPI**]_ »
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
« _Je suis plus qu'enthousiaste à propos de **FastAPI**. C'est tellement fun !_ »
Brian Okken - Animateur du podcast [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855)(ref)
---
« _Honnêtement, ce que vous avez construit a l'air super solide et soigné. À bien des égards, c'est ce que je voulais que **Hug** soit — c'est vraiment inspirant de voir quelqu'un construire ça._ »
Timothy Crosley - Créateur de [Hug](https://github.com/hugapi/hug)(ref)
---
« _Si vous cherchez à apprendre un **framework moderne** pour créer des APIs REST, regardez **FastAPI** [...] C'est rapide, facile à utiliser et facile à apprendre [...]_ »
« _Nous sommes passés à **FastAPI** pour nos **APIs** [...] Je pense que vous l'aimerez [...]_ »
Ines Montani - Matthew Honnibal - Fondateurs de [Explosion AI](https://explosion.ai) - Créateurs de [spaCy](https://spacy.io)(ref) - (ref)
---
« _Si quelqu'un cherche à construire une API Python de production, je recommande vivement **FastAPI**. Il est **magnifiquement conçu**, **simple à utiliser** et **hautement scalable**. Il est devenu un **composant clé** de notre stratégie de développement API-first et alimente de nombreuses automatisations et services tels que notre ingénieur TAC virtuel._ »
---
## Mini documentaire FastAPI { #fastapi-mini-documentary }
Un [mini documentaire FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE) est sorti fin 2025, vous pouvez le regarder en ligne :
## **Typer**, le FastAPI des CLIs { #typer-the-fastapi-of-clis }
Si vous construisez une application CLI à utiliser dans un terminal au lieu d'une API web, regardez [**Typer**](https://typer.tiangolo.com/).
**Typer** est le petit frère de FastAPI. Et il est destiné à être le **FastAPI des CLIs**. ⌨️ 🚀
## Prérequis { #requirements }
FastAPI repose sur les épaules de géants :
* [Starlette](https://www.starlette.dev/) pour les parties web.
* [Pydantic](https://docs.pydantic.dev/) pour les parties données.
## Installation { #installation }
Créez et activez un [environnement virtuel](https://fastapi.tiangolo.com/fr/virtual-environments/) puis installez FastAPI :
**Remarque** : Vous devez vous assurer de mettre « fastapi[standard] » entre guillemets pour garantir que cela fonctionne dans tous les terminaux.
## Exemple { #example }
### Créer { #create-it }
Créez 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Ou utilisez async def...
Si votre code utilise `async` / `await`, utilisez `async def` :
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Remarque** :
Si vous ne savez pas, consultez la section « Vous êtes pressés ? » à propos de [`async` et `await` dans la documentation](https://fastapi.tiangolo.com/fr/async/#in-a-hurry).
### Lancer { #run-it }
Lancez le serveur avec :
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
À propos de la commande fastapi dev...
La commande `fastapi dev` lit automatiquement votre fichier `main.py`, détecte l'application **FastAPI** qu'il contient et lance un serveur avec [Uvicorn](https://www.uvicorn.dev).
Par défaut, `fastapi dev` démarre avec le rechargement automatique activé pour le développement local.
Vous pouvez en savoir plus dans la [documentation de la CLI FastAPI](https://fastapi.tiangolo.com/fr/fastapi-cli/).
### Vérifier { #check-it }
Ouvrez votre navigateur à l'adresse [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Vous verrez la réponse JSON :
```JSON
{"item_id": 5, "q": "somequery"}
```
Vous avez déjà créé une API qui :
* Reçoit des requêtes HTTP sur les _chemins_ `/` et `/items/{item_id}`.
* Les deux _chemins_ acceptent des opérations `GET` (également connues sous le nom de _méthodes_ HTTP).
* Le _chemin_ `/items/{item_id}` a un _paramètre de chemin_ `item_id` qui doit être un `int`.
* Le _chemin_ `/items/{item_id}` a un _paramètre de requête_ optionnel `q` de type `str`.
### Documentation API interactive { #interactive-api-docs }
Maintenant, rendez-vous sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez la documentation interactive automatique de l'API (fournie par [Swagger UI](https://github.com/swagger-api/swagger-ui)) :

### Documentation API alternative { #alternative-api-docs }
Et maintenant, rendez-vous sur [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Vous verrez la documentation alternative automatique (fournie par [ReDoc](https://github.com/Rebilly/ReDoc)) :

## Mettre à niveau l'exemple { #example-upgrade }
Modifiez maintenant le fichier `main.py` pour recevoir un corps depuis une requête `PUT`.
Déclarez le corps en utilisant les types Python standard, grâce à Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Le serveur `fastapi dev` devrait se recharger automatiquement.
### Mettre à niveau la documentation API interactive { #interactive-api-docs-upgrade }
Maintenant, rendez-vous sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* La documentation interactive de l'API sera automatiquement mise à jour, y compris le nouveau corps :

* Cliquez sur le bouton « Try it out », il vous permet de renseigner les paramètres et d'interagir directement avec l'API :

* Cliquez ensuite sur le bouton « Execute », l'interface utilisateur communiquera avec votre API, enverra les paramètres, obtiendra les résultats et les affichera à l'écran :

### Mettre à niveau la documentation API alternative { #alternative-api-docs-upgrade }
Et maintenant, rendez-vous sur [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* La documentation alternative reflètera également le nouveau paramètre de requête et le nouveau corps :

### En résumé { #recap }
En résumé, vous déclarez **une fois** les types de paramètres, le corps, etc. en tant que paramètres de fonction.
Vous faites cela avec les types Python standard modernes.
Vous n'avez pas à apprendre une nouvelle syntaxe, les méthodes ou les classes d'une bibliothèque spécifique, etc.
Juste du **Python** standard.
Par exemple, pour un `int` :
```Python
item_id: int
```
ou pour un modèle `Item` plus complexe :
```Python
item: Item
```
... et avec cette déclaration unique, vous obtenez :
* Une assistance dans l'éditeur, notamment :
* l'autocomplétion.
* la vérification des types.
* La validation des données :
* des erreurs automatiques et claires lorsque les données ne sont pas valides.
* une validation même pour les objets JSON profondément imbriqués.
* Conversion des données d'entrée : venant du réseau vers les données et types Python. Lecture depuis :
* JSON.
* Paramètres de chemin.
* Paramètres de requête.
* Cookies.
* En-têtes.
* Formulaires.
* Fichiers.
* Conversion des données de sortie : conversion des données et types Python en données réseau (au format JSON) :
* Conversion des types Python (`str`, `int`, `float`, `bool`, `list`, etc).
* Objets `datetime`.
* Objets `UUID`.
* Modèles de base de données.
* ... et bien plus.
* Documentation API interactive automatique, avec 2 interfaces utilisateur au choix :
* Swagger UI.
* ReDoc.
---
Pour revenir à l'exemple de code précédent, **FastAPI** va :
* Valider la présence d'un `item_id` dans le chemin pour les requêtes `GET` et `PUT`.
* Valider que `item_id` est de type `int` pour les requêtes `GET` et `PUT`.
* Si ce n'est pas le cas, le client verra une erreur utile et claire.
* Vérifier s'il existe un paramètre de requête optionnel nommé `q` (comme dans `http://127.0.0.1:8000/items/foo?q=somequery`) pour les requêtes `GET`.
* Comme le paramètre `q` est déclaré avec `= None`, il est optionnel.
* Sans le `None`, il serait requis (comme l'est le corps dans le cas de `PUT`).
* Pour les requêtes `PUT` vers `/items/{item_id}`, lire le corps au format JSON :
* Vérifier qu'il a un attribut obligatoire `name` qui doit être un `str`.
* Vérifier qu'il a un attribut obligatoire `price` qui doit être un `float`.
* Vérifier qu'il a un attribut optionnel `is_offer`, qui doit être un `bool`, s'il est présent.
* Tout cela fonctionne également pour les objets JSON profondément imbriqués.
* Convertir automatiquement depuis et vers JSON.
* Tout documenter avec OpenAPI, qui peut être utilisé par :
* des systèmes de documentation interactive.
* des systèmes de génération automatique de clients, pour de nombreux langages.
* Fournir directement 2 interfaces web de documentation interactive.
---
Nous n'avons fait qu'effleurer la surface, mais vous avez déjà une idée de la façon dont tout fonctionne.
Essayez de changer la ligne contenant :
```Python
return {"item_name": item.name, "item_id": item_id}
```
... de :
```Python
... "item_name": item.name ...
```
... à :
```Python
... "item_price": item.price ...
```
... et voyez comment votre éditeur complète automatiquement les attributs et connaît leurs types :

Pour un exemple plus complet comprenant plus de fonctionnalités, voir le Tutoriel - Guide utilisateur.
**Alerte spoiler** : le tutoriel - guide utilisateur inclut :
* Déclaration de **paramètres** provenant d'autres emplacements comme : **en-têtes**, **cookies**, **champs de formulaire** et **fichiers**.
* Comment définir des **contraintes de validation** comme `maximum_length` ou `regex`.
* Un système **d'injection de dépendances** très puissant et facile à utiliser.
* Sécurité et authentification, y compris la prise en charge de **OAuth2** avec des **JWT tokens** et l'authentification **HTTP Basic**.
* Des techniques plus avancées (mais tout aussi faciles) pour déclarer des **modèles JSON profondément imbriqués** (grâce à Pydantic).
* Intégration **GraphQL** avec [Strawberry](https://strawberry.rocks) et d'autres bibliothèques.
* De nombreuses fonctionnalités supplémentaires (grâce à Starlette) comme :
* **WebSockets**
* des tests extrêmement faciles basés sur HTTPX et `pytest`
* **CORS**
* **Cookie Sessions**
* ... et plus encore.
### Déployer votre application (optionnel) { #deploy-your-app-optional }
Vous pouvez, si vous le souhaitez, déployer votre application FastAPI sur [FastAPI Cloud](https://fastapicloud.com), allez vous inscrire sur la liste d'attente si ce n'est pas déjà fait. 🚀
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste d'attente 😉), vous pouvez déployer votre application avec une seule commande.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
C'est tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
#### À propos de FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** est construit par le même auteur et la même équipe derrière **FastAPI**.
Il simplifie le processus de **construction**, de **déploiement** et **d'accès** à une API avec un effort minimal.
Il apporte la même **expérience développeur** de la création d'applications avec FastAPI au **déploiement** dans le cloud. 🎉
FastAPI Cloud est le principal sponsor et financeur des projets open source *FastAPI and friends*. ✨
#### Déployer sur d'autres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI sur n'importe quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
## Performance { #performance }
Les benchmarks TechEmpower indépendants montrent que les applications **FastAPI** s'exécutant sous Uvicorn sont [parmi les frameworks Python les plus rapides](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), juste derrière Starlette et Uvicorn eux-mêmes (utilisés en interne par FastAPI). (*)
Pour en savoir plus, consultez la section [Benchmarks](https://fastapi.tiangolo.com/fr/benchmarks/).
## Dépendances { #dependencies }
FastAPI dépend de Pydantic et Starlette.
### Dépendances `standard` { #standard-dependencies }
Lorsque vous installez FastAPI avec `pip install "fastapi[standard]"`, il inclut le groupe `standard` de dépendances optionnelles :
Utilisées par Pydantic :
* [`email-validator`](https://github.com/JoshData/python-email-validator) - pour la validation des adresses e-mail.
Utilisées par Starlette :
* [`httpx`](https://www.python-httpx.org) - Obligatoire si vous souhaitez utiliser le `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - Obligatoire si vous souhaitez utiliser la configuration de template par défaut.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - Obligatoire si vous souhaitez prendre en charge l’« parsing » de formulaires avec `request.form()`.
Utilisées par FastAPI :
* [`uvicorn`](https://www.uvicorn.dev) - pour le serveur qui charge et sert votre application. Cela inclut `uvicorn[standard]`, qui comprend certaines dépendances (par ex. `uvloop`) nécessaires pour une haute performance.
* `fastapi-cli[standard]` - pour fournir la commande `fastapi`.
* Cela inclut `fastapi-cloud-cli`, qui vous permet de déployer votre application FastAPI sur [FastAPI Cloud](https://fastapicloud.com).
### Sans les dépendances `standard` { #without-standard-dependencies }
Si vous ne souhaitez pas inclure les dépendances optionnelles `standard`, vous pouvez installer avec `pip install fastapi` au lieu de `pip install "fastapi[standard]"`.
### Sans `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Si vous souhaitez installer FastAPI avec les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Dépendances optionnelles supplémentaires { #additional-optional-dependencies }
Il existe des dépendances supplémentaires que vous pourriez vouloir installer.
Dépendances optionnelles supplémentaires pour Pydantic :
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - pour la gestion des paramètres.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - pour des types supplémentaires à utiliser avec Pydantic.
Dépendances optionnelles supplémentaires pour FastAPI :
* [`orjson`](https://github.com/ijl/orjson) - Obligatoire si vous souhaitez utiliser `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - Obligatoire si vous souhaitez utiliser `UJSONResponse`.
## Licence { #license }
Ce projet est soumis aux termes de la licence MIT.
================================================
FILE: docs/fr/docs/learn/index.md
================================================
# Apprendre { #learn }
Voici les sections introductives et les tutoriels pour apprendre **FastAPI**.
Vous pouvez considérer ceci comme un **livre**, un **cours**, la méthode **officielle** et recommandée pour apprendre FastAPI. 😎
================================================
FILE: docs/fr/docs/project-generation.md
================================================
# Modèle Full Stack FastAPI { #full-stack-fastapi-template }
Les modèles, bien qu'ils soient généralement livrés avec une configuration spécifique, sont conçus pour être flexibles et personnalisables. Cela vous permet de les modifier et de les adapter aux exigences de votre projet, ce qui en fait un excellent point de départ. 🏁
Vous pouvez utiliser ce modèle pour démarrer, car il inclut une grande partie de la configuration initiale, la sécurité, la base de données et quelques endpoints d'API déjà prêts pour vous.
Dépôt GitHub : [Modèle Full Stack FastAPI](https://github.com/tiangolo/full-stack-fastapi-template)
## Modèle Full Stack FastAPI - Pile technologique et fonctionnalités { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/fr) pour l'API backend Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) pour les interactions avec la base de données SQL en Python (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), utilisé par FastAPI, pour la validation des données et la gestion des paramètres.
- 💾 [PostgreSQL](https://www.postgresql.org) comme base de données SQL.
- 🚀 [React](https://react.dev) pour le frontend.
- 💃 Utilisation de TypeScript, des hooks, de Vite et d'autres éléments d'un stack frontend moderne.
- 🎨 [Tailwind CSS](https://tailwindcss.com) et [shadcn/ui](https://ui.shadcn.com) pour les composants frontend.
- 🤖 Un client frontend généré automatiquement.
- 🧪 [Playwright](https://playwright.dev) pour les tests de bout en bout.
- 🦇 Prise en charge du mode sombre.
- 🐋 [Docker Compose](https://www.docker.com) pour le développement et la production.
- 🔒 Hachage sécurisé des mots de passe par défaut.
- 🔑 Authentification JWT (JSON Web Token).
- 📫 Récupération de mot de passe par e-mail.
- ✅ Tests avec [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) comme proxy inverse / répartiteur de charge.
- 🚢 Instructions de déploiement avec Docker Compose, y compris la configuration d'un proxy Traefik frontal pour gérer les certificats HTTPS automatiques.
- 🏭 CI (intégration continue) et CD (déploiement continu) basés sur GitHub Actions.
================================================
FILE: docs/fr/docs/python-types.md
================================================
# Introduction aux types Python { #python-types-intro }
Python prend en charge des « annotations de type » (aussi appelées « type hints ») facultatives.
Ces **« annotations de type »** sont une syntaxe spéciale qui permet de déclarer le type d'une variable.
En déclarant les types de vos variables, les éditeurs et outils peuvent vous offrir un meilleur support.
Ceci est un **tutoriel rapide / rappel** à propos des annotations de type Python. Il couvre uniquement le minimum nécessaire pour les utiliser avec **FastAPI** ... ce qui est en réalité très peu.
**FastAPI** est totalement basé sur ces annotations de type, elles lui donnent de nombreux avantages et bénéfices.
Mais même si vous n'utilisez jamais **FastAPI**, vous auriez intérêt à en apprendre un peu à leur sujet.
/// note | Remarque
Si vous êtes un expert Python, et que vous savez déjà tout sur les annotations de type, passez au chapitre suivant.
///
## Motivation { #motivation }
Commençons par un exemple simple :
{* ../../docs_src/python_types/tutorial001_py310.py *}
Exécuter ce programme affiche :
```
John Doe
```
La fonction fait ce qui suit :
* Prend un `first_name` et un `last_name`.
* Convertit la première lettre de chacun en majuscule avec `title()`.
* Concatène ces deux valeurs avec un espace au milieu.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Modifier le code { #edit-it }
C'est un programme très simple.
Mais maintenant imaginez que vous l'écriviez de zéro.
À un certain moment, vous auriez commencé la définition de la fonction, vous aviez les paramètres prêts ...
Mais ensuite vous devez appeler « cette méthode qui convertit la première lettre en majuscule ».
Était-ce `upper` ? Était-ce `uppercase` ? `first_uppercase` ? `capitalize` ?
Vous essayez alors avec l'ami de toujours des programmeurs, l'autocomplétion de l'éditeur.
Vous tapez le premier paramètre de la fonction, `first_name`, puis un point (`.`) et appuyez sur `Ctrl+Espace` pour déclencher l'autocomplétion.
Mais, malheureusement, vous n'obtenez rien d'utile :
### Ajouter des types { #add-types }
Modifions une seule ligne de la version précédente.
Nous allons changer exactement ce fragment, les paramètres de la fonction, de :
```Python
first_name, last_name
```
à :
```Python
first_name: str, last_name: str
```
C'est tout.
Ce sont les « annotations de type » :
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Ce n'est pas la même chose que de déclarer des valeurs par défaut, ce qui serait :
```Python
first_name="john", last_name="doe"
```
C'est différent.
Nous utilisons des deux-points (`:`), pas des signes égal (`=`).
Et ajouter des annotations de type ne change normalement pas ce qui se passe par rapport à ce qui se passerait sans elles.
Mais maintenant, imaginez que vous êtes à nouveau en train de créer cette fonction, mais avec des annotations de type.
Au même moment, vous essayez de déclencher l'autocomplétion avec `Ctrl+Espace` et vous voyez :
Avec cela, vous pouvez faire défiler en voyant les options, jusqu'à trouver celle qui « vous dit quelque chose » :
## Plus de motivation { #more-motivation }
Regardez cette fonction, elle a déjà des annotations de type :
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Comme l'éditeur connaît les types des variables, vous n'obtenez pas seulement l'autocomplétion, vous obtenez aussi des vérifications d'erreurs :
Vous savez maintenant qu'il faut corriger, convertir `age` en chaîne avec `str(age)` :
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Déclarer des types { #declaring-types }
Vous venez de voir l'endroit principal pour déclarer des annotations de type : dans les paramètres des fonctions.
C'est aussi l'endroit principal où vous les utiliserez avec **FastAPI**.
### Types simples { #simple-types }
Vous pouvez déclarer tous les types standards de Python, pas seulement `str`.
Vous pouvez utiliser, par exemple :
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Module `typing` { #typing-module }
Pour certains cas d'utilisation supplémentaires, vous pourriez avoir besoin d'importer certains éléments depuis le module standard `typing`, par exemple lorsque vous voulez déclarer que quelque chose a « n'importe quel type », vous pouvez utiliser `Any` depuis `typing` :
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Types génériques { #generic-types }
Certains types peuvent prendre des « paramètres de type » entre crochets, pour définir leurs types internes, par exemple une « liste de chaînes » se déclarerait `list[str]`.
Ces types qui peuvent prendre des paramètres de type sont appelés des **types génériques** ou **Generics**.
Vous pouvez utiliser les mêmes types intégrés comme génériques (avec des crochets et des types à l'intérieur) :
* `list`
* `tuple`
* `set`
* `dict`
#### Liste { #list }
Par exemple, définissons une variable comme une `list` de `str`.
Déclarez la variable, en utilisant la même syntaxe avec deux-points (`:`).
Comme type, mettez `list`.
Comme la liste est un type qui contient des types internes, mettez-les entre crochets :
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info
Ces types internes entre crochets sont appelés « paramètres de type ».
Dans ce cas, `str` est le paramètre de type passé à `list`.
///
Cela signifie : « la variable `items` est une `list`, et chacun des éléments de cette liste est un `str` ».
En faisant cela, votre éditeur peut vous fournir de l'aide même pendant le traitement des éléments de la liste :
Sans types, c'est presque impossible à réaliser.
Remarquez que la variable `item` est l'un des éléments de la liste `items`.
Et pourtant, l'éditeur sait que c'est un `str` et fournit le support approprié.
#### Tuple et Set { #tuple-and-set }
Vous feriez la même chose pour déclarer des `tuple` et des `set` :
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Cela signifie :
* La variable `items_t` est un `tuple` avec 3 éléments, un `int`, un autre `int`, et un `str`.
* La variable `items_s` est un `set`, et chacun de ses éléments est de type `bytes`.
#### Dict { #dict }
Pour définir un `dict`, vous passez 2 paramètres de type, séparés par des virgules.
Le premier paramètre de type est pour les clés du `dict`.
Le second paramètre de type est pour les valeurs du `dict` :
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Cela signifie :
* La variable `prices` est un `dict` :
* Les clés de ce `dict` sont de type `str` (disons, le nom de chaque article).
* Les valeurs de ce `dict` sont de type `float` (disons, le prix de chaque article).
#### Union { #union }
Vous pouvez déclarer qu'une variable peut être **plusieurs types**, par exemple, un `int` ou un `str`.
Pour le définir, vous utilisez la barre verticale (`|`) pour séparer les deux types.
C'est ce qu'on appelle une « union », car la variable peut être n'importe quoi dans l'union de ces deux ensembles de types.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Cela signifie que `item` peut être un `int` ou un `str`.
#### Possiblement `None` { #possibly-none }
Vous pouvez déclarer qu'une valeur peut avoir un type, comme `str`, mais qu'elle peut aussi être `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Utiliser `str | None` au lieu de simplement `str` permettra à l'éditeur de vous aider à détecter des erreurs où vous supposeriez qu'une valeur est toujours un `str`, alors qu'elle pourrait en fait aussi être `None`.
### Classes en tant que types { #classes-as-types }
Vous pouvez aussi déclarer une classe comme type d'une variable.
Disons que vous avez une classe `Person`, avec un nom :
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Vous pouvez ensuite déclarer une variable de type `Person` :
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
Et là encore, vous obtenez tout le support de l'éditeur :
Remarquez que cela signifie « `one_person` est une **instance** de la classe `Person` ».
Cela ne signifie pas « `one_person` est la **classe** appelée `Person` ».
## Modèles Pydantic { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) est une bibliothèque Python pour effectuer de la validation de données.
Vous déclarez la « forme » de la donnée sous forme de classes avec des attributs.
Et chaque attribut a un type.
Ensuite, vous créez une instance de cette classe avec certaines valeurs et elle validera les valeurs, les convertira dans le type approprié (le cas échéant) et vous donnera un objet avec toutes les données.
Et vous obtenez tout le support de l'éditeur avec cet objet résultant.
Un exemple tiré de la documentation officielle de Pydantic :
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info
Pour en savoir plus à propos de [Pydantic, consultez sa documentation](https://docs.pydantic.dev/).
///
**FastAPI** est entièrement basé sur Pydantic.
Vous verrez beaucoup plus de tout cela en pratique dans le [Tutoriel - Guide utilisateur](tutorial/index.md).
## Annotations de type avec métadonnées { #type-hints-with-metadata-annotations }
Python dispose également d'une fonctionnalité qui permet de mettre des **métadonnées supplémentaires** dans ces annotations de type en utilisant `Annotated`.
Vous pouvez importer `Annotated` depuis `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python lui-même ne fait rien avec ce `Annotated`. Et pour les éditeurs et autres outils, le type est toujours `str`.
Mais vous pouvez utiliser cet espace dans `Annotated` pour fournir à **FastAPI** des métadonnées supplémentaires sur la façon dont vous voulez que votre application se comporte.
L'important à retenir est que **le premier « paramètre de type »** que vous passez à `Annotated` est le **type réel**. Le reste n'est que des métadonnées pour d'autres outils.
Pour l'instant, vous avez juste besoin de savoir que `Annotated` existe, et que c'est du Python standard. 😎
Plus tard, vous verrez à quel point cela peut être **puissant**.
/// tip | Astuce
Le fait que ce soit du **Python standard** signifie que vous bénéficierez toujours de la **meilleure expérience développeur possible** dans votre éditeur, avec les outils que vous utilisez pour analyser et refactoriser votre code, etc. ✨
Et aussi que votre code sera très compatible avec de nombreux autres outils et bibliothèques Python. 🚀
///
## Annotations de type dans **FastAPI** { #type-hints-in-fastapi }
**FastAPI** tire parti de ces annotations de type pour faire plusieurs choses.
Avec **FastAPI**, vous déclarez des paramètres avec des annotations de type et vous obtenez :
* **Du support de l'éditeur**.
* **Des vérifications de types**.
... et **FastAPI** utilise les mêmes déclarations pour :
* **Définir des prérequis** : à partir des paramètres de chemin de la requête, des paramètres de requête, des en-têtes, des corps, des dépendances, etc.
* **Convertir des données** : de la requête vers le type requis.
* **Valider des données** : provenant de chaque requête :
* En générant des **erreurs automatiques** renvoyées au client lorsque la donnée est invalide.
* **Documenter** l'API avec OpenAPI :
* ce qui est ensuite utilisé par les interfaces utilisateur de documentation interactive automatiques.
Tout cela peut sembler abstrait. Ne vous inquiétez pas. Vous verrez tout cela en action dans le [Tutoriel - Guide utilisateur](tutorial/index.md).
L'important est qu'en utilisant les types standards de Python, en un seul endroit (au lieu d'ajouter plus de classes, de décorateurs, etc.), **FastAPI** fera une grande partie du travail pour vous.
/// info
Si vous avez déjà parcouru tout le tutoriel et êtes revenu pour en voir plus sur les types, une bonne ressource est [l'« aide-mémoire » de `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/fr/docs/resources/index.md
================================================
# Ressources { #resources }
Ressources supplémentaires, liens externes et plus encore. ✈️
================================================
FILE: docs/fr/docs/translation-banner.md
================================================
/// details | 🌐 Traduction par IA et humains
Cette traduction a été réalisée par une IA guidée par des humains. 🤝
Elle peut contenir des erreurs d'interprétation du sens original, ou paraître peu naturelle, etc. 🤖
Vous pouvez améliorer cette traduction en [nous aidant à mieux guider le LLM d'IA](https://fastapi.tiangolo.com/fr/contributing/#translations).
[Version anglaise](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/fr/docs/tutorial/background-tasks.md
================================================
# Tâches d'arrière-plan { #background-tasks }
Vous pouvez définir des tâches d'arrière-plan qui seront exécutées après avoir retourné une réponse.
Ceci est utile pour les opérations qui doivent avoir lieu après une requête, mais où le client n'a pas réellement besoin d'attendre que l'opération soit terminée pour recevoir une réponse.
Cela comprend, par exemple :
* Les notifications par email envoyées après l'exécution d'une action :
* Étant donné que se connecter à un serveur et envoyer un email a tendance à être « lent » (plusieurs secondes), vous pouvez retourner la réponse directement et envoyer la notification en arrière-plan.
* Traiter des données :
* Par exemple, si vous recevez un fichier qui doit passer par un traitement lent, vous pouvez retourner une réponse « Accepted » (HTTP 202) puis faire le traitement en arrière-plan.
## Utiliser `BackgroundTasks` { #using-backgroundtasks }
Pour commencer, importez `BackgroundTasks` et définissez un paramètre dans votre *fonction de chemin d'accès* avec `BackgroundTasks` comme type déclaré.
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** créera l'objet de type `BackgroundTasks` pour vous et le passera comme paramètre.
## Créer une fonction de tâche { #create-a-task-function }
Créez une fonction à exécuter comme tâche d'arrière-plan.
Une fonction à exécuter comme tâche d'arrière-plan est juste une fonction standard qui peut recevoir des paramètres.
Elle peut être une fonction asynchrone (`async def`) ou une fonction normale (`def`), **FastAPI** saura la gérer correctement.
Dans cet exemple, la fonction de tâche écrira dans un fichier (afin de simuler un envoi d'email).
L'opération d'écriture n'utilisant ni `async` ni `await`, on définit la fonction avec un `def` normal.
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Ajouter une tâche d'arrière-plan { #add-the-background-task }
Dans votre *fonction de chemin d'accès*, passez votre fonction de tâche à l'objet de type `BackgroundTasks` (`background_tasks` ici) grâce à la méthode `.add_task()` :
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` reçoit comme arguments :
* Une fonction de tâche à exécuter en arrière-plan (`write_notification`).
* Les arguments positionnels à passer à la fonction de tâche dans l'ordre (`email`).
* Les arguments nommés à passer à la fonction de tâche (`message="some notification"`).
## Injection de dépendances { #dependency-injection }
Utiliser `BackgroundTasks` fonctionne aussi avec le système d'injection de dépendances. Vous pouvez déclarer un paramètre de type `BackgroundTasks` à différents niveaux : dans une *fonction de chemin d'accès*, dans une dépendance (dependable), dans une sous-dépendance, etc.
**FastAPI** sait quoi faire dans chaque cas et comment réutiliser le même objet, afin que toutes les tâches d'arrière-plan soient fusionnées et que les tâches soient ensuite exécutées en arrière-plan :
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
Dans cet exemple, les messages seront écrits dans le fichier `log.txt` après que la réponse soit envoyée.
S'il y avait un paramètre de requête dans la requête, alors il sera écrit dans le journal via une tâche d'arrière-plan.
Et ensuite une autre tâche d'arrière-plan (générée dans la *fonction de chemin d'accès*) écrira un message comprenant le paramètre de chemin `email`.
## Détails techniques { #technical-details }
La classe `BackgroundTasks` provient directement de [`starlette.background`](https://www.starlette.dev/background/).
Elle est importée/incluse directement dans **FastAPI** pour que vous puissiez l'importer depuis `fastapi` et éviter d'importer accidentellement `BackgroundTask` (sans `s` à la fin) depuis `starlette.background`.
En utilisant seulement `BackgroundTasks` (et non `BackgroundTask`), il est possible de l'utiliser en tant que paramètre de *fonction de chemin d'accès* et de laisser **FastAPI** gérer le reste pour vous, comme en utilisant l'objet `Request` directement.
Il est tout de même possible d'utiliser `BackgroundTask` seul dans **FastAPI**, mais dans ce cas il faut créer l'objet dans le code et renvoyer une `Response` Starlette l'incluant.
Plus de détails sont disponibles dans [la documentation officielle de Starlette sur les tâches d'arrière-plan](https://www.starlette.dev/background/).
## Avertissement { #caveat }
Si vous avez besoin de réaliser des traitements lourds en tâche d'arrière-plan et que vous n'avez pas besoin que ces traitements aient lieu dans le même process (par exemple, pas besoin de partager la mémoire, les variables, etc.), il peut s'avérer profitable d'utiliser des outils plus importants tels que [Celery](https://docs.celeryq.dev).
Ces outils nécessitent généralement des configurations plus complexes ainsi qu'un gestionnaire de queue de message, comme RabbitMQ ou Redis, mais ils permettent d'exécuter des tâches d'arrière-plan dans différents process, et surtout, sur plusieurs serveurs.
Mais si vous avez besoin d'accéder aux variables et objets de la même application **FastAPI**, ou si vous avez besoin d'effectuer de petites tâches d'arrière-plan (comme envoyer des notifications par email), vous pouvez simplement vous contenter d'utiliser `BackgroundTasks`.
## Résumé { #recap }
Importez et utilisez `BackgroundTasks` grâce aux paramètres de *fonction de chemin d'accès* et les dépendances pour ajouter des tâches d'arrière-plan.
================================================
FILE: docs/fr/docs/tutorial/bigger-applications.md
================================================
# Créer des applications plus grandes - Plusieurs fichiers { #bigger-applications-multiple-files }
Si vous créez une application ou une API web, il est rare que vous puissiez tout mettre dans un seul fichier.
**FastAPI** fournit un outil pratique pour structurer votre application tout en conservant toute la flexibilité.
/// info
Si vous venez de Flask, cela équivaut aux Blueprints de Flask.
///
## Exemple de structure de fichiers { #an-example-file-structure }
Supposons que vous ayez une structure de fichiers comme ceci :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Astuce
Il y a plusieurs fichiers `__init__.py` : un dans chaque répertoire ou sous-répertoire.
C'est cela qui permet d'importer du code d'un fichier dans un autre.
Par exemple, dans `app/main.py` vous pourriez avoir une ligne comme :
```
from app.routers import items
```
///
* Le répertoire `app` contient tout. Et il a un fichier vide `app/__init__.py`, c'est donc un « package Python » (une collection de « modules Python ») : `app`.
* Il contient un fichier `app/main.py`. Comme il se trouve dans un package Python (un répertoire avec un fichier `__init__.py`), c'est un « module » de ce package : `app.main`.
* Il y a aussi un fichier `app/dependencies.py`, tout comme `app/main.py`, c'est un « module » : `app.dependencies`.
* Il y a un sous-répertoire `app/routers/` avec un autre fichier `__init__.py`, c'est donc un « sous-package Python » : `app.routers`.
* Le fichier `app/routers/items.py` est dans un package, `app/routers/`, c'est donc un sous-module : `app.routers.items`.
* De même pour `app/routers/users.py`, c'est un autre sous-module : `app.routers.users`.
* Il y a aussi un sous-répertoire `app/internal/` avec un autre fichier `__init__.py`, c'est donc un autre « sous-package Python » : `app.internal`.
* Et le fichier `app/internal/admin.py` est un autre sous-module : `app.internal.admin`.
La même structure de fichiers avec des commentaires :
```bash
.
├── app # "app" est un package Python
│ ├── __init__.py # ce fichier fait de "app" un "package Python"
│ ├── main.py # module "main", ex. import app.main
│ ├── dependencies.py # module "dependencies", ex. import app.dependencies
│ └── routers # "routers" est un "sous-package Python"
│ │ ├── __init__.py # fait de "routers" un "sous-package Python"
│ │ ├── items.py # sous-module "items", ex. import app.routers.items
│ │ └── users.py # sous-module "users", ex. import app.routers.users
│ └── internal # "internal" est un "sous-package Python"
│ ├── __init__.py # fait de "internal" un "sous-package Python"
│ └── admin.py # sous-module "admin", ex. import app.internal.admin
```
## `APIRouter` { #apirouter }
Supposons que le fichier dédié à la gestion des utilisateurs soit le sous-module `/app/routers/users.py`.
Vous voulez séparer les *chemins d'accès* liés à vos utilisateurs du reste du code pour le garder organisé.
Mais cela fait toujours partie de la même application/API web **FastAPI** (cela fait partie du même « package Python »).
Vous pouvez créer les *chemins d'accès* pour ce module à l'aide de `APIRouter`.
### Importer `APIRouter` { #import-apirouter }
Vous l'importez et créez une « instance » de la même manière que vous le feriez avec la classe `FastAPI` :
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### Déclarer des *chemins d'accès* avec `APIRouter` { #path-operations-with-apirouter }
Puis vous l'utilisez pour déclarer vos *chemins d'accès*.
Utilisez-le de la même manière que vous utiliseriez la classe `FastAPI` :
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Vous pouvez considérer `APIRouter` comme une « mini `FastAPI` ».
Toutes les mêmes options sont prises en charge.
Tous les mêmes `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip | Astuce
Dans cet exemple, la variable s'appelle `router`, mais vous pouvez la nommer comme vous le souhaitez.
///
Nous allons inclure ce `APIRouter` dans l'application principale `FastAPI`, mais d'abord, examinons les dépendances et un autre `APIRouter`.
## Gérer les dépendances { #dependencies }
Nous voyons que nous allons avoir besoin de certaines dépendances utilisées à plusieurs endroits de l'application.
Nous les mettons donc dans leur propre module `dependencies` (`app/dependencies.py`).
Nous allons maintenant utiliser une dépendance simple pour lire un en-tête personnalisé `X-Token` :
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Astuce
Nous utilisons un en-tête inventé pour simplifier cet exemple.
Mais dans les cas réels, vous obtiendrez de meilleurs résultats en utilisant les [utilitaires de sécurité](security/index.md) intégrés.
///
## Créer un autre module avec `APIRouter` { #another-module-with-apirouter }
Supposons que vous ayez également les endpoints dédiés à la gestion des « items » de votre application dans le module `app/routers/items.py`.
Vous avez des *chemins d'accès* pour :
* `/items/`
* `/items/{item_id}`
C'est exactement la même structure que pour `app/routers/users.py`.
Mais nous voulons être plus malins et simplifier un peu le code.
Nous savons que tous les *chemins d'accès* de ce module ont les mêmes éléments :
* Préfixe de chemin `prefix` : `/items`.
* `tags` : (un seul tag : `items`).
* `responses` supplémentaires.
* `dependencies` : ils ont tous besoin de la dépendance `X-Token` que nous avons créée.
Donc, au lieu d'ajouter tout cela à chaque *chemin d'accès*, nous pouvons l'ajouter au `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Comme le chemin de chaque *chemin d'accès* doit commencer par `/`, comme dans :
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
... le préfixe ne doit pas inclure un `/` final.
Ainsi, le préfixe dans ce cas est `/items`.
Nous pouvons également ajouter une liste de `tags` et des `responses` supplémentaires qui seront appliqués à tous les *chemins d'accès* inclus dans ce routeur.
Et nous pouvons ajouter une liste de `dependencies` qui seront ajoutées à tous les *chemins d'accès* du routeur et seront exécutées/résolues pour chaque requête qui leur est faite.
/// tip | Astuce
Notez que, tout comme pour les [dépendances dans les décorateurs de *chemin d'accès*](dependencies/dependencies-in-path-operation-decorators.md), aucune valeur ne sera transmise à votre *fonction de chemin d'accès*.
///
Le résultat final est que les chemins d'item sont désormais :
* `/items/`
* `/items/{item_id}`
... comme prévu.
* Ils seront marqués avec une liste de tags qui contient une seule chaîne « items ».
* Ces « tags » sont particulièrement utiles pour les systèmes de documentation interactive automatique (utilisant OpenAPI).
* Ils incluront tous les `responses` prédéfinies.
* Tous ces *chemins d'accès* auront la liste des `dependencies` évaluées/exécutées avant eux.
* Si vous déclarez également des dépendances dans un *chemin d'accès* spécifique, **elles seront aussi exécutées**.
* Les dépendances du routeur sont exécutées en premier, puis les [`dependencies` dans le décorateur](dependencies/dependencies-in-path-operation-decorators.md), puis les dépendances des paramètres normaux.
* Vous pouvez également ajouter des [`Security` dependencies avec des `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Astuce
Avoir des `dependencies` dans le `APIRouter` peut servir, par exemple, à exiger une authentification pour tout un groupe de *chemins d'accès*. Même si les dépendances ne sont pas ajoutées individuellement à chacun d'eux.
///
/// check | Vérifications
Les paramètres `prefix`, `tags`, `responses` et `dependencies` sont (comme dans de nombreux autres cas) simplement une fonctionnalité de **FastAPI** pour vous aider à éviter la duplication de code.
///
### Importer les dépendances { #import-the-dependencies }
Ce code se trouve dans le module `app.routers.items`, le fichier `app/routers/items.py`.
Et nous devons récupérer la fonction de dépendance depuis le module `app.dependencies`, le fichier `app/dependencies.py`.
Nous utilisons donc un import relatif avec `..` pour les dépendances :
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Comprendre le fonctionnement des imports relatifs { #how-relative-imports-work }
/// tip | Astuce
Si vous savez parfaitement comment fonctionnent les imports, passez à la section suivante ci-dessous.
///
Un seul point `.`, comme dans :
```Python
from .dependencies import get_token_header
```
signifierait :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* trouver le module `dependencies` (un fichier imaginaire `app/routers/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Mais ce fichier n'existe pas, nos dépendances sont dans un fichier `app/dependencies.py`.
Rappelez-vous à quoi ressemble la structure de notre app/fichiers :
---
Les deux points `..`, comme dans :
```Python
from ..dependencies import get_token_header
```
veulent dire :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* aller au package parent (le répertoire `app/`)...
* et là, trouver le module `dependencies` (le fichier `app/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Cela fonctionne correctement ! 🎉
---
De la même manière, si nous avions utilisé trois points `...`, comme dans :
```Python
from ...dependencies import get_token_header
```
cela voudrait dire :
* En partant du même package dans lequel vit ce module (le fichier `app/routers/items.py`) (le répertoire `app/routers/`)...
* aller au package parent (le répertoire `app/`)...
* puis aller au parent de ce package (il n'y a pas de package parent, `app` est le niveau supérieur 😱)...
* et là, trouver le module `dependencies` (le fichier `app/dependencies.py`)...
* et en importer la fonction `get_token_header`.
Cela ferait référence à un package au-dessus de `app/`, avec son propre fichier `__init__.py`, etc. Mais nous n'avons pas cela. Donc, cela lèverait une erreur dans notre exemple. 🚨
Mais maintenant vous savez comment cela fonctionne, vous pouvez donc utiliser des imports relatifs dans vos propres applications, aussi complexes soient-elles. 🤓
### Ajouter des `tags`, `responses` et `dependencies` personnalisés { #add-some-custom-tags-responses-and-dependencies }
Nous n'ajoutons pas le préfixe `/items` ni `tags=["items"]` à chaque *chemin d'accès* parce que nous les avons ajoutés au `APIRouter`.
Mais nous pouvons toujours ajouter _davantage_ de `tags` qui seront appliqués à un *chemin d'accès* spécifique, ainsi que des `responses` supplémentaires propres à ce *chemin d'accès* :
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Astuce
Ce dernier *chemin d'accès* aura la combinaison de tags : `["items", "custom"]`.
Et il aura également les deux réponses dans la documentation, une pour `404` et une pour `403`.
///
## Créer l'application `FastAPI` principale { #the-main-fastapi }
Voyons maintenant le module `app/main.py`.
C'est ici que vous importez et utilisez la classe `FastAPI`.
Ce sera le fichier principal de votre application qui reliera tout ensemble.
Et comme la plupart de votre logique vivra désormais dans son propre module, le fichier principal sera assez simple.
### Importer `FastAPI` { #import-fastapi }
Vous importez et créez une classe `FastAPI` comme d'habitude.
Et nous pouvons même déclarer des [dépendances globales](dependencies/global-dependencies.md) qui seront combinées avec les dépendances de chaque `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Importer les `APIRouter` { #import-the-apirouter }
Nous importons maintenant les autres sous-modules qui ont des `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Comme les fichiers `app/routers/users.py` et `app/routers/items.py` sont des sous-modules qui font partie du même package Python `app`, nous pouvons utiliser un seul point `.` pour les importer en utilisant des « imports relatifs ».
### Comprendre le fonctionnement de l'import { #how-the-importing-works }
La section :
```Python
from .routers import items, users
```
signifie :
* En partant du même package dans lequel vit ce module (le fichier `app/main.py`) (le répertoire `app/`)...
* chercher le sous-package `routers` (le répertoire `app/routers/`)...
* et en importer le sous-module `items` (le fichier `app/routers/items.py`) et `users` (le fichier `app/routers/users.py`)...
Le module `items` aura une variable `router` (`items.router`). C'est celle que nous avons créée dans le fichier `app/routers/items.py`, c'est un objet `APIRouter`.
Nous faisons ensuite la même chose pour le module `users`.
Nous pourrions aussi les importer ainsi :
```Python
from app.routers import items, users
```
/// info
La première version est un « import relatif » :
```Python
from .routers import items, users
```
La deuxième version est un « import absolu » :
```Python
from app.routers import items, users
```
Pour en savoir plus sur les Packages et Modules Python, lisez [la documentation officielle de Python sur les modules](https://docs.python.org/3/tutorial/modules.html).
///
### Éviter les collisions de noms { #avoid-name-collisions }
Nous importons le sous-module `items` directement, au lieu d'importer uniquement sa variable `router`.
C'est parce que nous avons également une autre variable nommée `router` dans le sous-module `users`.
Si nous les avions importées l'une après l'autre, comme :
```Python
from .routers.items import router
from .routers.users import router
```
le `router` de `users` écraserait celui de `items` et nous ne pourrions pas les utiliser en même temps.
Donc, pour pouvoir utiliser les deux dans le même fichier, nous importons directement les sous-modules :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Inclure les `APIRouter` pour `users` et `items` { #include-the-apirouters-for-users-and-items }
Incluons maintenant les `router` des sous-modules `users` et `items` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info
`users.router` contient le `APIRouter` à l'intérieur du fichier `app/routers/users.py`.
Et `items.router` contient le `APIRouter` à l'intérieur du fichier `app/routers/items.py`.
///
Avec `app.include_router()`, nous pouvons ajouter chaque `APIRouter` à l'application principale `FastAPI`.
Cela inclura toutes les routes de ce routeur comme faisant partie de l'application.
/// note | Détails techniques
En interne, cela créera en fait un *chemin d'accès* pour chaque *chemin d'accès* qui a été déclaré dans le `APIRouter`.
Donc, en coulisses, cela fonctionnera comme si tout faisait partie d'une seule et même application.
///
/// check | Vérifications
Vous n'avez pas à vous soucier de la performance lors de l'inclusion de routeurs.
Cela prendra des microsecondes et ne se produira qu'au démarrage.
Donc cela n'affectera pas la performance. ⚡
///
### Inclure un `APIRouter` avec un `prefix`, des `tags`, des `responses` et des `dependencies` personnalisés { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Imaginons maintenant que votre organisation vous ait fourni le fichier `app/internal/admin.py`.
Il contient un `APIRouter` avec quelques *chemins d'accès* d'administration que votre organisation partage entre plusieurs projets.
Pour cet exemple, il sera très simple. Mais supposons que, parce qu'il est partagé avec d'autres projets de l'organisation, nous ne puissions pas le modifier et ajouter un `prefix`, des `dependencies`, des `tags`, etc. directement au `APIRouter` :
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Mais nous voulons quand même définir un `prefix` personnalisé lors de l'inclusion du `APIRouter` afin que tous ses *chemins d'accès* commencent par `/admin`, nous voulons le sécuriser avec les `dependencies` que nous avons déjà pour ce projet, et nous voulons inclure des `tags` et des `responses`.
Nous pouvons déclarer tout cela sans avoir à modifier le `APIRouter` d'origine en passant ces paramètres à `app.include_router()` :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
De cette façon, le `APIRouter` original restera inchangé, afin que nous puissions toujours partager ce même fichier `app/internal/admin.py` avec d'autres projets de l'organisation.
Le résultat est que, dans notre application, chacun des *chemins d'accès* du module `admin` aura :
* Le préfixe `/admin`.
* Le tag `admin`.
* La dépendance `get_token_header`.
* La réponse `418`. 🍵
Mais cela n'affectera que ce `APIRouter` dans notre application, pas dans tout autre code qui l'utilise.
Ainsi, par exemple, d'autres projets pourraient utiliser le même `APIRouter` avec une méthode d'authentification différente.
### Inclure un *chemin d'accès* { #include-a-path-operation }
Nous pouvons également ajouter des *chemins d'accès* directement à l'application `FastAPI`.
Ici, nous le faisons ... juste pour montrer que nous le pouvons 🤷 :
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
et cela fonctionnera correctement, avec tous les autres *chemins d'accès* ajoutés avec `app.include_router()`.
/// info | Détails très techniques
Note : c'est un détail très technique que vous pouvez probablement **simplement ignorer**.
---
Les `APIRouter` ne sont pas « montés », ils ne sont pas isolés du reste de l'application.
C'est parce que nous voulons inclure leurs *chemins d'accès* dans le schéma OpenAPI et les interfaces utilisateur.
Comme nous ne pouvons pas simplement les isoler et les « monter » indépendamment du reste, les *chemins d'accès* sont « clonés » (recréés), pas inclus directement.
///
## Configurer l'`entrypoint` dans `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
Comme votre objet FastAPI `app` vit dans `app/main.py`, vous pouvez configurer l'`entrypoint` dans votre fichier `pyproject.toml` comme ceci :
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
ce qui équivaut à importer ainsi :
```python
from app.main import app
```
De cette façon, la commande `fastapi` saura où trouver votre app.
/// Note | Remarque
Vous pourriez aussi passer le chemin à la commande, comme :
```console
$ fastapi dev app/main.py
```
Mais vous devriez vous rappeler de passer le bon chemin à chaque fois que vous appelez la commande `fastapi`.
En outre, d'autres outils pourraient ne pas être en mesure de la trouver, par exemple l'[Extension VS Code](../editor-support.md) ou [FastAPI Cloud](https://fastapicloud.com), il est donc recommandé d'utiliser l'`entrypoint` dans `pyproject.toml`.
///
## Consulter la documentation API automatique { #check-the-automatic-api-docs }
Maintenant, exécutez votre application :
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Et ouvrez les documents à [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez la documentation API automatique, incluant les chemins de tous les sous-modules, utilisant les bons chemins (et préfixes) et les bons tags :
## Inclure le même routeur plusieurs fois avec des `prefix` différents { #include-the-same-router-multiple-times-with-different-prefix }
Vous pouvez aussi utiliser `.include_router()` plusieurs fois avec le même routeur en utilisant des préfixes différents.
Cela peut être utile, par exemple, pour exposer la même API sous des préfixes différents, p. ex. `/api/v1` et `/api/latest`.
C'est un usage avancé dont vous n'aurez peut-être pas vraiment besoin, mais il est là au cas où.
## Inclure un `APIRouter` dans un autre { #include-an-apirouter-in-another }
De la même manière que vous pouvez inclure un `APIRouter` dans une application `FastAPI`, vous pouvez inclure un `APIRouter` dans un autre `APIRouter` en utilisant :
```Python
router.include_router(other_router)
```
Vous devez vous assurer de le faire avant d'inclure `router` dans l'application `FastAPI`, afin que les *chemins d'accès* de `other_router` soient également inclus.
================================================
FILE: docs/fr/docs/tutorial/body-fields.md
================================================
# Corps - Champs { #body-fields }
De la même manière que vous pouvez déclarer des validations supplémentaires et des métadonnées dans les paramètres d'une fonction de chemin d'accès avec `Query`, `Path` et `Body`, vous pouvez déclarer des validations et des métadonnées à l'intérieur des modèles Pydantic en utilisant `Field` de Pydantic.
## Importer `Field` { #import-field }
D'abord, vous devez l'importer :
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Alertes
Notez que `Field` est importé directement depuis `pydantic`, et non depuis `fastapi` comme le sont les autres (`Query`, `Path`, `Body`, etc.).
///
## Déclarer les attributs du modèle { #declare-model-attributes }
Vous pouvez ensuite utiliser `Field` avec des attributs de modèle :
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` fonctionne de la même manière que `Query`, `Path` et `Body`, il dispose des mêmes paramètres, etc.
/// note | Détails techniques
En réalité, `Query`, `Path` et d'autres que vous verrez ensuite créent des objets de sous-classes d'une classe commune `Param`, qui est elle-même une sous-classe de la classe `FieldInfo` de Pydantic.
Et `Field` de Pydantic renvoie également une instance de `FieldInfo`.
`Body` renvoie aussi directement des objets d'une sous-classe de `FieldInfo`. Et il y en a d'autres que vous verrez plus tard qui sont des sous-classes de la classe `Body`.
Rappelez-vous que lorsque vous importez `Query`, `Path` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// tip | Astuce
Remarquez comment chaque attribut de modèle avec un type, une valeur par défaut et `Field` a la même structure qu'un paramètre de fonction de chemin d'accès, avec `Field` au lieu de `Path`, `Query` et `Body`.
///
## Ajouter des informations supplémentaires { #add-extra-information }
Vous pouvez déclarer des informations supplémentaires dans `Field`, `Query`, `Body`, etc. Elles seront incluses dans le JSON Schema généré.
Vous en apprendrez davantage sur l'ajout d'informations supplémentaires plus loin dans les documents, lorsque vous apprendrez à déclarer des exemples.
/// warning | Alertes
Les clés supplémentaires passées à `Field` seront également présentes dans le schéma OpenAPI résultant pour votre application.
Comme ces clés ne font pas nécessairement partie de la spécification OpenAPI, certains outils OpenAPI, par exemple [le validateur OpenAPI](https://validator.swagger.io/), peuvent ne pas fonctionner avec votre schéma généré.
///
## Récapitulatif { #recap }
Vous pouvez utiliser `Field` de Pydantic pour déclarer des validations supplémentaires et des métadonnées pour les attributs de modèle.
Vous pouvez également utiliser des arguments nommés supplémentaires pour transmettre des métadonnées JSON Schema additionnelles.
================================================
FILE: docs/fr/docs/tutorial/body-multiple-params.md
================================================
# Body - Paramètres multiples { #body-multiple-parameters }
Maintenant que nous avons vu comment utiliser `Path` et `Query`, voyons des usages plus avancés des déclarations de paramètres du corps de la requête.
## Mélanger les paramètres `Path`, `Query` et du corps de la requête { #mix-path-query-and-body-parameters }
Tout d'abord, sachez que vous pouvez mélanger librement les déclarations des paramètres `Path`, `Query` et du corps de la requête, **FastAPI** saura quoi faire.
Et vous pouvez également déclarer des paramètres du corps de la requête comme étant optionnels, en leur assignant une valeur par défaut à `None` :
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Remarque
Notez que, dans ce cas, l'élément `item` récupéré depuis le corps de la requête est optionnel. Comme sa valeur par défaut est `None`.
///
## Paramètres multiples du corps de la requête { #multiple-body-parameters }
Dans l'exemple précédent, les chemins d'accès attendraient un corps de la requête JSON avec les attributs d'un `Item`, par exemple :
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Mais vous pouvez également déclarer plusieurs paramètres provenant du corps de la requête, par exemple `item` et `user` :
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
Dans ce cas, **FastAPI** détectera qu'il y a plus d'un paramètre du corps de la requête dans la fonction (il y a deux paramètres qui sont des modèles Pydantic).
Il utilisera alors les noms des paramètres comme clés (noms de champs) dans le corps de la requête, et s'attendra à recevoir un corps de la requête semblable à :
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Remarque
Notez que, bien que `item` ait été déclaré de la même manière qu'auparavant, il est désormais attendu à l'intérieur du corps de la requête sous la clé `item`.
///
**FastAPI** effectuera la conversion automatique depuis la requête, de sorte que le paramètre `item` reçoive son contenu spécifique, et de même pour `user`.
Il effectuera la validation des données composées, et les documentera ainsi pour le schéma OpenAPI et la documentation automatique.
## Valeurs singulières dans le corps de la requête { #singular-values-in-body }
De la même façon qu'il existe `Query` et `Path` pour définir des données supplémentaires pour les paramètres de requête et de chemin, **FastAPI** fournit un équivalent `Body`.
Par exemple, en étendant le modèle précédent, vous pourriez décider d'avoir une autre clé `importance` dans le même corps de la requête, en plus de `item` et `user`.
Si vous le déclarez tel quel, comme c'est une valeur singulière, **FastAPI** supposera qu'il s'agit d'un paramètre de requête.
Mais vous pouvez indiquer à **FastAPI** de la traiter comme une autre clé du corps de la requête en utilisant `Body` :
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
Dans ce cas, **FastAPI** s'attendra à un corps de la requête semblable à :
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Encore une fois, il convertira les types de données, validera, documentera, etc.
## Paramètres multiples du corps de la requête et paramètres de requête { #multiple-body-params-and-query }
Bien entendu, vous pouvez également déclarer des paramètres de requête supplémentaires quand vous en avez besoin, en plus de tout paramètre du corps de la requête.
Comme, par défaut, les valeurs singulières sont interprétées comme des paramètres de requête, vous n'avez pas besoin d'ajouter explicitement `Query`, vous pouvez simplement écrire :
```Python
q: str | None = None
```
Par exemple :
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info
`Body` possède également les mêmes paramètres supplémentaires de validation et de métadonnées que `Query`, `Path` et d'autres que vous verrez plus tard.
///
## Intégrer un seul paramètre du corps de la requête { #embed-a-single-body-parameter }
Supposons que vous n'ayez qu'un seul paramètre `item` dans le corps de la requête, provenant d'un modèle Pydantic `Item`.
Par défaut, **FastAPI** attendra alors son contenu directement.
Mais si vous voulez qu'il attende un JSON avec une clé `item` contenant le contenu du modèle, comme lorsqu'on déclare des paramètres supplémentaires du corps de la requête, vous pouvez utiliser le paramètre spécial `embed` de `Body` :
```Python
item: Item = Body(embed=True)
```
comme dans :
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
Dans ce cas **FastAPI** s'attendra à un corps de la requête semblable à :
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
au lieu de :
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Récapitulatif { #recap }
Vous pouvez ajouter plusieurs paramètres du corps de la requête à votre fonction de chemin d'accès, même si une requête ne peut avoir qu'un seul corps de la requête.
Mais **FastAPI** s'en chargera, vous fournira les bonnes données dans votre fonction, et validera et documentera le schéma correct dans le chemin d'accès.
Vous pouvez également déclarer des valeurs singulières à recevoir dans le corps de la requête.
Et vous pouvez indiquer à **FastAPI** d'intégrer le corps de la requête sous une clé même lorsqu'un seul paramètre est déclaré.
================================================
FILE: docs/fr/docs/tutorial/body-nested-models.md
================================================
# Corps - Modèles imbriqués { #body-nested-models }
Avec FastAPI, vous pouvez définir, valider, documenter et utiliser des modèles imbriqués à n'importe quelle profondeur (grâce à Pydantic).
## Déclarer des champs de liste { #list-fields }
Vous pouvez définir un attribut comme étant un sous-type. Par exemple, une `list` Python :
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Cela fera de `tags` une liste, bien que le type des éléments de la liste ne soit pas déclaré.
## Champs de liste avec paramètre de type { #list-fields-with-type-parameter }
Mais Python a une manière spécifique de déclarer des listes avec des types internes, ou « paramètres de type » :
### Déclarer une `list` avec un paramètre de type { #declare-a-list-with-a-type-parameter }
Pour déclarer des types qui ont des paramètres de type (types internes), comme `list`, `dict`, `tuple`,
passez le(s) type(s) interne(s) comme « paramètres de type » à l'aide de crochets : `[` et `]`
```Python
my_list: list[str]
```
C'est simplement la syntaxe Python standard pour les déclarations de type.
Utilisez cette même syntaxe standard pour les attributs de modèles avec des types internes.
Ainsi, dans notre exemple, nous pouvons faire de `tags` spécifiquement une « liste de chaînes » :
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Types set { #set-types }
Mais en y réfléchissant, nous réalisons que les tags ne devraient pas se répéter, ce seraient probablement des chaînes uniques.
Et Python dispose d'un type de données spécial pour les ensembles d'éléments uniques, le `set`.
Nous pouvons alors déclarer `tags` comme un set de chaînes :
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Avec cela, même si vous recevez une requête contenant des doublons, elle sera convertie en un set d'éléments uniques.
Et chaque fois que vous renverrez ces données, même si la source contenait des doublons, elles seront renvoyées sous la forme d'un set d'éléments uniques.
Elles seront également annotées / documentées en conséquence.
## Modèles imbriqués { #nested-models }
Chaque attribut d'un modèle Pydantic a un type.
Mais ce type peut lui-même être un autre modèle Pydantic.
Ainsi, vous pouvez déclarer des « objets » JSON profondément imbriqués avec des noms d'attributs, des types et des validations spécifiques.
Tout cela, de manière arbitrairement imbriquée.
### Définir un sous-modèle { #define-a-submodel }
Par exemple, nous pouvons définir un modèle `Image` :
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Utiliser le sous-modèle comme type { #use-the-submodel-as-a-type }
Nous pouvons ensuite l'utiliser comme type d'un attribut :
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Cela signifie que FastAPI attendrait un corps similaire à :
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Là encore, avec cette simple déclaration, avec FastAPI vous obtenez :
- Prise en charge par l'éditeur (autocomplétion, etc.), même pour les modèles imbriqués
- Conversion des données
- Validation des données
- Documentation automatique
## Types spéciaux et validation { #special-types-and-validation }
Outre les types singuliers normaux comme `str`, `int`, `float`, etc. vous pouvez utiliser des types singuliers plus complexes qui héritent de `str`.
Pour voir toutes les options dont vous disposez, consultez [l’aperçu des types de Pydantic](https://docs.pydantic.dev/latest/concepts/types/). Vous verrez quelques exemples au chapitre suivant.
Par exemple, comme dans le modèle `Image` nous avons un champ `url`, nous pouvons le déclarer comme instance de `HttpUrl` de Pydantic au lieu de `str` :
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
La chaîne sera vérifiée comme URL valide et documentée comme telle dans JSON Schema / OpenAPI.
## Attributs avec des listes de sous-modèles { #attributes-with-lists-of-submodels }
Vous pouvez également utiliser des modèles Pydantic comme sous-types de `list`, `set`, etc. :
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Cela attendra (convertira, validera, documentera, etc.) un corps JSON comme :
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info
Remarquez que la clé `images` contient maintenant une liste d'objets image.
///
## Modèles profondément imbriqués { #deeply-nested-models }
Vous pouvez définir des modèles imbriqués à une profondeur arbitraire :
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info
Remarquez que `Offer` a une liste d’`Item`, qui à leur tour ont une liste optionnelle d’`Image`.
///
## Corps de listes pures { #bodies-of-pure-lists }
Si la valeur de premier niveau du corps JSON attendu est un `array` JSON (une `list` Python), vous pouvez déclarer le type dans le paramètre de la fonction, de la même manière que dans les modèles Pydantic :
```Python
images: list[Image]
```
comme :
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Bénéficier de la prise en charge de l'éditeur partout { #editor-support-everywhere }
Et vous bénéficiez de la prise en charge de l'éditeur partout.
Même pour les éléments à l'intérieur des listes :
Vous ne pourriez pas obtenir ce type de prise en charge de l'éditeur si vous travailliez directement avec des `dict` au lieu de modèles Pydantic.
Mais vous n'avez pas à vous en soucier non plus, les `dict` entrants sont convertis automatiquement et votre sortie est également convertie automatiquement en JSON.
## Corps de `dict` arbitraires { #bodies-of-arbitrary-dicts }
Vous pouvez également déclarer un corps comme un `dict` avec des clés d’un certain type et des valeurs d’un autre type.
De cette façon, vous n'avez pas besoin de savoir à l'avance quels sont les noms de champs/attributs valides (comme ce serait le cas avec des modèles Pydantic).
Cela serait utile si vous voulez recevoir des clés que vous ne connaissez pas à l'avance.
---
Un autre cas utile est lorsque vous souhaitez avoir des clés d'un autre type (par exemple `int`).
C'est ce que nous allons voir ici.
Dans ce cas, vous accepteriez n'importe quel `dict` tant qu'il a des clés `int` avec des valeurs `float` :
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Astuce
Gardez à l'esprit que JSON ne prend en charge que des `str` comme clés.
Mais Pydantic dispose d'une conversion automatique des données.
Cela signifie que, même si vos clients d'API ne peuvent envoyer que des chaînes comme clés, tant que ces chaînes contiennent des entiers purs, Pydantic les convertira et les validera.
Et le `dict` que vous recevez dans `weights` aura en réalité des clés `int` et des valeurs `float`.
///
## Récapitulatif { #recap }
Avec FastAPI, vous bénéficiez de la flexibilité maximale fournie par les modèles Pydantic, tout en gardant votre code simple, concis et élégant.
Mais avec tous les avantages :
- Prise en charge par l'éditeur (autocomplétion partout !)
- Conversion des données (a.k.a. parsing / sérialisation)
- Validation des données
- Documentation des schémas
- Documentation automatique
================================================
FILE: docs/fr/docs/tutorial/body-updates.md
================================================
# Corps - Mises à jour { #body-updates }
## Mettre à jour en remplaçant avec `PUT` { #update-replacing-with-put }
Pour mettre à jour un élément, vous pouvez utiliser l’opération [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT).
Vous pouvez utiliser le `jsonable_encoder` pour convertir les données d’entrée en données pouvant être stockées au format JSON (par exemple, avec une base de données NoSQL). Par exemple, convertir `datetime` en `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
On utilise `PUT` pour recevoir des données qui doivent remplacer les données existantes.
### Avertissement concernant le remplacement { #warning-about-replacing }
Cela signifie que si vous souhaitez mettre à jour l’élément `bar` avec `PUT` et un corps contenant :
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
comme il n’inclut pas l’attribut déjà enregistré « tax »: 20.2, le modèle d’entrée prendrait la valeur par défaut « tax »: 10.5.
Et les données seraient enregistrées avec cette « nouvelle » « tax » de 10.5.
## Effectuer des mises à jour partielles avec `PATCH` { #partial-updates-with-patch }
Vous pouvez également utiliser l’opération [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) pour mettre à jour des données de manière partielle.
Cela signifie que vous pouvez n’envoyer que les données que vous souhaitez mettre à jour, en laissant le reste intact.
/// note | Remarque
`PATCH` est moins utilisé et moins connu que `PUT`.
Et de nombreuses équipes n’utilisent que `PUT`, même pour les mises à jour partielles.
Vous êtes libre de les utiliser comme vous le souhaitez, **FastAPI** n’impose aucune restriction.
Mais ce guide vous montre, plus ou moins, la façon dont ils sont censés être utilisés.
///
### Utiliser le paramètre `exclude_unset` de Pydantic { #using-pydantics-exclude-unset-parameter }
Si vous souhaitez recevoir des mises à jour partielles, il est très utile d’utiliser le paramètre `exclude_unset` dans la méthode `.model_dump()` du modèle Pydantic.
Comme `item.model_dump(exclude_unset=True)`.
Cela génère un `dict` ne contenant que les données définies lors de la création du modèle `item`, en excluant les valeurs par défaut.
Vous pouvez ensuite l’utiliser pour produire un `dict` avec uniquement les données définies (envoyées dans la requête), en omettant les valeurs par défaut :
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Utiliser le paramètre `update` de Pydantic { #using-pydantics-update-parameter }
Vous pouvez maintenant créer une copie du modèle existant avec `.model_copy()`, et passer le paramètre `update` avec un `dict` contenant les données à mettre à jour.
Comme `stored_item_model.model_copy(update=update_data)` :
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Récapitulatif des mises à jour partielles { #partial-updates-recap }
En résumé, pour appliquer des mises à jour partielles, vous procédez ainsi :
* (Optionnel) utilisez `PATCH` au lieu de `PUT`.
* Récupérez les données stockées.
* Placez ces données dans un modèle Pydantic.
* Générez un `dict` sans valeurs par défaut à partir du modèle d’entrée (en utilisant `exclude_unset`).
* De cette façon, vous mettez à jour uniquement les valeurs effectivement définies par l’utilisateur, au lieu d’écraser des valeurs déjà stockées par des valeurs par défaut de votre modèle.
* Créez une copie du modèle stocké, en mettant à jour ses attributs avec les mises à jour partielles reçues (en utilisant le paramètre `update`).
* Convertissez le modèle copié en quelque chose qui peut être stocké dans votre base de données (par exemple en utilisant le `jsonable_encoder`).
* Cela est comparable à l’utilisation à nouveau de la méthode `.model_dump()` du modèle, mais cela vérifie (et convertit) les valeurs vers des types pouvant être convertis en JSON, par exemple `datetime` en `str`.
* Enregistrez les données dans votre base de données.
* Retournez le modèle mis à jour.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Astuce
Vous pouvez en réalité utiliser cette même technique avec une opération HTTP `PUT`.
Mais l’exemple ici utilise `PATCH` car il a été créé pour ces cas d’usage.
///
/// note | Remarque
Remarquez que le modèle d’entrée est toujours validé.
Ainsi, si vous souhaitez recevoir des mises à jour partielles pouvant omettre tous les attributs, vous devez disposer d’un modèle avec tous les attributs marqués comme optionnels (avec des valeurs par défaut ou `None`).
Pour distinguer les modèles avec toutes les valeurs optionnelles pour les mises à jour et les modèles avec des valeurs requises pour la création, vous pouvez utiliser les idées décrites dans [Modèles supplémentaires](extra-models.md).
///
================================================
FILE: docs/fr/docs/tutorial/body.md
================================================
# Corps de la requête { #request-body }
Quand vous avez besoin d'envoyer de la donnée depuis un client (comme un navigateur) vers votre API, vous l'envoyez en tant que **corps de requête**.
Le corps d'une **requête** est de la donnée envoyée par le client à votre API. Le corps d'une **réponse** est la donnée envoyée par votre API au client.
Votre API aura presque toujours à envoyer un corps de **réponse**. Mais un client n'a pas toujours à envoyer un **corps de requête** : parfois il demande seulement un chemin, peut-être avec quelques paramètres de requête, mais n'envoie pas de corps.
Pour déclarer un corps de **requête**, on utilise les modèles de [Pydantic](https://docs.pydantic.dev/) en profitant de tous leurs avantages et fonctionnalités.
/// info
Pour envoyer de la donnée, vous devez utiliser : `POST` (le plus populaire), `PUT`, `DELETE` ou `PATCH`.
Envoyer un corps dans une requête `GET` a un comportement non défini dans les spécifications, cela est néanmoins supporté par **FastAPI**, seulement pour des cas d'utilisation très complexes/extrêmes.
Ceci étant découragé, la documentation interactive générée par Swagger UI ne montrera pas de documentation pour le corps d'une requête `GET`, et les proxys intermédiaires risquent de ne pas le supporter.
///
## Importer le `BaseModel` de Pydantic { #import-pydantics-basemodel }
Commencez par importer la classe `BaseModel` du module `pydantic` :
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Créer votre modèle de données { #create-your-data-model }
Déclarez ensuite votre modèle de données en tant que classe qui hérite de `BaseModel`.
Utilisez les types Python standard pour tous les attributs :
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Tout comme pour la déclaration de paramètres de requête, quand un attribut de modèle a une valeur par défaut, il n'est pas nécessaire. Sinon, il est requis. Utilisez `None` pour le rendre simplement optionnel.
Par exemple, le modèle ci-dessus déclare un JSON « `object` » (ou `dict` Python) tel que :
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
... `description` et `tax` étant des attributs optionnels (avec `None` comme valeur par défaut), ce JSON « `object` » serait aussi valide :
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Le déclarer comme paramètre { #declare-it-as-a-parameter }
Pour l'ajouter à votre *chemin d'accès*, déclarez-le comme vous déclareriez des paramètres de chemin ou de requête :
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
... et déclarez que son type est le modèle que vous avez créé : `Item`.
## Résultats { #results }
En utilisant uniquement les déclarations de type Python, **FastAPI** réussit à :
* Lire le contenu de la requête en tant que JSON.
* Convertir les types correspondants (si nécessaire).
* Valider la donnée.
* Si la donnée est invalide, une erreur propre et claire sera renvoyée, indiquant exactement où et quelle était la donnée incorrecte.
* Passer la donnée reçue dans le paramètre `item`.
* Ce paramètre ayant été déclaré dans la fonction comme étant de type `Item`, vous aurez aussi tout le support offert par l'éditeur (autocomplétion, etc.) pour tous les attributs de ce paramètre et les types de ces attributs.
* Générer des définitions [JSON Schema](https://json-schema.org) pour votre modèle ; vous pouvez également les utiliser partout ailleurs si cela a du sens pour votre projet.
* Ces schémas participeront à la constitution du schéma généré OpenAPI, et seront utilisés par les documentations automatiques UIs.
## Documentation automatique { #automatic-docs }
Les schémas JSON de vos modèles seront intégrés au schéma OpenAPI global de votre application, et seront donc affichés dans la documentation interactive de l'API :
Et seront aussi utilisés dans chaque *chemin d'accès* de la documentation utilisant ces modèles :
## Support de l'éditeur { #editor-support }
Dans votre éditeur, vous aurez des annotations de type et de l'autocomplétion partout dans votre fonction (ce qui n'aurait pas été le cas si vous aviez reçu un `dict` plutôt qu'un modèle Pydantic) :
Et vous obtenez aussi des vérifications d'erreurs pour les opérations de types incorrectes :
Ce n'est pas un hasard, ce framework entier a été bâti avec ce design comme objectif.
Et cela a été rigoureusement testé durant la phase de design, avant toute implémentation, pour vous assurer que cela fonctionnerait avec tous les éditeurs.
Des changements sur Pydantic ont même été faits pour supporter cela.
Les captures d'écran précédentes ont été prises sur [Visual Studio Code](https://code.visualstudio.com).
Mais vous auriez le même support de l'éditeur avec [PyCharm](https://www.jetbrains.com/pycharm/) et la majorité des autres éditeurs de code Python :
/// tip | Astuce
Si vous utilisez [PyCharm](https://www.jetbrains.com/pycharm/) comme éditeur, vous pouvez utiliser le plug-in [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
Ce qui améliore le support pour les modèles Pydantic avec :
* de l'autocomplétion
* des vérifications de type
* du « refactoring »
* de la recherche
* des inspections
///
## Utiliser le modèle { #use-the-model }
Dans la fonction, vous pouvez accéder à tous les attributs de l'objet du modèle directement :
{* ../../docs_src/body/tutorial002_py310.py *}
## Corps de la requête + paramètres de chemin { #request-body-path-parameters }
Vous pouvez déclarer des paramètres de chemin et un corps de requête pour la même *chemin d'accès*.
**FastAPI** est capable de reconnaître que les paramètres de la fonction qui correspondent aux paramètres de chemin doivent être **récupérés depuis le chemin**, et que les paramètres de fonctions déclarés comme modèles Pydantic devraient être **récupérés depuis le corps de la requête**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Corps de la requête + paramètres de chemin et de requête { #request-body-path-query-parameters }
Vous pouvez aussi déclarer un **corps**, et des paramètres de **chemin** et de **requête** dans la même *chemin d'accès*.
**FastAPI** saura reconnaître chacun d'entre eux et récupérer la bonne donnée au bon endroit.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Les paramètres de la fonction seront reconnus comme tel :
* Si le paramètre est aussi déclaré dans le **chemin**, il sera utilisé comme paramètre de chemin.
* Si le paramètre est d'un **type singulier** (comme `int`, `float`, `str`, `bool`, etc.), il sera interprété comme un paramètre de **requête**.
* Si le paramètre est déclaré comme ayant pour type un **modèle Pydantic**, il sera interprété comme faisant partie du **corps** de la requête.
/// note | Remarque
**FastAPI** saura que la valeur de `q` n'est pas requise grâce à la valeur par défaut `= None`.
L'annotation de type `str | None` n'est pas utilisée par **FastAPI** pour déterminer que la valeur n'est pas requise, il le saura parce qu'elle a une valeur par défaut `= None`.
Mais ajouter ces annotations de type permettra à votre éditeur de vous offrir un meilleur support et de détecter des erreurs.
///
## Sans Pydantic { #without-pydantic }
Si vous ne voulez pas utiliser des modèles Pydantic, vous pouvez aussi utiliser des paramètres de **Body**. Pour cela, allez voir la documentation sur [Corps de la requête - Paramètres multiples : Valeurs singulières dans le corps](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/fr/docs/tutorial/cookie-param-models.md
================================================
# Modèles de paramètres de cookies { #cookie-parameter-models }
Si vous avez un groupe de **cookies** liés, vous pouvez créer un **modèle Pydantic** pour les déclarer. 🍪
Cela vous permet de **réutiliser le modèle** à **plusieurs endroits** et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Ceci est pris en charge depuis la version `0.115.0` de FastAPI. 🤓
///
/// tip | Astuce
Cette même technique s'applique à `Query`, `Cookie` et `Header`. 😎
///
## Déclarer des cookies avec un modèle Pydantic { #cookies-with-a-pydantic-model }
Déclarez les paramètres de **cookie** dont vous avez besoin dans un **modèle Pydantic**, puis déclarez le paramètre comme `Cookie` :
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** va **extraire** les données pour **chaque champ** à partir des **cookies** reçus dans la requête et vous fournir le modèle Pydantic que vous avez défini.
## Consulter la documentation { #check-the-docs }
Vous pouvez voir les cookies définis dans l'interface de la documentation à `/docs` :
/// info
Gardez à l'esprit que, comme les **navigateurs gèrent les cookies** de manière particulière et en arrière-plan, ils **n'autorisent pas** facilement **JavaScript** à y accéder.
Si vous allez dans **l'interface de la documentation de l'API** à `/docs`, vous pourrez voir la **documentation** des cookies pour vos *chemins d'accès*.
Mais même si vous **remplissez les données** et cliquez sur « Execute », comme l'interface de la documentation fonctionne avec **JavaScript**, les cookies ne seront pas envoyés et vous verrez un **message d'erreur** comme si vous n'aviez saisi aucune valeur.
///
## Interdire les cookies supplémentaires { #forbid-extra-cookies }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pourriez vouloir **restreindre** les cookies que vous souhaitez recevoir.
Votre API a désormais le pouvoir de contrôler son propre consentement aux cookies. 🤪🍪
Vous pouvez utiliser la configuration du modèle de Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Si un client tente d'envoyer des **cookies supplémentaires**, il recevra une **réponse d'erreur**.
Pauvres bannières de cookies, avec tous leurs efforts pour obtenir votre consentement pour que l'API pour le rejeter. 🍪
Par exemple, si le client tente d'envoyer un cookie `santa_tracker` avec la valeur `good-list-please`, il recevra une **réponse d'erreur** lui indiquant que le `santa_tracker` le cookie n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Récapitulatif { #summary }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des **cookies** dans **FastAPI**. 😎
================================================
FILE: docs/fr/docs/tutorial/cookie-params.md
================================================
# Paramètres de cookie { #cookie-parameters }
Vous pouvez définir des paramètres de cookie de la même manière que vous définissez les paramètres `Query` et `Path`.
## Importer `Cookie` { #import-cookie }
Commencez par importer `Cookie` :
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Déclarer des paramètres `Cookie` { #declare-cookie-parameters }
Déclarez ensuite les paramètres de cookie en utilisant la même structure qu'avec `Path` et `Query`.
Vous pouvez définir la valeur par défaut ainsi que tous les paramètres supplémentaires de validation ou d'annotation :
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Détails techniques
`Cookie` est une classe « sœur » de `Path` et `Query`. Elle hérite également de la même classe commune `Param`.
Mais rappelez-vous que lorsque vous importez `Query`, `Path`, `Cookie` et d'autres depuis `fastapi`, il s'agit en réalité de fonctions qui renvoient des classes spéciales.
///
/// info
Pour déclarer des cookies, vous devez utiliser `Cookie`, sinon les paramètres seraient interprétés comme des paramètres de requête.
///
/// info
Gardez à l'esprit que, comme **les navigateurs gèrent les cookies** de manière particulière et en coulisses, ils **n'autorisent pas** facilement **JavaScript** à y accéder.
Si vous allez dans l'**interface de la documentation de l'API** à `/docs`, vous pourrez voir la **documentation** des cookies pour vos *chemins d'accès*.
Mais même si vous **renseignez les données** et cliquez sur « Execute », comme l'interface de documentation fonctionne avec **JavaScript**, les cookies ne seront pas envoyés et vous verrez un message **d'erreur** comme si vous n'aviez saisi aucune valeur.
///
## Récapitulatif { #recap }
Déclarez les cookies avec `Cookie`, en utilisant le même schéma commun que `Query` et `Path`.
================================================
FILE: docs/fr/docs/tutorial/cors.md
================================================
# CORS (Partage des ressources entre origines) { #cors-cross-origin-resource-sharing }
[CORS ou « Cross-Origin Resource Sharing »](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) fait référence aux situations où un frontend exécuté dans un navigateur contient du code JavaScript qui communique avec un backend, et où le backend se trouve dans une « origine » différente de celle du frontend.
## Origine { #origin }
Une origine est la combinaison du protocole (`http`, `https`), du domaine (`myapp.com`, `localhost`, `localhost.tiangolo.com`) et du port (`80`, `443`, `8080`).
Ainsi, toutes celles-ci sont des origines différentes :
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Même si elles sont toutes sur `localhost`, elles utilisent des protocoles ou des ports différents, ce sont donc des « origines » différentes.
## Étapes { #steps }
Disons donc que vous avez un frontend exécuté dans votre navigateur à `http://localhost:8080`, et que son JavaScript essaie de communiquer avec un backend exécuté à `http://localhost` (comme nous ne spécifions pas de port, le navigateur supposera le port par défaut `80`).
Le navigateur enverra alors une requête HTTP `OPTIONS` au backend `:80`, et si le backend envoie les en-têtes appropriés autorisant la communication depuis cette origine différente (`http://localhost:8080`), alors le navigateur `:8080` permettra au JavaScript du frontend d’envoyer sa requête au backend `:80`.
Pour y parvenir, le backend `:80` doit disposer d’une liste « d’origines autorisées ».
Dans ce cas, la liste devrait inclure `http://localhost:8080` pour que le frontend `:8080` fonctionne correctement.
## Caractères génériques { #wildcards }
Il est également possible de déclarer la liste comme « * » (un « wildcard ») pour indiquer que toutes sont autorisées.
Mais cela n’autorisera que certains types de communication, en excluant tout ce qui implique des informations d’identification : cookies, en-têtes Authorization comme ceux utilisés avec les Bearer Tokens, etc.
Ainsi, pour que tout fonctionne correctement, il est préférable d’indiquer explicitement les origines autorisées.
## Utiliser `CORSMiddleware` { #use-corsmiddleware }
Vous pouvez le configurer dans votre application **FastAPI** à l’aide de `CORSMiddleware`.
* Importer `CORSMiddleware`.
* Créer une liste d’origines autorisées (sous forme de chaînes).
* L’ajouter comme « middleware » à votre application **FastAPI**.
Vous pouvez également spécifier si votre backend autorise :
* Les informations d’identification (en-têtes Authorization, cookies, etc.).
* Des méthodes HTTP spécifiques (`POST`, `PUT`) ou toutes avec le caractère générique « * ».
* Des en-têtes HTTP spécifiques ou tous avec le caractère générique « * ».
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Les paramètres utilisés par défaut par l’implémentation de `CORSMiddleware` sont restrictifs, vous devez donc activer explicitement des origines, méthodes ou en-têtes particuliers afin que les navigateurs soient autorisés à les utiliser dans un contexte inter‑domaine.
Les arguments suivants sont pris en charge :
* `allow_origins` - Une liste d’origines autorisées à effectuer des requêtes cross-origin. Par ex. `['https://example.org', 'https://www.example.org']`. Vous pouvez utiliser `['*']` pour autoriser n’importe quelle origine.
* `allow_origin_regex` - Une chaîne regex pour faire correspondre les origines autorisées à effectuer des requêtes cross-origin. Par ex. `'https://.*\.example\.org'`.
* `allow_methods` - Une liste de méthodes HTTP qui doivent être autorisées pour les requêtes cross-origin. Par défaut `['GET']`. Vous pouvez utiliser `['*']` pour autoriser toutes les méthodes standard.
* `allow_headers` - Une liste d’en-têtes HTTP de requête qui doivent être pris en charge pour les requêtes cross-origin. Par défaut `[]`. Vous pouvez utiliser `['*']` pour autoriser tous les en-têtes. Les en-têtes `Accept`, `Accept-Language`, `Content-Language` et `Content-Type` sont toujours autorisés pour les [requêtes CORS simples](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Indique que les cookies doivent être pris en charge pour les requêtes cross-origin. Par défaut `False`.
Aucun de `allow_origins`, `allow_methods` et `allow_headers` ne peut être défini à `['*']` si `allow_credentials` est défini à `True`. Ils doivent tous être [spécifiés explicitement](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Indique les en-têtes de réponse qui doivent être accessibles au navigateur. Par défaut `[]`.
* `max_age` - Définit un temps maximum (en secondes) pendant lequel les navigateurs peuvent mettre en cache les réponses CORS. Par défaut `600`.
Le middleware répond à deux types particuliers de requêtes HTTP ...
### Requêtes CORS de pré‑vérification { #cors-preflight-requests }
Il s’agit de toute requête `OPTIONS` avec les en-têtes `Origin` et `Access-Control-Request-Method`.
Dans ce cas, le middleware interceptera la requête entrante et répondra avec les en-têtes CORS appropriés, et soit une réponse `200`, soit `400` à titre informatif.
### Requêtes simples { #simple-requests }
Toute requête avec un en-tête `Origin`. Dans ce cas, le middleware laissera passer la requête normalement, mais inclura les en-têtes CORS appropriés dans la réponse.
## En savoir plus { #more-info }
Pour plus d’informations sur CORS, consultez la [documentation CORS de Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** fournit plusieurs middlewares dans `fastapi.middleware` uniquement pour votre confort, en tant que développeur. Mais la plupart des middlewares disponibles proviennent directement de Starlette.
///
================================================
FILE: docs/fr/docs/tutorial/debugging.md
================================================
# Débogage { #debugging }
Vous pouvez connecter le débogueur dans votre éditeur, par exemple avec Visual Studio Code ou PyCharm.
## Appeler `uvicorn` { #call-uvicorn }
Dans votre application FastAPI, importez et exécutez directement `uvicorn` :
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### À propos de `__name__ == "__main__"` { #about-name-main }
Le but principal de `__name__ == "__main__"` est d'avoir du code qui est exécuté lorsque votre fichier est appelé avec :
```console
$ python myapp.py
```
mais qui n'est pas appelé lorsqu'un autre fichier l'importe, comme dans :
```Python
from myapp import app
```
#### Pour davantage de détails { #more-details }
Imaginons que votre fichier s'appelle `myapp.py`.
Si vous l'exécutez avec :
```console
$ python myapp.py
```
alors la variable interne `__name__` de votre fichier, créée automatiquement par Python, aura pour valeur la chaîne de caractères `"__main__"`.
Ainsi, la section :
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
va s'exécuter.
---
Cela ne se produira pas si vous importez ce module (fichier).
Par exemple, si vous avez un autre fichier `importer.py` qui contient :
```Python
from myapp import app
# Code supplémentaire
```
dans ce cas, la variable automatique `__name__` à l'intérieur de `myapp.py` n'aura pas la valeur `"__main__"`.
Ainsi, la ligne :
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
ne sera pas exécutée.
/// info
Pour plus d'informations, consultez [la documentation officielle de Python](https://docs.python.org/3/library/__main__.html).
///
## Exécuter votre code avec votre débogueur { #run-your-code-with-your-debugger }
Parce que vous exécutez le serveur Uvicorn directement depuis votre code, vous pouvez appeler votre programme Python (votre application FastAPI) directement depuis le débogueur.
---
Par exemple, dans Visual Studio Code, vous pouvez :
- Allez dans le panneau « Debug ».
- « Add configuration ... ».
- Sélectionnez « Python ».
- Lancez le débogueur avec l'option « Python: Current File (Integrated Terminal) ».
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
Voici à quoi cela pourrait ressembler :
---
Si vous utilisez Pycharm, vous pouvez :
- Ouvrez le menu « Run ».
- Sélectionnez l'option « Debug ... ».
- Un menu contextuel s'affiche alors.
- Sélectionnez le fichier à déboguer (dans ce cas, `main.py`).
Il démarrera alors le serveur avec votre code **FastAPI**, s'arrêtera à vos points d'arrêt, etc.
Voici à quoi cela pourrait ressembler :
================================================
FILE: docs/fr/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Utiliser des classes comme dépendances { #classes-as-dependencies }
Avant d'aller plus loin dans le système d'**Injection de dépendances**, mettons à niveau l'exemple précédent.
## Un `dict` de l'exemple précédent { #a-dict-from-the-previous-example }
Dans l'exemple précédent, nous renvoyions un `dict` depuis notre dépendance (« dependable ») :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Mais nous recevons alors un `dict` dans le paramètre `commons` de la fonction de chemin d'accès.
Et les éditeurs ne peuvent pas apporter beaucoup d'assistance (comme l'autocomplétion) pour les `dict`, car ils ne peuvent pas connaître leurs clés ni les types de valeurs.
Nous pouvons faire mieux ...
## Ce qui fait d'un objet une dépendance { #what-makes-a-dependency }
Jusqu'à présent, vous avez vu des dépendances déclarées sous forme de fonctions.
Mais ce n'est pas la seule manière de déclarer des dépendances (même si c'est probablement la plus courante).
L'élément clé est qu'une dépendance doit être un « callable ».
Un « callable » en Python est tout ce que Python peut « appeler » comme une fonction.
Ainsi, si vous avez un objet `something` (qui n'est peut‑être pas une fonction) et que vous pouvez « l'appeler » (l'exécuter) comme :
```Python
something()
```
ou
```Python
something(some_argument, some_keyword_argument="foo")
```
alors c'est un « callable ».
## Utiliser des classes comme dépendances { #classes-as-dependencies_1 }
Vous remarquerez que pour créer une instance d'une classe Python, vous utilisez la même syntaxe.
Par exemple :
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
Dans ce cas, `fluffy` est une instance de la classe `Cat`.
Et pour créer `fluffy`, vous « appelez » `Cat`.
Donc, une classe Python est aussi un « callable ».
Ainsi, avec **FastAPI**, vous pouvez utiliser une classe Python comme dépendance.
Ce que **FastAPI** vérifie réellement, c'est qu'il s'agit d'un « callable » (fonction, classe ou autre) et des paramètres qui y sont définis.
Si vous passez un « callable » comme dépendance dans **FastAPI**, il en analysera les paramètres et les traitera de la même manière que les paramètres d'une fonction de chemin d'accès. Y compris les sous‑dépendances.
Cela s'applique également aux callables sans aucun paramètre. Comme ce serait le cas pour des fonctions de chemin d'accès sans paramètres.
Nous pouvons alors remplacer la dépendance « dependable » `common_parameters` ci‑dessus par la classe `CommonQueryParams` :
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Faites attention à la méthode `__init__` utilisée pour créer l'instance de la classe :
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
... il a les mêmes paramètres que notre précédent `common_parameters` :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Ce sont ces paramètres que **FastAPI** utilisera pour « résoudre » la dépendance.
Dans les deux cas, il y aura :
- Un paramètre de requête optionnel `q` qui est un `str`.
- Un paramètre de requête `skip` qui est un `int`, avec une valeur par défaut de `0`.
- Un paramètre de requête `limit` qui est un `int`, avec une valeur par défaut de `100`.
Dans les deux cas, les données seront converties, validées, documentées dans le schéma OpenAPI, etc.
## Utiliser { #use-it }
Vous pouvez maintenant déclarer votre dépendance en utilisant cette classe.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** appelle la classe `CommonQueryParams`. Cela crée une « instance » de cette classe et l'instance sera passée comme paramètre `commons` à votre fonction.
## Annotation de type vs `Depends` { #type-annotation-vs-depends }
Remarquez que nous écrivons `CommonQueryParams` deux fois dans le code ci‑dessus :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Le dernier `CommonQueryParams`, dans :
```Python
... Depends(CommonQueryParams)
```
... est ce que **FastAPI** utilisera réellement pour savoir quelle est la dépendance.
C'est à partir de celui‑ci que FastAPI extraira les paramètres déclarés et c'est ce que FastAPI appellera effectivement.
---
Dans ce cas, le premier `CommonQueryParams`, dans :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams ...
```
////
... n'a aucune signification particulière pour **FastAPI**. FastAPI ne l'utilisera pas pour la conversion des données, la validation, etc. (car il utilise `Depends(CommonQueryParams)` pour cela).
Vous pourriez en fait écrire simplement :
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons = Depends(CommonQueryParams)
```
////
... comme dans :
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Mais il est recommandé de déclarer le type ; ainsi, votre éditeur saura ce qui sera passé comme paramètre `commons`, et pourra vous aider avec l'autocomplétion, les vérifications de type, etc. :
## Raccourci { #shortcut }
Mais vous voyez qu'il y a ici de la duplication de code : nous écrivons `CommonQueryParams` deux fois :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** fournit un raccourci pour ces cas, lorsque la dépendance est spécifiquement une classe que **FastAPI** va « appeler » pour créer une instance de la classe elle‑même.
Pour ces cas précis, vous pouvez faire ce qui suit :
Au lieu d'écrire :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
... vous écrivez :
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ sans Annotated
/// tip | Astuce
Privilégiez la version avec `Annotated` si possible.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Vous déclarez la dépendance comme type du paramètre et vous utilisez `Depends()` sans aucun paramètre, au lieu d'avoir à réécrire la classe entière à l'intérieur de `Depends(CommonQueryParams)`.
Le même exemple ressemblerait alors à ceci :
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
... et **FastAPI** saura quoi faire.
/// tip | Astuce
Si cela vous semble plus déroutant qu'utile, ignorez‑le, vous n'en avez pas besoin.
Ce n'est qu'un raccourci. Parce que **FastAPI** tient à vous aider à minimiser la duplication de code.
///
================================================
FILE: docs/fr/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Gérer les dépendances dans les décorateurs de chemins d'accès { #dependencies-in-path-operation-decorators }
Dans certains cas, vous n'avez pas vraiment besoin de la valeur de retour d'une dépendance dans votre *fonction de chemin d'accès*.
Ou la dépendance ne retourne aucune valeur.
Mais vous avez quand même besoin qu'elle soit exécutée/résolue.
Dans ces cas, au lieu de déclarer un paramètre de *fonction de chemin d'accès* avec `Depends`, vous pouvez ajouter une `list` de `dependencies` au *décorateur de chemin d'accès*.
## Ajouter `dependencies` au *décorateur de chemin d'accès* { #add-dependencies-to-the-path-operation-decorator }
Le *décorateur de chemin d'accès* accepte un argument optionnel `dependencies`.
Il doit s'agir d'une `list` de `Depends()` :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Ces dépendances seront exécutées/résolues de la même manière que des dépendances normales. Mais leur valeur (si elles en retournent une) ne sera pas transmise à votre *fonction de chemin d'accès*.
/// tip | Astuce
Certains éditeurs vérifient les paramètres de fonction non utilisés et les signalent comme des erreurs.
En utilisant ces `dependencies` dans le *décorateur de chemin d'accès*, vous pouvez vous assurer qu'elles sont exécutées tout en évitant des erreurs de l'éditeur/des outils.
Cela peut également éviter toute confusion pour les nouveaux développeurs qui voient un paramètre inutilisé dans votre code et pourraient penser qu'il est superflu.
///
/// info | Info
Dans cet exemple, nous utilisons des en-têtes personnalisés fictifs `X-Key` et `X-Token`.
Mais dans des cas réels, lors de l'implémentation de la sécurité, vous tirerez davantage d'avantages en utilisant les [utilitaires de sécurité (chapitre suivant)](../security/index.md) intégrés.
///
## Gérer les erreurs et les valeurs de retour des dépendances { #dependencies-errors-and-return-values }
Vous pouvez utiliser les mêmes *fonctions* de dépendance que d'habitude.
### Définir les exigences des dépendances { #dependency-requirements }
Elles peuvent déclarer des exigences pour la requête (comme des en-têtes) ou d'autres sous-dépendances :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Lever des exceptions { #raise-exceptions }
Ces dépendances peuvent `raise` des exceptions, comme des dépendances normales :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Gérer les valeurs de retour { #return-values }
Elles peuvent retourner des valeurs ou non, ces valeurs ne seront pas utilisées.
Vous pouvez donc réutiliser une dépendance normale (qui retourne une valeur) que vous utilisez déjà ailleurs ; même si la valeur n'est pas utilisée, la dépendance sera exécutée :
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Définir des dépendances pour un groupe de chemins d'accès { #dependencies-for-a-group-of-path-operations }
Plus tard, en lisant comment structurer des applications plus grandes ([Applications plus grandes - Plusieurs fichiers](../../tutorial/bigger-applications.md)), éventuellement avec plusieurs fichiers, vous apprendrez à déclarer un unique paramètre `dependencies` pour un groupe de *chemins d'accès*.
## Définir des dépendances globales { #global-dependencies }
Ensuite, nous verrons comment ajouter des dépendances à l'application `FastAPI` entière, afin qu'elles s'appliquent à chaque *chemin d'accès*.
================================================
FILE: docs/fr/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Utiliser des dépendances avec `yield` { #dependencies-with-yield }
FastAPI prend en charge des dépendances qui effectuent des étapes supplémentaires après l'exécution.
Pour cela, utilisez `yield` au lieu de `return`, et écrivez les étapes supplémentaires (code) après.
/// tip | Astuce
Vous devez vous assurer d'utiliser `yield` une seule fois par dépendance.
///
/// note | Détails techniques
Toute fonction valide à utiliser avec :
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) ou
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
sera valide comme dépendance **FastAPI**.
En fait, FastAPI utilise ces deux décorateurs en interne.
///
## Créer une dépendance de base de données avec `yield` { #a-database-dependency-with-yield }
Par exemple, vous pouvez l'utiliser pour créer une session de base de données et la fermer après la fin.
Seul le code précédant et incluant l'instruction `yield` est exécuté avant la création de la réponse :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
La valeur transmise par `yield` est celle qui est injectée dans les *chemins d'accès* et autres dépendances :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Le code suivant l'instruction `yield` est exécuté après la réponse :
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Astuce
Vous pouvez utiliser des fonctions `async` ou des fonctions classiques.
**FastAPI** fera ce qu'il faut dans chaque cas, comme avec des dépendances normales.
///
## Créer une dépendance avec `yield` et `try` { #a-dependency-with-yield-and-try }
Si vous utilisez un bloc `try` dans une dépendance avec `yield`, vous recevrez toute exception qui a été levée lors de l'utilisation de la dépendance.
Par exemple, si à un moment donné, dans une autre dépendance ou dans un *chemin d'accès*, un code a effectué un « rollback » de transaction de base de données ou a créé une autre exception, vous recevrez l'exception dans votre dépendance.
Vous pouvez donc rechercher cette exception spécifique dans la dépendance avec `except SomeException`.
De la même manière, vous pouvez utiliser `finally` pour vous assurer que les étapes de sortie sont exécutées, qu'il y ait eu une exception ou non.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Utiliser des sous-dépendances avec `yield` { #sub-dependencies-with-yield }
Vous pouvez avoir des sous-dépendances et des « arbres » de sous-dépendances de toute taille et forme, et certaines ou toutes peuvent utiliser `yield`.
**FastAPI** s'assurera que le « code de sortie » dans chaque dépendance avec `yield` est exécuté dans le bon ordre.
Par exemple, `dependency_c` peut dépendre de `dependency_b`, et `dependency_b` de `dependency_a` :
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
Et elles peuvent toutes utiliser `yield`.
Dans ce cas, `dependency_c`, pour exécuter son code de sortie, a besoin que la valeur de `dependency_b` (appelée ici `dep_b`) soit toujours disponible.
Et, à son tour, `dependency_b` a besoin que la valeur de `dependency_a` (appelée ici `dep_a`) soit disponible pour son code de sortie.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
De la même manière, vous pouvez avoir certaines dépendances avec `yield` et d'autres avec `return`, et faire en sorte que certaines dépendent des autres.
Et vous pouvez avoir une seule dépendance qui exige plusieurs autres dépendances avec `yield`, etc.
Vous pouvez combiner les dépendances comme vous le souhaitez.
**FastAPI** s'assurera que tout est exécuté dans le bon ordre.
/// note | Détails techniques
Cela fonctionne grâce aux [gestionnaires de contexte](https://docs.python.org/3/library/contextlib.html) de Python.
**FastAPI** les utilise en interne pour y parvenir.
///
## Utiliser des dépendances avec `yield` et `HTTPException` { #dependencies-with-yield-and-httpexception }
Vous avez vu que vous pouvez utiliser des dépendances avec `yield` et avoir des blocs `try` qui tentent d'exécuter du code puis exécutent du code de sortie après `finally`.
Vous pouvez également utiliser `except` pour intercepter l'exception qui a été levée et faire quelque chose avec.
Par exemple, vous pouvez lever une autre exception, comme `HTTPException`.
/// tip | Astuce
C'est une technique plutôt avancée, et dans la plupart des cas vous n'en aurez pas vraiment besoin, car vous pouvez lever des exceptions (y compris `HTTPException`) depuis le reste de votre code applicatif, par exemple, dans la *fonction de chemin d'accès*.
Mais elle est à votre disposition si vous en avez besoin. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Si vous souhaitez intercepter des exceptions et créer une réponse personnalisée en fonction de cela, créez un [Gestionnaire d'exceptions personnalisé](../handling-errors.md#install-custom-exception-handlers).
## Utiliser des dépendances avec `yield` et `except` { #dependencies-with-yield-and-except }
Si vous interceptez une exception avec `except` dans une dépendance avec `yield` et que vous ne la relancez pas (ou que vous ne levez pas une nouvelle exception), FastAPI ne pourra pas remarquer qu'il y a eu une exception, de la même manière que cela se produirait avec Python classique :
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
Dans ce cas, le client verra une réponse *HTTP 500 Internal Server Error* comme il se doit, étant donné que nous ne levons pas de `HTTPException` ou similaire, mais le serveur **n'aura aucun logs** ni aucune autre indication de l'erreur. 😱
### Toujours `raise` dans les dépendances avec `yield` et `except` { #always-raise-in-dependencies-with-yield-and-except }
Si vous interceptez une exception dans une dépendance avec `yield`, à moins de lever une autre `HTTPException` ou similaire, **vous devez relancer l'exception d'origine**.
Vous pouvez relancer la même exception avec `raise` :
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
À présent, le client recevra la même réponse *HTTP 500 Internal Server Error*, mais le serveur aura notre `InternalError` personnalisé dans les logs. 😎
## Comprendre l'exécution des dépendances avec `yield` { #execution-of-dependencies-with-yield }
La séquence d'exécution ressemble plus ou moins à ce diagramme. Le temps s'écoule de haut en bas. Et chaque colonne représente une des parties qui interagit ou exécute du code.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info
Une **seule réponse** sera envoyée au client. Il peut s'agir d'une des réponses d'erreur ou de la réponse provenant du *chemin d'accès*.
Après l'envoi de l'une de ces réponses, aucune autre réponse ne peut être envoyée.
///
/// tip | Astuce
Si vous levez une exception dans le code de la *fonction de chemin d'accès*, elle sera transmise aux dépendances avec `yield`, y compris `HTTPException`. Dans la plupart des cas, vous voudrez relancer cette même exception ou en lever une nouvelle depuis la dépendance avec `yield` pour vous assurer qu'elle est correctement gérée.
///
## Utiliser la sortie anticipée et `scope` { #early-exit-and-scope }
Normalement, le code de sortie des dépendances avec `yield` est exécuté **après la réponse** envoyée au client.
Mais si vous savez que vous n'aurez pas besoin d'utiliser la dépendance après être revenu de la *fonction de chemin d'accès*, vous pouvez utiliser `Depends(scope="function")` pour indiquer à FastAPI qu'il doit fermer la dépendance après le retour de la *fonction de chemin d'accès*, mais **avant** que la **réponse ne soit envoyée**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` reçoit un paramètre `scope` qui peut être :
* « function » : démarrer la dépendance avant la *fonction de chemin d'accès* qui gère la requête, terminer la dépendance après la fin de la *fonction de chemin d'accès*, mais **avant** que la réponse ne soit renvoyée au client. Ainsi, la fonction de dépendance sera exécutée **autour** de la *fonction de chemin d'accès*.
* « request » : démarrer la dépendance avant la *fonction de chemin d'accès* qui gère la requête (similaire à l'utilisation de « function »), mais terminer **après** que la réponse a été renvoyée au client. Ainsi, la fonction de dépendance sera exécutée **autour** du cycle **requête** et réponse.
S'il n'est pas spécifié et que la dépendance utilise `yield`, le `scope` sera par défaut « request ».
### Définir `scope` pour les sous-dépendances { #scope-for-sub-dependencies }
Lorsque vous déclarez une dépendance avec un `scope="request"` (par défaut), toute sous-dépendance doit également avoir un `scope` de « request ».
Mais une dépendance avec un `scope` de « function » peut avoir des dépendances avec un `scope` de « function » et un `scope` de « request ».
Cela vient du fait que toute dépendance doit pouvoir exécuter son code de sortie avant ses sous-dépendances, car elle pourrait encore avoir besoin de les utiliser pendant son code de sortie.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## Utiliser des dépendances avec `yield`, `HTTPException`, `except` et Background Tasks { #dependencies-with-yield-httpexception-except-and-background-tasks }
Les dépendances avec `yield` ont évolué au fil du temps pour couvrir différents cas d'utilisation et corriger certains problèmes.
Si vous souhaitez voir ce qui a changé dans différentes versions de FastAPI, vous pouvez en savoir plus dans le guide avancé, dans [Dépendances avancées - Dépendances avec `yield`, `HTTPException`, `except` et Background Tasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Gestionnaires de contexte { #context-managers }
### Que sont les « Context Managers » { #what-are-context-managers }
Les « Context Managers » sont des objets Python que vous pouvez utiliser dans une instruction `with`.
Par exemple, [vous pouvez utiliser `with` pour lire un fichier](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) :
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
En coulisse, `open("./somefile.txt")` crée un objet appelé « Context Manager ».
Lorsque le bloc `with` se termine, il s'assure de fermer le fichier, même s'il y a eu des exceptions.
Lorsque vous créez une dépendance avec `yield`, **FastAPI** créera en interne un gestionnaire de contexte pour celle-ci et le combinera avec d'autres outils associés.
### Utiliser des gestionnaires de contexte dans des dépendances avec `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Alertes
C'est, plus ou moins, une idée « avancée ».
Si vous débutez avec **FastAPI**, vous voudrez peut-être l'ignorer pour le moment.
///
En Python, vous pouvez créer des gestionnaires de contexte en [créant une classe avec deux méthodes : `__enter__()` et `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
Vous pouvez également les utiliser dans des dépendances **FastAPI** avec `yield` en utilisant
des instructions `with` ou `async with` à l'intérieur de la fonction de dépendance :
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Astuce
Une autre façon de créer un gestionnaire de contexte consiste à utiliser :
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) ou
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
pour décorer une fonction avec un unique `yield`.
C'est ce que **FastAPI** utilise en interne pour les dépendances avec `yield`.
Mais vous n'avez pas à utiliser ces décorateurs pour les dépendances FastAPI (et vous ne devriez pas).
FastAPI le fera pour vous en interne.
///
================================================
FILE: docs/fr/docs/tutorial/dependencies/global-dependencies.md
================================================
# Dépendances globales { #global-dependencies }
Pour certains types d'applications, vous pourriez vouloir ajouter des dépendances à l'application entière.
Comme vous pouvez [ajouter des `dependencies` aux *décorateurs de chemin d'accès*](dependencies-in-path-operation-decorators.md), vous pouvez les ajouter à l'application `FastAPI`.
Dans ce cas, elles seront appliquées à tous les *chemins d'accès* de l'application :
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Et toutes les idées de la section sur [l'ajout de `dependencies` aux *décorateurs de chemin d'accès*](dependencies-in-path-operation-decorators.md) s'appliquent toujours, mais dans ce cas à tous les *chemins d'accès* de l'application.
## Dépendances pour des groupes de *chemins d'accès* { #dependencies-for-groups-of-path-operations }
Plus tard, en lisant comment structurer des applications plus grandes ([Applications plus grandes - Plusieurs fichiers](../../tutorial/bigger-applications.md)), éventuellement avec plusieurs fichiers, vous apprendrez comment déclarer un unique paramètre `dependencies` pour un groupe de *chemins d'accès*.
================================================
FILE: docs/fr/docs/tutorial/dependencies/index.md
================================================
# Dépendances { #dependencies }
**FastAPI** dispose d’un système d’**Injection de dépendances** très puissant mais intuitif.
Il est conçu pour être très simple à utiliser, et pour faciliter l’intégration d’autres composants à **FastAPI** pour n’importe quel développeur.
## Qu’est-ce que « l’injection de dépendances » { #what-is-dependency-injection }
L’**« injection de dépendances »** signifie, en programmation, qu’il existe un moyen pour votre code (dans ce cas, vos fonctions de chemins d’accès) de déclarer ce dont il a besoin pour fonctionner et utiliser : « dépendances ».
Ensuite, ce système (dans ce cas **FastAPI**) se charge de faire tout le nécessaire pour fournir à votre code ces dépendances requises (« injecter » les dépendances).
C’est très utile lorsque vous avez besoin de :
* Avoir de la logique partagée (la même logique de code encore et encore).
* Partager des connexions à la base de données.
* Imposer la sécurité, l’authentification, des exigences de rôles, etc.
* Et bien d’autres choses ...
Tout cela, en minimisant la répétition de code.
## Premiers pas { #first-steps }
Voyons un exemple très simple. Il sera tellement simple qu’il n’est pas très utile, pour l’instant.
Mais de cette façon nous pouvons nous concentrer sur le fonctionnement du système d’**injection de dépendances**.
### Créer une dépendance, ou « dependable » { #create-a-dependency-or-dependable }
Concentrons-nous d’abord sur la dépendance.
C’est simplement une fonction qui peut prendre tous les mêmes paramètres qu’une fonction de chemin d’accès peut prendre :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
C’est tout.
**2 lignes**.
Et elle a la même forme et structure que toutes vos fonctions de chemins d’accès.
Vous pouvez la considérer comme une fonction de chemin d’accès sans le « décorateur » (sans le `@app.get("/some-path")`).
Et elle peut retourner tout ce que vous voulez.
Dans ce cas, cette dépendance attend :
* Un paramètre de requête optionnel `q` qui est une `str`.
* Un paramètre de requête optionnel `skip` qui est un `int`, et vaut `0` par défaut.
* Un paramètre de requête optionnel `limit` qui est un `int`, et vaut `100` par défaut.
Puis elle retourne simplement un `dict` contenant ces valeurs.
/// info
FastAPI a ajouté la prise en charge de `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d’utiliser `Annotated`.
Vous devez vous assurer de [mettre à niveau la version de FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) vers au moins la 0.95.1 avant d’utiliser `Annotated`.
///
### Importer `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Déclarer la dépendance, dans le « dependant » { #declare-the-dependency-in-the-dependant }
De la même manière que vous utilisez `Body`, `Query`, etc. avec les paramètres de votre fonction de chemin d’accès, utilisez `Depends` avec un nouveau paramètre :
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Même si vous utilisez `Depends` dans les paramètres de votre fonction de la même façon que `Body`, `Query`, etc., `Depends` fonctionne un peu différemment.
Vous ne donnez à `Depends` qu’un seul paramètre.
Ce paramètre doit être quelque chose comme une fonction.
Vous ne l’appelez pas directement (n’ajoutez pas de parenthèses à la fin), vous le passez simplement en paramètre à `Depends()`.
Et cette fonction prend des paramètres de la même manière que les fonctions de chemins d’accès.
/// tip | Astuce
Vous verrez quelles autres « choses », en plus des fonctions, peuvent être utilisées comme dépendances dans le prochain chapitre.
///
Chaque fois qu’une nouvelle requête arrive, **FastAPI** se charge de :
* Appeler votre fonction de dépendance (« dependable ») avec les bons paramètres.
* Récupérer le résultat de votre fonction.
* Affecter ce résultat au paramètre dans votre fonction de chemin d’accès.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
De cette façon vous écrivez le code partagé une seule fois et **FastAPI** se charge de l’appeler pour vos chemins d’accès.
/// check | Vérifications
Notez que vous n’avez pas à créer une classe spéciale et à la passer quelque part à **FastAPI** pour l’« enregistrer » ou quoi que ce soit de similaire.
Vous la passez simplement à `Depends` et **FastAPI** sait faire le reste.
///
## Partager des dépendances `Annotated` { #share-annotated-dependencies }
Dans les exemples ci-dessus, vous voyez qu’il y a un tout petit peu de **duplication de code**.
Lorsque vous devez utiliser la dépendance `common_parameters()`, vous devez écrire tout le paramètre avec l’annotation de type et `Depends()` :
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Mais comme nous utilisons `Annotated`, nous pouvons stocker cette valeur `Annotated` dans une variable et l’utiliser à plusieurs endroits :
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Astuce
C’est simplement du Python standard, cela s’appelle un « alias de type », ce n’est en fait pas spécifique à **FastAPI**.
Mais comme **FastAPI** est basé sur les standards Python, y compris `Annotated`, vous pouvez utiliser cette astuce dans votre code. 😎
///
Les dépendances continueront de fonctionner comme prévu, et la **meilleure partie** est que **l’information de type sera conservée**, ce qui signifie que votre éditeur pourra continuer à vous fournir **l’autocomplétion**, **des erreurs en ligne**, etc. Idem pour d’autres outils comme `mypy`.
Cela sera particulièrement utile lorsque vous l’utiliserez dans une **grande base de code** où vous utilisez **les mêmes dépendances** encore et encore dans **de nombreux chemins d’accès**.
## Utiliser `async` ou non { #to-async-or-not-to-async }
Comme les dépendances seront aussi appelées par **FastAPI** (tout comme vos fonctions de chemins d’accès), les mêmes règles s’appliquent lors de la définition de vos fonctions.
Vous pouvez utiliser `async def` ou un `def` normal.
Et vous pouvez déclarer des dépendances avec `async def` à l’intérieur de fonctions de chemins d’accès `def` normales, ou des dépendances `def` à l’intérieur de fonctions de chemins d’accès `async def`, etc.
Peu importe. **FastAPI** saura quoi faire.
/// note | Remarque
Si vous ne savez pas, consultez la section [Async : *« Pressé ? »*](../../async.md#in-a-hurry) à propos de `async` et `await` dans la documentation.
///
## Intégrer à OpenAPI { #integrated-with-openapi }
Toutes les déclarations de requête, validations et exigences de vos dépendances (et sous-dépendances) seront intégrées dans le même schéma OpenAPI.
Ainsi, la documentation interactive contiendra aussi toutes les informations issues de ces dépendances :
## Utilisation simple { #simple-usage }
Si vous y regardez de près, les fonctions de chemins d’accès sont déclarées pour être utilisées chaque fois qu’un « chemin » et une « opération » correspondent, puis **FastAPI** se charge d’appeler la fonction avec les bons paramètres, en extrayant les données de la requête.
En réalité, tous (ou la plupart) des frameworks web fonctionnent de cette manière.
Vous n’appelez jamais ces fonctions directement. Elles sont appelées par votre framework (dans ce cas, **FastAPI**).
Avec le système d’injection de dépendances, vous pouvez aussi indiquer à **FastAPI** que votre fonction de chemin d’accès « dépend » également d’autre chose qui doit être exécuté avant votre fonction de chemin d’accès, et **FastAPI** se chargera de l’exécuter et d’« injecter » les résultats.
D’autres termes courants pour cette même idée « d’injection de dépendances » sont :
* ressources
* fournisseurs
* services
* injectables
* composants
## Plug-ins **FastAPI** { #fastapi-plug-ins }
Les intégrations et « plug-ins » peuvent être construits en utilisant le système d’**injection de dépendances**. Mais en réalité, il n’y a **pas besoin de créer des « plug-ins »**, car en utilisant des dépendances il est possible de déclarer un nombre infini d’intégrations et d’interactions qui deviennent disponibles pour vos fonctions de chemins d’accès.
Et les dépendances peuvent être créées de manière très simple et intuitive, ce qui vous permet d’importer juste les packages Python dont vous avez besoin, et de les intégrer à vos fonctions d’API en quelques lignes de code, *littéralement*.
Vous verrez des exemples de cela dans les prochains chapitres, à propos des bases de données relationnelles et NoSQL, de la sécurité, etc.
## Compatibilité **FastAPI** { #fastapi-compatibility }
La simplicité du système d’injection de dépendances rend **FastAPI** compatible avec :
* toutes les bases de données relationnelles
* les bases de données NoSQL
* les packages externes
* les API externes
* les systèmes d’authentification et d’autorisation
* les systèmes de supervision d’usage d’API
* les systèmes d’injection de données de réponse
* etc.
## Simple et puissant { #simple-and-powerful }
Bien que le système hiérarchique d’injection de dépendances soit très simple à définir et à utiliser, il reste très puissant.
Vous pouvez définir des dépendances qui, à leur tour, peuvent définir leurs propres dépendances.
Au final, un arbre hiérarchique de dépendances est construit, et le système d’**injection de dépendances** se charge de résoudre toutes ces dépendances pour vous (et leurs sous-dépendances) et de fournir (injecter) les résultats à chaque étape.
Par exemple, supposons que vous ayez 4 endpoints d’API (chemins d’accès) :
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
alors vous pourriez ajouter différentes exigences d’autorisations pour chacun d’eux uniquement avec des dépendances et des sous-dépendances :
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Intégrer à **OpenAPI** { #integrated-with-openapi_1 }
Toutes ces dépendances, tout en déclarant leurs exigences, ajoutent également des paramètres, des validations, etc. à vos chemins d’accès.
**FastAPI** se chargera d’ajouter le tout au schéma OpenAPI, afin que cela apparaisse dans les systèmes de documentation interactive.
================================================
FILE: docs/fr/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Sous-dépendances { #sub-dependencies }
Vous pouvez créer des dépendances qui ont des sous-dépendances.
Elles peuvent être aussi profondes que nécessaire.
**FastAPI** se chargera de les résoudre.
## Créer une première dépendance « dependable » { #first-dependency-dependable }
Vous pouvez créer une première dépendance (« dependable ») comme :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Elle déclare un paramètre de requête optionnel `q` de type `str`, puis le retourne simplement.
C'est assez simple (pas très utile), mais cela nous aidera à nous concentrer sur le fonctionnement des sous-dépendances.
## Créer une seconde dépendance, « dependable » et « dependant » { #second-dependency-dependable-and-dependant }
Vous pouvez ensuite créer une autre fonction de dépendance (un « dependable ») qui, en même temps, déclare sa propre dépendance (elle est donc aussi un « dependant ») :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Concentrons-nous sur les paramètres déclarés :
- Même si cette fonction est elle‑même une dépendance (« dependable »), elle déclare aussi une autre dépendance (elle « dépend » d'autre chose).
- Elle dépend de `query_extractor` et affecte la valeur renvoyée au paramètre `q`.
- Elle déclare également un cookie `last_query` optionnel, de type `str`.
- Si l'utilisateur n'a fourni aucune requête `q`, nous utilisons la dernière requête utilisée, que nous avons enregistrée auparavant dans un cookie.
## Utiliser la dépendance { #use-the-dependency }
Nous pouvons ensuite utiliser la dépendance avec :
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info
Notez que nous ne déclarons qu'une seule dépendance dans la *fonction de chemin d'accès*, `query_or_cookie_extractor`.
Mais **FastAPI** saura qu'il doit d'abord résoudre `query_extractor`, pour passer ses résultats à `query_or_cookie_extractor` lors de son appel.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Utiliser la même dépendance plusieurs fois { #using-the-same-dependency-multiple-times }
Si l'une de vos dépendances est déclarée plusieurs fois pour le même *chemin d'accès*, par exemple si plusieurs dépendances ont une sous-dépendance commune, **FastAPI** saura n'appeler cette sous-dépendance qu'une seule fois par requête.
Et il enregistrera la valeur renvoyée dans un « cache » et la transmettra à tous les « dependants » qui en ont besoin dans cette requête spécifique, au lieu d'appeler la dépendance plusieurs fois pour la même requête.
Dans un scénario avancé où vous savez que vous avez besoin que la dépendance soit appelée à chaque étape (éventuellement plusieurs fois) dans la même requête au lieu d'utiliser la valeur « mise en cache », vous pouvez définir le paramètre `use_cache=False` lors de l'utilisation de `Depends` :
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ non annoté
/// tip | Astuce
Privilégiez la version `Annotated` si possible.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Récapituler { #recap }
En dehors de tout le jargon utilisé ici, le système d'**injection de dépendances** est assez simple.
Ce ne sont que des fonctions qui ressemblent aux *fonctions de chemin d'accès*.
Mais il est très puissant et vous permet de déclarer des « graphes » (arbres) de dépendances imbriquées aussi profondément que vous le souhaitez.
/// tip | Astuce
Tout cela peut ne pas sembler très utile avec ces exemples simples.
Mais vous verrez à quel point c'est utile dans les chapitres sur la **sécurité**.
Et vous verrez aussi la quantité de code que cela vous fera économiser.
///
================================================
FILE: docs/fr/docs/tutorial/encoder.md
================================================
# Encodeur compatible JSON { #json-compatible-encoder }
Il existe des cas où vous pourriez avoir besoin de convertir un type de données (comme un modèle Pydantic) en quelque chose de compatible avec JSON (comme un `dict`, `list`, etc.).
Par exemple, si vous devez le stocker dans une base de données.
Pour cela, **FastAPI** fournit une fonction `jsonable_encoder()`.
## Utiliser `jsonable_encoder` { #using-the-jsonable-encoder }
Imaginons que vous ayez une base de données `fake_db` qui ne reçoit que des données compatibles JSON.
Par exemple, elle ne reçoit pas d'objets `datetime`, car ceux-ci ne sont pas compatibles avec JSON.
Ainsi, un objet `datetime` doit être converti en une `str` contenant les données au [format ISO](https://en.wikipedia.org/wiki/ISO_8601).
De la même manière, cette base de données n'accepterait pas un modèle Pydantic (un objet avec des attributs), seulement un `dict`.
Vous pouvez utiliser `jsonable_encoder` pour cela.
Elle reçoit un objet, comme un modèle Pydantic, et renvoie une version compatible JSON :
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
Dans cet exemple, elle convertirait le modèle Pydantic en `dict`, et le `datetime` en `str`.
Le résultat de son appel est quelque chose qui peut être encodé avec la fonction standard de Python [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
Elle ne renvoie pas une grande `str` contenant les données au format JSON (sous forme de chaîne). Elle renvoie une structure de données standard de Python (par ex. un `dict`) avec des valeurs et sous-valeurs toutes compatibles avec JSON.
/// note | Remarque
`jsonable_encoder` est en fait utilisée par **FastAPI** en interne pour convertir des données. Mais elle est utile dans de nombreux autres scénarios.
///
================================================
FILE: docs/fr/docs/tutorial/extra-data-types.md
================================================
# Types de données supplémentaires { #extra-data-types }
Jusqu'à présent, vous avez utilisé des types de données courants, comme :
* `int`
* `float`
* `str`
* `bool`
Mais vous pouvez aussi utiliser des types de données plus complexes.
Et vous bénéficierez toujours des mêmes fonctionnalités que jusqu'à présent :
* Excellente prise en charge dans l'éditeur.
* Conversion des données à partir des requêtes entrantes.
* Conversion des données pour les données de réponse.
* Validation des données.
* Annotations et documentation automatiques.
## Autres types de données { #other-data-types }
Voici quelques types de données supplémentaires que vous pouvez utiliser :
* `UUID` :
* Un « identifiant universel unique » standard, couramment utilisé comme ID dans de nombreuses bases de données et systèmes.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str`.
* `datetime.datetime` :
* Un `datetime.datetime` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `2008-09-15T15:53:00+05:00`.
* `datetime.date` :
* `datetime.date` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `2008-09-15`.
* `datetime.time` :
* Un `datetime.time` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `str` au format ISO 8601, par exemple : `14:23:55.003`.
* `datetime.timedelta` :
* Un `datetime.timedelta` Python.
* Dans les requêtes et les réponses, il sera représenté sous forme de `float` de secondes totales.
* Pydantic permet aussi de le représenter sous la forme d'un « encodage de différence de temps ISO 8601 », [voir la documentation pour plus d'informations](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset` :
* Dans les requêtes et les réponses, traité de la même manière qu'un `set` :
* Dans les requêtes, une liste sera lue, les doublons éliminés, puis convertie en `set`.
* Dans les réponses, le `set` sera converti en `list`.
* Le schéma généré indiquera que les valeurs du `set` sont uniques (en utilisant `uniqueItems` de JSON Schema).
* `bytes` :
* `bytes` Python standard.
* Dans les requêtes et les réponses, traité comme une `str`.
* Le schéma généré indiquera qu'il s'agit d'une `str` avec le « format » `binary`.
* `Decimal` :
* `Decimal` Python standard.
* Dans les requêtes et les réponses, géré de la même manière qu'un `float`.
* Vous pouvez consulter tous les types de données Pydantic valides ici : [Types de données Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
## Exemple { #example }
Voici un exemple de *chemin d'accès* avec des paramètres utilisant certains des types ci-dessus.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Notez que les paramètres à l'intérieur de la fonction ont leur type de données naturel et que vous pouvez, par exemple, effectuer des manipulations de dates normales, comme :
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/fr/docs/tutorial/extra-models.md
================================================
# Modèles supplémentaires { #extra-models }
En poursuivant l'exemple précédent, il est courant d'avoir plusieurs modèles liés.
C'est particulièrement vrai pour les modèles d'utilisateur, car :
* Le modèle d'entrée doit pouvoir contenir un mot de passe.
* Le modèle de sortie ne doit pas avoir de mot de passe.
* Le modèle de base de données devra probablement avoir un mot de passe haché.
/// danger | Danger
Ne stockez jamais les mots de passe des utilisateurs en clair. Stockez toujours un « hachage sécurisé » que vous pourrez ensuite vérifier.
Si vous ne savez pas ce que c'est, vous apprendrez ce qu'est un « hachage de mot de passe » dans les [chapitres sur la sécurité](security/simple-oauth2.md#password-hashing).
///
## Utiliser plusieurs modèles { #multiple-models }
Voici une idée générale de l'apparence des modèles avec leurs champs de mot de passe et les endroits où ils sont utilisés :
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### À propos de `**user_in.model_dump()` { #about-user-in-model-dump }
#### La méthode `.model_dump()` de Pydantic { #pydantics-model-dump }
`user_in` est un modèle Pydantic de classe `UserIn`.
Les modèles Pydantic ont une méthode `.model_dump()` qui renvoie un `dict` avec les données du modèle.
Ainsi, si nous créons un objet Pydantic `user_in` comme :
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
et que nous appelons ensuite :
```Python
user_dict = user_in.model_dump()
```
nous avons maintenant un `dict` avec les données dans la variable `user_dict` (c'est un `dict` au lieu d'un objet modèle Pydantic).
Et si nous appelons :
```Python
print(user_dict)
```
nous obtiendrions un `dict` Python contenant :
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Déballer un `dict` { #unpacking-a-dict }
Si nous prenons un `dict` comme `user_dict` et que nous le passons à une fonction (ou une classe) avec `**user_dict`, Python va « déballer » ce `dict`. Il passera les clés et valeurs de `user_dict` directement comme arguments nommés.
Ainsi, en reprenant `user_dict` ci-dessus, écrire :
```Python
UserInDB(**user_dict)
```
aurait pour résultat quelque chose d'équivalent à :
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Ou plus exactement, en utilisant `user_dict` directement, quels que soient ses contenus futurs :
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Créer un modèle Pydantic à partir du contenu d'un autre { #a-pydantic-model-from-the-contents-of-another }
Comme dans l'exemple ci-dessus nous avons obtenu `user_dict` depuis `user_in.model_dump()`, ce code :
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
serait équivalent à :
```Python
UserInDB(**user_in.model_dump())
```
... parce que `user_in.model_dump()` est un `dict`, et nous demandons ensuite à Python de « déballer » ce `dict` en le passant à `UserInDB` précédé de `**`.
Ainsi, nous obtenons un modèle Pydantic à partir des données d'un autre modèle Pydantic.
#### Déballer un `dict` et ajouter des mots-clés supplémentaires { #unpacking-a-dict-and-extra-keywords }
Et en ajoutant ensuite l'argument nommé supplémentaire `hashed_password=hashed_password`, comme ici :
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
... revient à :
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Alertes
Les fonctions auxiliaires `fake_password_hasher` et `fake_save_user` ne servent qu'à démontrer un flux de données possible, mais elles n'offrent évidemment aucune sécurité réelle.
///
## Réduire la duplication { #reduce-duplication }
Réduire la duplication de code est l'une des idées centrales de **FastAPI**.
La duplication de code augmente les risques de bogues, de problèmes de sécurité, de désynchronisation du code (lorsque vous mettez à jour un endroit mais pas les autres), etc.
Et ces modèles partagent beaucoup de données et dupliquent des noms et types d'attributs.
Nous pouvons faire mieux.
Nous pouvons déclarer un modèle `UserBase` qui sert de base à nos autres modèles. Ensuite, nous pouvons créer des sous-classes de ce modèle qui héritent de ses attributs (déclarations de type, validation, etc.).
Toutes les conversions de données, validations, documentation, etc., fonctionneront comme d'habitude.
De cette façon, nous pouvons ne déclarer que les différences entre les modèles (avec `password` en clair, avec `hashed_password` et sans mot de passe) :
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` ou `anyOf` { #union-or-anyof }
Vous pouvez déclarer qu'une réponse est l'`Union` de deux types ou plus, ce qui signifie que la réponse peut être n'importe lequel d'entre eux.
Cela sera défini dans OpenAPI avec `anyOf`.
Pour ce faire, utilisez l'annotation de type Python standard [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union) :
/// note | Remarque
Lors de la définition d'une [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions), incluez d'abord le type le plus spécifique, suivi du type le moins spécifique. Dans l'exemple ci-dessous, le type le plus spécifique `PlaneItem` précède `CarItem` dans `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` en Python 3.10 { #union-in-python-3-10 }
Dans cet exemple, nous passons `Union[PlaneItem, CarItem]` comme valeur de l'argument `response_model`.
Comme nous le passons comme valeur d'un argument au lieu de l'utiliser dans une annotation de type, nous devons utiliser `Union` même en Python 3.10.
S'il s'agissait d'une annotation de type, nous pourrions utiliser la barre verticale, comme :
```Python
some_variable: PlaneItem | CarItem
```
Mais si nous écrivons cela dans l'affectation `response_model=PlaneItem | CarItem`, nous obtiendrons une erreur, car Python essaierait d'effectuer une « opération invalide » entre `PlaneItem` et `CarItem` au lieu de l'interpréter comme une annotation de type.
## Liste de modèles { #list-of-models }
De la même manière, vous pouvez déclarer des réponses contenant des listes d'objets.
Pour cela, utilisez le `list` Python standard :
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Réponse avec un `dict` arbitraire { #response-with-arbitrary-dict }
Vous pouvez également déclarer une réponse en utilisant un simple `dict` arbitraire, en déclarant uniquement le type des clés et des valeurs, sans utiliser de modèle Pydantic.
C'est utile si vous ne connaissez pas à l'avance les noms de champs/attributs valides (qui seraient nécessaires pour un modèle Pydantic).
Dans ce cas, vous pouvez utiliser `dict` :
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Récapitulatif { #recap }
Utilisez plusieurs modèles Pydantic et héritez librement selon chaque cas.
Vous n'avez pas besoin d'avoir un seul modèle de données par entité si cette entité doit pouvoir avoir différents « états ». Comme pour l'« entité » utilisateur, avec un état incluant `password`, `password_hash` et sans mot de passe.
================================================
FILE: docs/fr/docs/tutorial/first-steps.md
================================================
# Démarrer { #first-steps }
Le fichier **FastAPI** le plus simple possible pourrait ressembler à ceci :
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copiez cela dans un fichier `main.py`.
Démarrez le serveur en direct :
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Dans la sortie, il y a une ligne semblable à :
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Cette ligne montre l’URL où votre application est servie, sur votre machine locale.
### Vérifier { #check-it }
Ouvrez votre navigateur à l’adresse [http://127.0.0.1:8000](http://127.0.0.1:8000).
Vous verrez la réponse JSON suivante :
```JSON
{"message": "Hello World"}
```
### Documentation interactive de l’API { #interactive-api-docs }
Allez maintenant sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez la documentation interactive de l’API générée automatiquement (fournie par [Swagger UI](https://github.com/swagger-api/swagger-ui)) :

### Documentation alternative de l’API { #alternative-api-docs }
Et maintenant, allez sur [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Vous verrez la documentation automatique alternative (fournie par [ReDoc](https://github.com/Rebilly/ReDoc)) :

### OpenAPI { #openapi }
**FastAPI** génère un « schéma » contenant toute votre API en utilisant le standard **OpenAPI** pour définir des API.
#### « Schéma » { #schema }
Un « schéma » est une définition ou une description de quelque chose. Pas le code qui l’implémente, mais uniquement une description abstraite.
#### « Schéma » d’API { #api-schema }
Ici, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) est une spécification qui dicte comment définir le schéma de votre API.
Cette définition de schéma inclut les chemins de votre API, les paramètres possibles qu’ils prennent, etc.
#### « Schéma » de données { #data-schema }
Le terme « schéma » peut également faire référence à la forme d’une donnée, comme un contenu JSON.
Dans ce cas, cela désignerait les attributs JSON, ainsi que leurs types, etc.
#### OpenAPI et JSON Schema { #openapi-and-json-schema }
OpenAPI définit un schéma d’API pour votre API. Et ce schéma inclut des définitions (ou « schémas ») des données envoyées et reçues par votre API en utilisant **JSON Schema**, le standard pour les schémas de données JSON.
#### Voir le `openapi.json` { #check-the-openapi-json }
Si vous êtes curieux de voir à quoi ressemble le schéma OpenAPI brut, FastAPI génère automatiquement un JSON (schéma) avec les descriptions de toute votre API.
Vous pouvez le voir directement à l’adresse : [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Il affichera un JSON commençant par quelque chose comme :
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### À quoi sert OpenAPI { #what-is-openapi-for }
Le schéma OpenAPI est ce qui alimente les deux systèmes de documentation interactive inclus.
Et il existe des dizaines d’alternatives, toutes basées sur OpenAPI. Vous pourriez facilement ajouter n’importe laquelle de ces alternatives à votre application construite avec **FastAPI**.
Vous pourriez également l’utiliser pour générer du code automatiquement, pour les clients qui communiquent avec votre API. Par exemple, des applications frontend, mobiles ou IoT.
### Configurer le `entrypoint` de l’application dans `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Vous pouvez configurer l’emplacement de votre application dans un fichier `pyproject.toml` comme :
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Ce `entrypoint` indiquera à la commande `fastapi` qu’elle doit importer l’application comme :
```python
from main import app
```
Si votre code est structuré comme :
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Alors vous définiriez le `entrypoint` comme :
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
ce qui équivaudrait à :
```python
from backend.main import app
```
### `fastapi dev` avec un chemin { #fastapi-dev-with-path }
Vous pouvez également passer le chemin du fichier à la commande `fastapi dev`, et elle devinera l’objet d’application FastAPI à utiliser :
```console
$ fastapi dev main.py
```
Mais vous devrez vous souvenir de passer le chemin correct à chaque exécution de la commande `fastapi`.
De plus, d’autres outils pourraient ne pas être capables de le trouver, par exemple l’[Extension VS Code](../editor-support.md) ou [FastAPI Cloud](https://fastapicloud.com), il est donc recommandé d’utiliser le `entrypoint` dans `pyproject.toml`.
### Déployer votre application (optionnel) { #deploy-your-app-optional }
Vous pouvez, si vous le souhaitez, déployer votre application FastAPI sur [FastAPI Cloud](https://fastapicloud.com), allez rejoindre la liste d’attente si ce n’est pas déjà fait. 🚀
Si vous avez déjà un compte **FastAPI Cloud** (nous vous avons invité depuis la liste d’attente 😉), vous pouvez déployer votre application avec une seule commande.
Avant de déployer, vous devez vous assurer que vous êtes connecté :
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Puis déployez votre application :
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
C’est tout ! Vous pouvez maintenant accéder à votre application à cette URL. ✨
## Récapitulatif, étape par étape { #recap-step-by-step }
### Étape 1 : importer `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` est une classe Python qui fournit toutes les fonctionnalités nécessaires à votre API.
/// note | Détails techniques
`FastAPI` est une classe qui hérite directement de `Starlette`.
Vous pouvez donc aussi utiliser toutes les fonctionnalités de [Starlette](https://www.starlette.dev/) avec `FastAPI`.
///
### Étape 2 : créer une « instance » `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Ici, la variable `app` sera une « instance » de la classe `FastAPI`.
Ce sera le point principal d’interaction pour créer toute votre API.
### Étape 3 : créer un « chemin d’accès » { #step-3-create-a-path-operation }
#### Chemin { #path }
« Chemin » fait ici référence à la dernière partie de l’URL à partir du premier `/`.
Donc, dans une URL telle que :
```
https://example.com/items/foo
```
... le chemin serait :
```
/items/foo
```
/// info
Un « chemin » est aussi couramment appelé « endpoint » ou « route ».
///
Lors de la création d’une API, le « chemin » est la manière principale de séparer les « préoccupations » et les « ressources ».
#### Opération { #operation }
« Opération » fait ici référence à l’une des « méthodes » HTTP.
L’une de :
* `POST`
* `GET`
* `PUT`
* `DELETE`
... et les plus exotiques :
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
Dans le protocole HTTP, vous pouvez communiquer avec chaque chemin en utilisant une (ou plusieurs) de ces « méthodes ».
---
En construisant des APIs, vous utilisez normalement ces méthodes HTTP spécifiques pour effectuer une action précise.
En général, vous utilisez :
* `POST` : pour créer des données.
* `GET` : pour lire des données.
* `PUT` : pour mettre à jour des données.
* `DELETE` : pour supprimer des données.
Donc, dans OpenAPI, chacune des méthodes HTTP est appelée une « opération ».
Nous allons donc aussi les appeler « opérations ».
#### Définir un « décorateur de chemin d’accès » { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
Le `@app.get("/")` indique à **FastAPI** que la fonction juste en dessous est chargée de gérer les requêtes qui vont vers :
* le chemin `/`
* en utilisant une get opération
/// info | `@decorator` Info
Cette syntaxe `@something` en Python est appelée un « décorateur ».
Vous la mettez au-dessus d’une fonction. Comme un joli chapeau décoratif (j’imagine que c’est de là que vient le terme 🤷🏻♂).
Un « décorateur » prend la fonction en dessous et fait quelque chose avec.
Dans notre cas, ce décorateur indique à **FastAPI** que la fonction en dessous correspond au **chemin** `/` avec une **opération** `get`.
C’est le « décorateur de chemin d’accès ».
///
Vous pouvez aussi utiliser les autres opérations :
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Ainsi que les plus exotiques :
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Astuce
Vous êtes libre d’utiliser chaque opération (méthode HTTP) comme vous le souhaitez.
**FastAPI** n’impose aucune signification spécifique.
Les informations ici sont présentées comme des lignes directrices, pas comme une obligation.
Par exemple, lorsque vous utilisez GraphQL, vous effectuez normalement toutes les actions en utilisant uniquement des opérations `POST`.
///
### Étape 4 : définir la **fonction de chemin d’accès** { #step-4-define-the-path-operation-function }
Voici notre « fonction de chemin d’accès » :
* **chemin** : `/`.
* **opération** : `get`.
* **fonction** : la fonction sous le « décorateur » (sous `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
C’est une fonction Python.
Elle sera appelée par **FastAPI** chaque fois qu’il recevra une requête vers l’URL « / » en utilisant une opération `GET`.
Dans ce cas, c’est une fonction `async`.
---
Vous pouvez aussi la définir comme une fonction normale au lieu de `async def` :
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Remarque
Si vous ne connaissez pas la différence, consultez [Asynchrone : « Pressé ? »](../async.md#in-a-hurry).
///
### Étape 5 : retourner le contenu { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Vous pouvez retourner un `dict`, une `list`, des valeurs uniques comme `str`, `int`, etc.
Vous pouvez également retourner des modèles Pydantic (vous en verrez plus à ce sujet plus tard).
Il existe de nombreux autres objets et modèles qui seront automatiquement convertis en JSON (y compris des ORM, etc.). Essayez d’utiliser vos favoris, il est fort probable qu’ils soient déjà pris en charge.
### Étape 6 : le déployer { #step-6-deploy-it }
Déployez votre application sur **[FastAPI Cloud](https://fastapicloud.com)** avec une seule commande : `fastapi deploy`. 🎉
#### À propos de FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** est construit par le même auteur et l’équipe derrière **FastAPI**.
Il simplifie le processus de **construction**, de **déploiement** et d’**accès** à une API avec un minimum d’effort.
Il apporte la même **expérience développeur** de création d’applications avec FastAPI au **déploiement** dans le cloud. 🎉
FastAPI Cloud est le sponsor principal et le financeur des projets open source *FastAPI and friends*. ✨
#### Déployer sur d’autres fournisseurs cloud { #deploy-to-other-cloud-providers }
FastAPI est open source et basé sur des standards. Vous pouvez déployer des applications FastAPI chez n’importe quel fournisseur cloud de votre choix.
Suivez les guides de votre fournisseur cloud pour y déployer des applications FastAPI. 🤓
## Récapitulatif { #recap }
* Importez `FastAPI`.
* Créez une instance `app`.
* Écrivez un **décorateur de chemin d’accès** avec des décorateurs comme `@app.get("/")`.
* Définissez une **fonction de chemin d’accès** ; par exemple, `def root(): ...`.
* Exécutez le serveur de développement avec la commande `fastapi dev`.
* Déployez éventuellement votre application avec `fastapi deploy`.
================================================
FILE: docs/fr/docs/tutorial/handling-errors.md
================================================
# Gérer les erreurs { #handling-errors }
Il existe de nombreuses situations où vous devez signaler une erreur à un client qui utilise votre API.
Ce client peut être un navigateur avec un frontend, un code d'un tiers, un appareil IoT, etc.
Vous pourriez avoir besoin d'indiquer au client que :
* Le client n'a pas les privilèges suffisants pour cette opération.
* Le client n'a pas accès à cette ressource.
* L'élément auquel le client tentait d'accéder n'existe pas.
* etc.
Dans ces cas, vous retournez normalement un **code d'état HTTP** dans la plage de **400** (de 400 à 499).
C'est similaire aux codes d'état HTTP 200 (de 200 à 299). Ces codes « 200 » signifient que, d'une certaine manière, la requête a été un « succès ».
Les codes d'état dans la plage des 400 signifient qu'il y a eu une erreur côté client.
Vous souvenez-vous de toutes ces erreurs **« 404 Not Found »** (et des blagues) ?
## Utiliser `HTTPException` { #use-httpexception }
Pour renvoyer au client des réponses HTTP avec des erreurs, vous utilisez `HTTPException`.
### Importer `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Lever une `HTTPException` dans votre code { #raise-an-httpexception-in-your-code }
`HTTPException` est une exception Python normale avec des données supplémentaires pertinentes pour les API.
Comme il s'agit d'une exception Python, vous ne la `return` pas, vous la `raise`.
Cela signifie aussi que si vous êtes dans une fonction utilitaire appelée depuis votre fonction de chemin d'accès, et que vous levez la `HTTPException` à l'intérieur de cette fonction utilitaire, le reste du code de la fonction de chemin d'accès ne s'exécutera pas : la requête sera immédiatement interrompue et l'erreur HTTP issue de la `HTTPException` sera envoyée au client.
L'avantage de lever une exception plutôt que de retourner une valeur apparaîtra plus clairement dans la section sur les Dépendances et la Sécurité.
Dans cet exemple, lorsque le client demande un élément par un ID qui n'existe pas, levez une exception avec un code d'état `404` :
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Réponse résultante { #the-resulting-response }
Si le client demande `http://example.com/items/foo` (un `item_id` « foo »), il recevra un code d'état HTTP 200 et une réponse JSON :
```JSON
{
"item": "The Foo Wrestlers"
}
```
Mais si le client demande `http://example.com/items/bar` (un `item_id` inexistant « bar »), il recevra un code d'état HTTP 404 (l'erreur « not found ») et une réponse JSON :
```JSON
{
"detail": "Item not found"
}
```
/// tip | Astuce
Lorsque vous levez une `HTTPException`, vous pouvez passer n'importe quelle valeur convertible en JSON comme paramètre `detail`, pas uniquement un `str`.
Vous pouvez passer un `dict`, une `list`, etc.
Elles sont gérées automatiquement par **FastAPI** et converties en JSON.
///
## Ajouter des en-têtes personnalisés { #add-custom-headers }
Dans certaines situations, il est utile de pouvoir ajouter des en-têtes personnalisés à l'erreur HTTP. Par exemple, pour certains types de sécurité.
Vous n'aurez probablement pas besoin de l'utiliser directement dans votre code.
Mais si vous en aviez besoin pour un scénario avancé, vous pouvez ajouter des en-têtes personnalisés :
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Installer des gestionnaires d'exception personnalisés { #install-custom-exception-handlers }
Vous pouvez ajouter des gestionnaires d'exception personnalisés avec [les mêmes utilitaires d'exception de Starlette](https://www.starlette.dev/exceptions/).
Supposons que vous ayez une exception personnalisée `UnicornException` que vous (ou une bibliothèque que vous utilisez) pourriez `raise`.
Et vous souhaitez gérer cette exception globalement avec FastAPI.
Vous pouvez ajouter un gestionnaire d'exception personnalisé avec `@app.exception_handler()` :
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Ici, si vous appelez `/unicorns/yolo`, le chemin d'accès va `raise` une `UnicornException`.
Mais elle sera gérée par `unicorn_exception_handler`.
Ainsi, vous recevrez une erreur propre, avec un code d'état HTTP `418` et un contenu JSON :
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.requests import Request` et `from starlette.responses import JSONResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` par simple commodité pour vous, développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette. Il en va de même pour `Request`.
///
## Remplacer les gestionnaires d'exception par défaut { #override-the-default-exception-handlers }
**FastAPI** fournit des gestionnaires d'exception par défaut.
Ces gestionnaires se chargent de renvoyer les réponses JSON par défaut lorsque vous `raise` une `HTTPException` et lorsque la requête contient des données invalides.
Vous pouvez remplacer ces gestionnaires d'exception par les vôtres.
### Remplacer les exceptions de validation de la requête { #override-request-validation-exceptions }
Lorsqu'une requête contient des données invalides, **FastAPI** lève en interne une `RequestValidationError`.
Et il inclut également un gestionnaire d'exception par défaut pour cela.
Pour la remplacer, importez `RequestValidationError` et utilisez-la avec `@app.exception_handler(RequestValidationError)` pour décorer le gestionnaire d'exception.
Le gestionnaire d'exception recevra une `Request` et l'exception.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
À présent, si vous allez sur `/items/foo`, au lieu d'obtenir l'erreur JSON par défaut suivante :
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
vous obtiendrez une version texte, avec :
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Remplacer le gestionnaire d'erreurs `HTTPException` { #override-the-httpexception-error-handler }
De la même manière, vous pouvez remplacer le gestionnaire de `HTTPException`.
Par exemple, vous pourriez vouloir renvoyer une réponse en texte brut au lieu de JSON pour ces erreurs :
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.responses import PlainTextResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` par simple commodité pour vous, le développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
///
/// warning | Alertes
Gardez à l'esprit que la `RequestValidationError` contient l'information du nom de fichier et de la ligne où l'erreur de validation se produit, afin que vous puissiez l'afficher dans vos journaux avec les informations pertinentes si vous le souhaitez.
Mais cela signifie que si vous vous contentez de la convertir en chaîne et de renvoyer cette information directement, vous pourriez divulguer un peu d'information sur votre système. C'est pourquoi, ici, le code extrait et affiche chaque erreur indépendamment.
///
### Utiliser le corps de `RequestValidationError` { #use-the-requestvalidationerror-body }
La `RequestValidationError` contient le `body` qu'elle a reçu avec des données invalides.
Vous pouvez l'utiliser pendant le développement de votre application pour journaliser le corps et le déboguer, le renvoyer à l'utilisateur, etc.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Essayez maintenant d'envoyer un élément invalide comme :
```JSON
{
"title": "towel",
"size": "XL"
}
```
Vous recevrez une réponse vous indiquant que les données sont invalides et contenant le corps reçu :
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### `HTTPException` de FastAPI vs `HTTPException` de Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** a sa propre `HTTPException`.
Et la classe d'erreur `HTTPException` de **FastAPI** hérite de la classe d'erreur `HTTPException` de Starlette.
La seule différence est que la `HTTPException` de **FastAPI** accepte toute donnée sérialisable en JSON pour le champ `detail`, tandis que la `HTTPException` de Starlette n'accepte que des chaînes.
Ainsi, vous pouvez continuer à lever la `HTTPException` de **FastAPI** normalement dans votre code.
Mais lorsque vous enregistrez un gestionnaire d'exception, vous devez l'enregistrer pour la `HTTPException` de Starlette.
De cette façon, si une partie du code interne de Starlette, ou une extension ou un plug-in Starlette, lève une `HTTPException` de Starlette, votre gestionnaire pourra l'intercepter et la traiter.
Dans cet exemple, afin de pouvoir avoir les deux `HTTPException` dans le même code, les exceptions de Starlette sont renommées en `StarletteHTTPException` :
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Réutiliser les gestionnaires d'exception de **FastAPI** { #reuse-fastapis-exception-handlers }
Si vous souhaitez utiliser l'exception avec les mêmes gestionnaires d'exception par défaut de **FastAPI**, vous pouvez importer et réutiliser les gestionnaires d'exception par défaut depuis `fastapi.exception_handlers` :
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
Dans cet exemple, vous vous contentez d'afficher l'erreur avec un message très expressif, mais vous voyez l'idée. Vous pouvez utiliser l'exception puis simplement réutiliser les gestionnaires d'exception par défaut.
================================================
FILE: docs/fr/docs/tutorial/header-param-models.md
================================================
# Modèles de paramètres d'en-tête { #header-parameter-models }
Si vous avez un groupe de **paramètres d'en-tête** liés, vous pouvez créer un **modèle Pydantic** pour les déclarer.
Cela vous permet de **réutiliser le modèle** à **plusieurs endroits** et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Cela est pris en charge depuis la version `0.115.0` de FastAPI. 🤓
///
## Paramètres d'en-tête avec un modèle Pydantic { #header-parameters-with-a-pydantic-model }
Déclarez les **paramètres d'en-tête** dont vous avez besoin dans un **modèle Pydantic**, puis déclarez le paramètre comme `Header` :
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** extrait les données de **chaque champ** depuis les **en-têtes** de la requête et vous fournit le modèle Pydantic que vous avez défini.
## Consulter la documentation { #check-the-docs }
Vous pouvez voir les en-têtes requis dans l'interface de la documentation à `/docs` :
## Interdire les en-têtes supplémentaires { #forbid-extra-headers }
Dans certains cas d'utilisation particuliers (probablement pas très courants), vous pourriez vouloir **restreindre** les en-têtes que vous souhaitez recevoir.
Vous pouvez utiliser la configuration du modèle de Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Si un client essaie d'envoyer des **en-têtes supplémentaires**, il recevra une **réponse d'erreur**.
Par exemple, si le client essaie d'envoyer un en-tête `tool` avec la valeur `plumbus`, il recevra une **réponse d'erreur** lui indiquant que le paramètre d'en-tête `tool` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Désactiver convert_underscores { #disable-convert-underscores }
Comme pour les paramètres d'en-tête classiques, lorsque vous avez des caractères de soulignement dans les noms de paramètres, ils sont **automatiquement convertis en tirets**.
Par exemple, si vous avez un paramètre d'en-tête `save_data` dans le code, l'en-tête HTTP attendu sera `save-data`, et il apparaîtra ainsi dans la documentation.
Si, pour une raison quelconque, vous devez désactiver cette conversion automatique, vous pouvez aussi le faire pour les modèles Pydantic de paramètres d'en-tête.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Alertes
Avant de définir `convert_underscores` à `False`, gardez à l'esprit que certains proxys et serveurs HTTP interdisent l'utilisation d'en-têtes contenant des underscores.
///
## Résumé { #summary }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des **en-têtes** dans **FastAPI**. 😎
================================================
FILE: docs/fr/docs/tutorial/header-params.md
================================================
# Paramètres d'en-tête { #header-parameters }
Vous pouvez définir des paramètres `Header` de la même manière que vous définissez des paramètres `Query`, `Path` et `Cookie`.
## Importer `Header` { #import-header }
Commencez par importer `Header` :
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Déclarer des paramètres `Header` { #declare-header-parameters }
Déclarez ensuite les paramètres d'en-tête en utilisant la même structure qu'avec `Path`, `Query` et `Cookie`.
Vous pouvez définir la valeur par défaut ainsi que tous les paramètres supplémentaires de validation ou d'annotation :
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Détails techniques
`Header` est une classe « sœur » de `Path`, `Query` et `Cookie`. Elle hérite également de la même classe commune `Param`.
Mais rappelez-vous que lorsque vous importez `Query`, `Path`, `Header` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// info
Pour déclarer des en-têtes, vous devez utiliser `Header`, sinon les paramètres seraient interprétés comme des paramètres de requête.
///
## Conversion automatique { #automatic-conversion }
`Header` offre un peu de fonctionnalité supplémentaire par rapport à `Path`, `Query` et `Cookie`.
La plupart des en-têtes standards sont séparés par un caractère « trait d'union », également appelé « signe moins » (`-`).
Mais une variable comme `user-agent` est invalide en Python.
Ainsi, par défaut, `Header` convertit les caractères des noms de paramètres du tiret bas (`_`) en trait d'union (`-`) pour extraire et documenter les en-têtes.
De plus, les en-têtes HTTP ne sont pas sensibles à la casse, vous pouvez donc les déclarer avec le style Python standard (aussi appelé « snake_case »).
Vous pouvez donc utiliser `user_agent` comme vous le feriez normalement dans du code Python, au lieu d'avoir à mettre des majuscules aux premières lettres comme `User_Agent` ou quelque chose de similaire.
Si, pour une raison quelconque, vous devez désactiver la conversion automatique des traits bas en traits d'union, définissez le paramètre `convert_underscores` de `Header` sur `False` :
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Alertes
Avant de définir `convert_underscores` sur `False`, gardez à l'esprit que certains proxies et serveurs HTTP interdisent l'utilisation d'en-têtes contenant des traits bas.
///
## Gérer les en-têtes dupliqués { #duplicate-headers }
Il est possible de recevoir des en-têtes en double. Autrement dit, le même en-tête avec plusieurs valeurs.
Vous pouvez définir ces cas à l'aide d'une liste dans la déclaration de type.
Vous recevrez toutes les valeurs de l'en-tête dupliqué sous forme de `list` Python.
Par exemple, pour déclarer un en-tête `X-Token` qui peut apparaître plusieurs fois, vous pouvez écrire :
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Si vous communiquez avec ce *chemin d'accès* en envoyant deux en-têtes HTTP comme :
```
X-Token: foo
X-Token: bar
```
La réponse ressemblerait à ceci :
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Récapitulatif { #recap }
Déclarez les en-têtes avec `Header`, en suivant le même modèle que pour `Query`, `Path` et `Cookie`.
Et ne vous souciez pas des traits bas dans vos variables, **FastAPI** s'occupe de les convertir.
================================================
FILE: docs/fr/docs/tutorial/index.md
================================================
# Tutoriel - Guide utilisateur { #tutorial-user-guide }
Ce tutoriel vous montre comment utiliser **FastAPI** avec la plupart de ses fonctionnalités, étape par étape.
Chaque section s'appuie progressivement sur les précédentes, mais elle est structurée de manière à séparer les sujets, afin que vous puissiez aller directement à l'un d'entre eux pour répondre à vos besoins spécifiques d'API.
Il est également conçu pour servir de référence ultérieure, afin que vous puissiez revenir voir exactement ce dont vous avez besoin.
## Exécuter le code { #run-the-code }
Tous les blocs de code peuvent être copiés et utilisés directement (il s'agit en fait de fichiers Python testés).
Pour exécuter l'un de ces exemples, copiez le code dans un fichier `main.py`, et démarrez `fastapi dev` :
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Il est **FORTEMENT encouragé** que vous écriviez ou copiez le code, l'éditiez et l'exécutiez localement.
L'utiliser dans votre éditeur est ce qui vous montre vraiment les avantages de FastAPI, en voyant le peu de code que vous avez à écrire, toutes les vérifications de type, l'autocomplétion, etc.
---
## Installer FastAPI { #install-fastapi }
La première étape consiste à installer FastAPI.
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md), de l'activer, puis **d'installer FastAPI** :
/// note | Remarque
Lorsque vous installez avec `pip install "fastapi[standard]"`, cela inclut des dépendances standards optionnelles par défaut, y compris `fastapi-cloud-cli`, qui vous permet de déployer sur [FastAPI Cloud](https://fastapicloud.com).
Si vous ne souhaitez pas avoir ces dépendances optionnelles, vous pouvez à la place installer `pip install fastapi`.
Si vous souhaitez installer les dépendances standard mais sans `fastapi-cloud-cli`, vous pouvez installer avec `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip | Astuce
FastAPI dispose d'une [extension officielle pour VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (et Cursor), qui fournit de nombreuses fonctionnalités, notamment un explorateur de chemins d'accès, une recherche de chemins d'accès, la navigation CodeLens dans les tests (aller à la définition depuis les tests), ainsi que le déploiement et les journaux FastAPI Cloud, le tout depuis votre éditeur.
///
## Guide d'utilisation avancé { #advanced-user-guide }
Il existe également un **Guide d'utilisation avancé** que vous pouvez lire plus tard après ce **Tutoriel - Guide d'utilisation**.
Le **Guide d'utilisation avancé**, qui s'appuie sur cette base, utilise les mêmes concepts et vous apprend quelques fonctionnalités supplémentaires.
Mais vous devez d'abord lire le **Tutoriel - Guide d'utilisation** (ce que vous êtes en train de lire en ce moment).
Il est conçu pour que vous puissiez construire une application complète avec seulement le **Tutoriel - Guide d'utilisation**, puis l'étendre de différentes manières, en fonction de vos besoins, en utilisant certaines des idées supplémentaires du **Guide d'utilisation avancé**.
================================================
FILE: docs/fr/docs/tutorial/metadata.md
================================================
# Métadonnées et URL des documents { #metadata-and-docs-urls }
Vous pouvez personnaliser plusieurs configurations de métadonnées dans votre application **FastAPI**.
## Métadonnées pour l'API { #metadata-for-api }
Vous pouvez définir les champs suivants qui sont utilisés dans la spécification OpenAPI et les interfaces utilisateur de documentation automatique de l’API :
| Paramètre | Type | Description |
|------------|------|-------------|
| `title` | `str` | Le titre de l’API. |
| `summary` | `str` | Un court résumé de l’API. Disponible depuis OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Une brève description de l’API. Elle peut utiliser Markdown. |
| `version` | `string` | La version de l’API. C’est la version de votre propre application, pas d’OpenAPI. Par exemple `2.5.0`. |
| `terms_of_service` | `str` | Une URL vers les Conditions d’utilisation de l’API. Le cas échéant, il doit s’agir d’une URL. |
| `contact` | `dict` | Les informations de contact pour l’API exposée. Cela peut contenir plusieurs champs. champs de contact
Paramètre
Type
Description
name
str
Le nom identifiant de la personne/organisation de contact.
url
str
L’URL pointant vers les informations de contact. DOIT être au format d’une URL.
email
str
L’adresse e-mail de la personne/organisation de contact. DOIT être au format d’une adresse e-mail.
|
| `license_info` | `dict` | Les informations de licence pour l’API exposée. Cela peut contenir plusieurs champs. champs de license_info
Paramètre
Type
Description
name
str
OBLIGATOIRE (si un license_info est défini). Le nom de la licence utilisée pour l’API.
identifier
str
Une expression de licence [SPDX](https://spdx.org/licenses/) pour l’API. Le champ identifier est mutuellement exclusif du champ url. Disponible depuis OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
Une URL vers la licence utilisée pour l’API. DOIT être au format d’une URL.
|
Vous pouvez les définir comme suit :
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Astuce
Vous pouvez écrire du Markdown dans le champ `description` et il sera rendu dans la sortie.
///
Avec cette configuration, les documents API automatiques ressembleraient à :
## Identifiant de licence { #license-identifier }
Depuis OpenAPI 3.1.0 et FastAPI 0.99.0, vous pouvez également définir `license_info` avec un `identifier` au lieu d’une `url`.
Par exemple :
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Métadonnées pour les tags { #metadata-for-tags }
Vous pouvez également ajouter des métadonnées supplémentaires pour les différents tags utilisés pour regrouper vos chemins d'accès avec le paramètre `openapi_tags`.
Il prend une liste contenant un dictionnaire pour chaque tag.
Chaque dictionnaire peut contenir :
* `name` (**requis**) : un `str` avec le même nom de tag que vous utilisez dans le paramètre `tags` de vos *chemins d'accès* et `APIRouter`s.
* `description` : un `str` avec une courte description pour le tag. Il peut contenir du Markdown et sera affiché dans l’interface utilisateur de la documentation.
* `externalDocs` : un `dict` décrivant une documentation externe avec :
* `description` : un `str` avec une courte description pour la documentation externe.
* `url` (**requis**) : un `str` avec l’URL de la documentation externe.
### Créer des métadonnées pour les tags { #create-metadata-for-tags }
Essayons cela avec un exemple de tags pour `users` et `items`.
Créez des métadonnées pour vos tags et transmettez-les au paramètre `openapi_tags` :
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Notez que vous pouvez utiliser Markdown à l’intérieur des descriptions, par exemple « login » sera affiché en gras (**login**) et « fancy » sera affiché en italique (_fancy_).
/// tip | Astuce
Vous n’avez pas à ajouter des métadonnées pour tous les tags que vous utilisez.
///
### Utiliser vos tags { #use-your-tags }
Utilisez le paramètre `tags` avec vos *chemins d'accès* (et `APIRouter`s) pour les affecter à différents tags :
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info
En savoir plus sur les tags dans [Configuration de chemins d'accès](path-operation-configuration.md#tags).
///
### Consultez les documents { #check-the-docs }
Désormais, si vous consultez les documents, ils afficheront toutes les métadonnées supplémentaires :
### Définir l’ordre des tags { #order-of-tags }
L’ordre de chaque dictionnaire de métadonnées de tag définit également l’ordre affiché dans l’interface utilisateur de la documentation.
Par exemple, même si `users` viendrait après `items` par ordre alphabétique, il est affiché avant, car nous avons ajouté ses métadonnées comme premier dictionnaire de la liste.
## URL OpenAPI { #openapi-url }
Par défaut, le schéma OpenAPI est servi à `/openapi.json`.
Mais vous pouvez le configurer avec le paramètre `openapi_url`.
Par exemple, pour qu’il soit servi à `/api/v1/openapi.json` :
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Si vous souhaitez désactiver complètement le schéma OpenAPI, vous pouvez définir `openapi_url=None`, cela désactivera également les interfaces utilisateur de la documentation qui l’utilisent.
## URL des documents { #docs-urls }
Vous pouvez configurer les deux interfaces utilisateur de documentation incluses :
* **Swagger UI** : servie à `/docs`.
* Vous pouvez définir son URL avec le paramètre `docs_url`.
* Vous pouvez la désactiver en définissant `docs_url=None`.
* **ReDoc** : servie à `/redoc`.
* Vous pouvez définir son URL avec le paramètre `redoc_url`.
* Vous pouvez la désactiver en définissant `redoc_url=None`.
Par exemple, pour que Swagger UI soit servi à `/documentation` et désactiver ReDoc :
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/fr/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
Vous pouvez ajouter des middlewares aux applications **FastAPI**.
Un « middleware » est une fonction qui agit sur chaque **requête** avant qu’elle ne soit traitée par un *chemin d'accès* spécifique. Et aussi sur chaque **réponse** avant son renvoi.
* Il intercepte chaque **requête** qui parvient à votre application.
* Il peut alors faire quelque chose avec cette **requête** ou exécuter tout code nécessaire.
* Ensuite, il transmet la **requête** pour qu’elle soit traitée par le reste de l’application (par un *chemin d'accès*).
* Puis il récupère la **réponse** générée par l’application (par un *chemin d'accès*).
* Il peut faire quelque chose avec cette **réponse** ou exécuter tout code nécessaire.
* Enfin, il renvoie la **réponse**.
/// note | Détails techniques
Si vous avez des dépendances avec `yield`, le code de sortie s’exécutera après le middleware.
S’il y avait des tâches d’arrière-plan (présentées dans la section [Tâches d’arrière-plan](background-tasks.md), que vous verrez plus tard), elles s’exécuteront après tous les middlewares.
///
## Créer un middleware { #create-a-middleware }
Pour créer un middleware, utilisez le décorateur `@app.middleware("http")` au-dessus d’une fonction.
La fonction de middleware reçoit :
* La `request`.
* Une fonction `call_next` qui recevra la `request` en paramètre.
* Cette fonction transmettra la `request` au *chemin d'accès* correspondant.
* Puis elle renverra la `response` générée par le *chemin d'accès* correspondant.
* Vous pouvez ensuite modifier la `response` avant de la renvoyer.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Astuce
Gardez à l’esprit que des en-têtes propriétaires personnalisés peuvent être ajoutés [en utilisant le préfixe `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Mais si vous avez des en-têtes personnalisés que vous voulez rendre visibles pour un client dans un navigateur, vous devez les ajouter à votre configuration CORS ([CORS (Partage des ressources entre origines)](cors.md)) en utilisant le paramètre `expose_headers` documenté dans [la documentation CORS de Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.requests import Request`.
**FastAPI** le fournit pour votre confort de développeur. Mais cela provient directement de Starlette.
///
### Avant et après la `response` { #before-and-after-the-response }
Vous pouvez ajouter du code à exécuter avec la `request`, avant que tout *chemin d'accès* ne la reçoive.
Et aussi après que la `response` a été générée, avant de la renvoyer.
Par exemple, vous pourriez ajouter un en-tête personnalisé `X-Process-Time` contenant le temps en secondes nécessaire pour traiter la requête et générer une réponse :
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Astuce
Ici, nous utilisons [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) au lieu de `time.time()` car cela peut être plus précis pour ces cas d’usage. 🤓
///
## Ordre d’exécution de plusieurs middlewares { #multiple-middleware-execution-order }
Quand vous ajoutez plusieurs middlewares en utilisant soit le décorateur `@app.middleware()`, soit la méthode `app.add_middleware()`, chaque nouveau middleware enveloppe l’application, formant une pile. Le dernier middleware ajouté est le plus externe, et le premier est le plus interne.
Sur le chemin de la requête, le plus externe s’exécute en premier.
Sur le chemin de la réponse, il s’exécute en dernier.
Par exemple :
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Cela aboutit à l’ordre d’exécution suivant :
* **Requête** : MiddlewareB → MiddlewareA → route
* **Réponse** : route → MiddlewareA → MiddlewareB
Ce comportement d’empilement garantit que les middlewares s’exécutent dans un ordre prévisible et contrôlable.
## Autres middlewares { #other-middlewares }
Vous pouvez en lire davantage sur d’autres middlewares dans le [Guide de l’utilisateur avancé : Middleware avancé](../advanced/middleware.md).
Vous verrez comment gérer CORS avec un middleware dans la section suivante.
================================================
FILE: docs/fr/docs/tutorial/path-operation-configuration.md
================================================
# Configurer les chemins d'accès { #path-operation-configuration }
Vous pouvez passer plusieurs paramètres à votre *décorateur de chemin d'accès* pour le configurer.
/// warning | Alertes
Notez que ces paramètres sont passés directement au *décorateur de chemin d'accès*, et non à votre *fonction de chemin d'accès*.
///
## Définir le code d'état de la réponse { #response-status-code }
Vous pouvez définir le `status_code` (HTTP) à utiliser dans la réponse de votre *chemin d'accès*.
Vous pouvez passer directement le code `int`, comme `404`.
Mais si vous ne vous souvenez pas à quoi correspond chaque code numérique, vous pouvez utiliser les constantes abrégées dans `status` :
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Ce code d'état sera utilisé dans la réponse et ajouté au schéma OpenAPI.
/// note | Détails techniques
Vous pouvez également utiliser `from starlette import status`.
**FastAPI** fournit le même `starlette.status` sous le nom `fastapi.status` pour votre commodité, en tant que développeur. Mais cela provient directement de Starlette.
///
## Ajouter des tags { #tags }
Vous pouvez ajouter des tags à votre *chemin d'accès*, en passant le paramètre `tags` avec une `list` de `str` (généralement un seul `str`) :
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Ils seront ajoutés au schéma OpenAPI et utilisés par les interfaces de documentation automatiques :
### Utiliser des tags avec Enum { #tags-with-enums }
Si vous avez une grande application, vous pourriez finir par accumuler **plusieurs tags**, et vous voudrez vous assurer d'utiliser toujours le **même tag** pour les *chemins d'accès* associés.
Dans ces cas, il peut être judicieux de stocker les tags dans un `Enum`.
**FastAPI** le prend en charge de la même manière qu'avec des chaînes simples :
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Ajouter un résumé et une description { #summary-and-description }
Vous pouvez ajouter un `summary` et une `description` :
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Utiliser la description depuis la docstring { #description-from-docstring }
Comme les descriptions ont tendance à être longues et à couvrir plusieurs lignes, vous pouvez déclarer la description du *chemin d'accès* dans la docstring de la fonction et **FastAPI** la lira à partir de là.
Vous pouvez écrire [Markdown](https://en.wikipedia.org/wiki/Markdown) dans la docstring, il sera interprété et affiché correctement (en tenant compte de l'indentation de la docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Elle sera utilisée dans les documents interactifs :
## Définir la description de la réponse { #response-description }
Vous pouvez spécifier la description de la réponse avec le paramètre `response_description` :
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info
Notez que `response_description` se réfère spécifiquement à la réponse, tandis que `description` se réfère au *chemin d'accès* en général.
///
/// check | Vérifications
OpenAPI spécifie que chaque *chemin d'accès* requiert une description de réponse.
Donc, si vous n'en fournissez pas, **FastAPI** en générera automatiquement une « Réponse réussie ».
///
## Déprécier un *chemin d'accès* { #deprecate-a-path-operation }
Si vous devez marquer un *chemin d'accès* comme déprécié, sans pour autant le supprimer, passez le paramètre `deprecated` :
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Il sera clairement marqué comme déprécié dans les documents interactifs :
Voyez à quoi ressemblent les *chemins d'accès* dépréciés et non dépréciés :
## Récapitulatif { #recap }
Vous pouvez facilement configurer et ajouter des métadonnées à vos *chemins d'accès* en passant des paramètres aux *décorateurs de chemin d'accès*.
================================================
FILE: docs/fr/docs/tutorial/path-params-numeric-validations.md
================================================
# Paramètres de chemin et validations numériques { #path-parameters-and-numeric-validations }
De la même façon que vous pouvez déclarer plus de validations et de métadonnées pour les paramètres de requête avec `Query`, vous pouvez déclarer le même type de validations et de métadonnées pour les paramètres de chemin avec `Path`.
## Importer `Path` { #import-path }
Tout d'abord, importez `Path` de `fastapi`, et importez `Annotated` :
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info
FastAPI a ajouté le support pour `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d'utiliser `Annotated`.
Assurez-vous de [Mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) à la version 0.95.1 à minima avant d'utiliser `Annotated`.
///
## Déclarer des métadonnées { #declare-metadata }
Vous pouvez déclarer les mêmes paramètres que pour `Query`.
Par exemple, pour déclarer une valeur de métadonnée `title` pour le paramètre de chemin `item_id`, vous pouvez écrire :
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Remarque
Un paramètre de chemin est toujours requis car il doit faire partie du chemin. Même si vous l'avez déclaré avec `None` ou défini une valeur par défaut, cela ne changerait rien, il serait toujours requis.
///
## Ordonner les paramètres comme vous le souhaitez { #order-the-parameters-as-you-need }
/// tip | Astuce
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
///
Disons que vous voulez déclarer le paramètre de requête `q` comme un `str` requis.
Et vous n'avez pas besoin de déclarer autre chose pour ce paramètre, donc vous n'avez pas vraiment besoin d'utiliser `Query`.
Mais vous avez toujours besoin d'utiliser `Path` pour le paramètre de chemin `item_id`. Et vous ne voulez pas utiliser `Annotated` pour une raison quelconque.
Python se plaindra si vous mettez une valeur avec une « valeur par défaut » avant une valeur qui n'a pas de « valeur par défaut ».
Mais vous pouvez les réorganiser, et avoir la valeur sans défaut (le paramètre de requête `q`) en premier.
Cela n'a pas d'importance pour **FastAPI**. Il détectera les paramètres par leurs noms, types et déclarations par défaut (`Query`, `Path`, etc), il ne se soucie pas de l'ordre.
Ainsi, vous pouvez déclarer votre fonction comme suit :
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Mais gardez à l'esprit que si vous utilisez `Annotated`, vous n'aurez pas ce problème, cela n'aura pas d'importance car vous n'utilisez pas les valeurs par défaut des paramètres de fonction pour `Query()` ou `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Ordonner les paramètres comme vous le souhaitez, astuces { #order-the-parameters-as-you-need-tricks }
/// tip | Astuce
Ce n'est probablement pas aussi important ou nécessaire si vous utilisez `Annotated`.
///
Voici une **petite astuce** qui peut être pratique, mais vous n'en aurez pas souvent besoin.
Si vous voulez :
* déclarer le paramètre de requête `q` sans `Query` ni valeur par défaut
* déclarer le paramètre de chemin `item_id` en utilisant `Path`
* les avoir dans un ordre différent
* ne pas utiliser `Annotated`
... Python a une petite syntaxe spéciale pour cela.
Passez `*`, comme premier paramètre de la fonction.
Python ne fera rien avec ce `*`, mais il saura que tous les paramètres suivants doivent être appelés comme arguments « mots-clés » (paires clé-valeur), également connus sous le nom de kwargs. Même s'ils n'ont pas de valeur par défaut.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Mieux avec `Annotated` { #better-with-annotated }
Gardez à l'esprit que si vous utilisez `Annotated`, comme vous n'utilisez pas les valeurs par défaut des paramètres de fonction, vous n'aurez pas ce problème, et vous n'aurez probablement pas besoin d'utiliser `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Validations numériques : supérieur ou égal { #number-validations-greater-than-or-equal }
Avec `Query` et `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des contraintes numériques.
Ici, avec `ge=1`, `item_id` devra être un nombre entier « `g`reater than or `e`qual » à `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Validations numériques : supérieur et inférieur ou égal { #number-validations-greater-than-and-less-than-or-equal }
La même chose s'applique pour :
* `gt` : `g`reater `t`han
* `le` : `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Validations numériques : flottants, supérieur et inférieur { #number-validations-floats-greater-than-and-less-than }
Les validations numériques fonctionnent également pour les valeurs `float`.
C'est ici qu'il devient important de pouvoir déclarer gt et pas seulement ge. Avec cela, vous pouvez exiger, par exemple, qu'une valeur doit être supérieure à `0`, même si elle est inférieure à `1`.
Ainsi, `0.5` serait une valeur valide. Mais `0.0` ou `0` ne le serait pas.
Et la même chose pour lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Pour résumer { #recap }
Avec `Query`, `Path` (et d'autres que vous verrez plus tard) vous pouvez déclarer des métadonnées et des validations de chaînes de la même manière qu'avec les [Paramètres de requête et validations de chaînes](query-params-str-validations.md).
Et vous pouvez également déclarer des validations numériques :
* `gt` : `g`reater `t`han
* `ge` : `g`reater than or `e`qual
* `lt` : `l`ess `t`han
* `le` : `l`ess than or `e`qual
/// info
`Query`, `Path`, et d'autres classes que vous verrez plus tard sont des sous-classes d'une classe commune `Param`.
Tous partagent les mêmes paramètres pour des validations supplémentaires et des métadonnées que vous avez vu précédemment.
///
/// note | Détails techniques
Lorsque vous importez `Query`, `Path` et d'autres de `fastapi`, ce sont en fait des fonctions.
Ces dernières, lorsqu'elles sont appelées, renvoient des instances de classes du même nom.
Ainsi, vous importez `Query`, qui est une fonction. Et lorsque vous l'appelez, elle renvoie une instance d'une classe également nommée `Query`.
Ces fonctions sont là (au lieu d'utiliser simplement les classes directement) pour que votre éditeur ne marque pas d'erreurs sur leurs types.
De cette façon, vous pouvez utiliser votre éditeur et vos outils de codage habituels sans avoir à ajouter des configurations personnalisées pour ignorer ces erreurs.
///
================================================
FILE: docs/fr/docs/tutorial/path-params.md
================================================
# Paramètres de chemin { #path-parameters }
Vous pouvez déclarer des « paramètres » ou « variables » de chemin avec la même syntaxe utilisée par les chaînes de format Python :
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
La valeur du paramètre de chemin `item_id` sera transmise à votre fonction dans l'argument `item_id`.
Donc, si vous exécutez cet exemple et allez sur [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), vous verrez comme réponse :
```JSON
{"item_id":"foo"}
```
## Paramètres de chemin typés { #path-parameters-with-types }
Vous pouvez déclarer le type d'un paramètre de chemin dans la fonction, en utilisant les annotations de type Python standard :
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Ici, `item_id` est déclaré comme `int`.
/// check | Vérifications
Cela vous apporte la prise en charge par l'éditeur dans votre fonction, avec vérifications d'erreurs, autocomplétion, etc.
///
## Conversion de données { #data-conversion }
Si vous exécutez cet exemple et ouvrez votre navigateur sur [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), vous verrez comme réponse :
```JSON
{"item_id":3}
```
/// check | Vérifications
Remarquez que la valeur reçue par votre fonction (et renvoyée) est `3`, en tant qu'entier (`int`) Python, pas la chaîne de caractères « 3 ».
Ainsi, avec cette déclaration de type, **FastAPI** vous fournit automatiquement le « parsing » de la requête.
///
## Validation de données { #data-validation }
Mais si vous allez dans le navigateur sur [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), vous verrez une belle erreur HTTP :
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
car le paramètre de chemin `item_id` a pour valeur « foo », qui n'est pas un `int`.
La même erreur apparaîtrait si vous fournissiez un `float` au lieu d'un `int`, comme ici : [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Vérifications
Ainsi, avec la même déclaration de type Python, **FastAPI** vous fournit la validation de données.
Remarquez que l'erreur indique clairement l'endroit exact où la validation n'a pas réussi.
C'est incroyablement utile lors du développement et du débogage du code qui interagit avec votre API.
///
## Documentation { #documentation }
Et lorsque vous ouvrez votre navigateur sur [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), vous verrez une documentation d'API automatique et interactive comme :
/// check | Vérifications
À nouveau, simplement avec cette même déclaration de type Python, **FastAPI** vous fournit une documentation interactive automatique (intégrant Swagger UI).
Remarquez que le paramètre de chemin est déclaré comme entier.
///
## Les avantages d'une norme, documentation alternative { #standards-based-benefits-alternative-documentation }
Et comme le schéma généré suit la norme [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md), il existe de nombreux outils compatibles.
Grâce à cela, **FastAPI** fournit lui-même une documentation d'API alternative (utilisant ReDoc), accessible sur [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) :
De la même façon, il existe de nombreux outils compatibles, y compris des outils de génération de code pour de nombreux langages.
## Pydantic { #pydantic }
Toute la validation de données est effectuée sous le capot par [Pydantic](https://docs.pydantic.dev/), vous en bénéficiez donc pleinement. Vous savez ainsi que vous êtes entre de bonnes mains.
Vous pouvez utiliser les mêmes déclarations de type avec `str`, `float`, `bool` et de nombreux autres types de données complexes.
Plusieurs d'entre eux sont explorés dans les prochains chapitres du tutoriel.
## L'ordre importe { #order-matters }
Quand vous créez des *chemins d'accès*, vous pouvez vous retrouver dans une situation avec un chemin fixe.
Par exemple `/users/me`, disons pour récupérer les données de l'utilisateur actuel.
Et vous pouvez aussi avoir un chemin `/users/{user_id}` pour récupérer des données sur un utilisateur spécifique grâce à un identifiant d'utilisateur.
Comme les *chemins d'accès* sont évalués dans l'ordre, vous devez vous assurer que le chemin `/users/me` est déclaré avant celui de `/users/{user_id}` :
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Sinon, le chemin `/users/{user_id}` correspondrait aussi à `/users/me`, « pensant » qu'il reçoit un paramètre `user_id` avec la valeur « me ».
De même, vous ne pouvez pas redéfinir un chemin d'accès :
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Le premier sera toujours utilisé puisque le chemin correspond en premier.
## Valeurs prédéfinies { #predefined-values }
Si vous avez un *chemin d'accès* qui reçoit un *paramètre de chemin*, mais que vous voulez que les valeurs possibles de ce *paramètre de chemin* soient prédéfinies, vous pouvez utiliser une `Enum` Python standard.
### Créer une classe `Enum` { #create-an-enum-class }
Importez `Enum` et créez une sous-classe qui hérite de `str` et de `Enum`.
En héritant de `str`, la documentation de l'API saura que les valeurs doivent être de type `string` et pourra donc s'afficher correctement.
Créez ensuite des attributs de classe avec des valeurs fixes, qui seront les valeurs valides disponibles :
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Astuce
Si vous vous demandez, « AlexNet », « ResNet » et « LeNet » sont juste des noms de modèles de Machine Learning.
///
### Déclarer un paramètre de chemin { #declare-a-path-parameter }
Créez ensuite un *paramètre de chemin* avec une annotation de type utilisant la classe d'énumération que vous avez créée (`ModelName`) :
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Consulter la documentation { #check-the-docs }
Comme les valeurs disponibles pour le *paramètre de chemin* sont prédéfinies, la documentation interactive peut les afficher clairement :
### Travailler avec les *énumérations* Python { #working-with-python-enumerations }
La valeur du *paramètre de chemin* sera un *membre d'énumération*.
#### Comparer des *membres d'énumération* { #compare-enumeration-members }
Vous pouvez le comparer avec le *membre d'énumération* dans votre enum `ModelName` :
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Obtenir la *valeur de l'énumération* { #get-the-enumeration-value }
Vous pouvez obtenir la valeur réelle (une `str` dans ce cas) avec `model_name.value`, ou en général, `votre_membre_d_enum.value` :
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Astuce
Vous pouvez aussi accéder à la valeur « lenet » avec `ModelName.lenet.value`.
///
#### Retourner des *membres d'énumération* { #return-enumeration-members }
Vous pouvez retourner des *membres d'énumération* depuis votre *chemin d'accès*, même imbriqués dans un corps JSON (par ex. un `dict`).
Ils seront convertis vers leurs valeurs correspondantes (des chaînes de caractères ici) avant d'être renvoyés au client :
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
Dans votre client, vous recevrez une réponse JSON comme :
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Paramètres de chemin contenant des chemins { #path-parameters-containing-paths }
Disons que vous avez un *chemin d'accès* avec un chemin `/files/{file_path}`.
Mais vous avez besoin que `file_path` lui-même contienne un *chemin*, comme `home/johndoe/myfile.txt`.
Ainsi, l'URL pour ce fichier serait : `/files/home/johndoe/myfile.txt`.
### Support d'OpenAPI { #openapi-support }
OpenAPI ne prend pas en charge une manière de déclarer un *paramètre de chemin* contenant un *chemin* à l'intérieur, car cela peut conduire à des scénarios difficiles à tester et à définir.
Néanmoins, vous pouvez toujours le faire dans **FastAPI**, en utilisant l'un des outils internes de Starlette.
Et la documentation fonctionnera quand même, même si aucune indication supplémentaire ne sera ajoutée pour dire que le paramètre doit contenir un chemin.
### Convertisseur de chemin { #path-convertor }
En utilisant une option directement depuis Starlette, vous pouvez déclarer un *paramètre de chemin* contenant un *chemin* avec une URL comme :
```
/files/{file_path:path}
```
Dans ce cas, le nom du paramètre est `file_path`, et la dernière partie, `:path`, indique que le paramètre doit correspondre à n'importe quel *chemin*.
Vous pouvez donc l'utiliser ainsi :
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Astuce
Vous pourriez avoir besoin que le paramètre contienne `/home/johndoe/myfile.txt`, avec un slash initial (`/`).
Dans ce cas, l'URL serait : `/files//home/johndoe/myfile.txt`, avec un double slash (`//`) entre `files` et `home`.
///
## Récapitulatif { #recap }
Avec **FastAPI**, en utilisant des déclarations de type Python courtes, intuitives et standard, vous obtenez :
* Support de l'éditeur : vérifications d'erreurs, autocomplétion, etc.
* Données « parsing »
* Validation de données
* Annotations d'API et documentation automatique
Et vous n'avez besoin de les déclarer qu'une seule fois.
C'est probablement l'avantage visible principal de **FastAPI** comparé aux autres frameworks (outre les performances pures).
================================================
FILE: docs/fr/docs/tutorial/query-param-models.md
================================================
# Modèles de paramètres de requête { #query-parameter-models }
Si vous avez un groupe de paramètres de requête liés, vous pouvez créer un modèle Pydantic pour les déclarer.
Cela vous permet de réutiliser le modèle à plusieurs endroits et aussi de déclarer des validations et des métadonnées pour tous les paramètres en une seule fois. 😎
/// note | Remarque
Pris en charge depuis FastAPI version `0.115.0`. 🤓
///
## Déclarer des paramètres de requête avec un modèle Pydantic { #query-parameters-with-a-pydantic-model }
Déclarez les paramètres de requête dont vous avez besoin dans un modèle Pydantic, puis déclarez le paramètre en tant que `Query` :
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
FastAPI extrait les données pour chaque champ à partir des paramètres de requête de la requête et vous fournit le modèle Pydantic que vous avez défini.
## Consulter les documents { #check-the-docs }
Vous pouvez voir les paramètres de requête dans l'interface des documents à `/docs` :
## Interdire des paramètres de requête supplémentaires { #forbid-extra-query-parameters }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pouvez vouloir restreindre les paramètres de requête que vous souhaitez recevoir.
Vous pouvez utiliser la configuration du modèle Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Si un client tente d'envoyer des données supplémentaires dans les paramètres de requête, il recevra une réponse d'erreur.
Par exemple, si le client tente d'envoyer un paramètre de requête `tool` avec la valeur `plumbus`, comme :
```http
https://example.com/items/?limit=10&tool=plumbus
```
Il recevra une réponse d'erreur lui indiquant que le paramètre de requête `tool` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Résumé { #summary }
Vous pouvez utiliser des modèles Pydantic pour déclarer des paramètres de requête dans FastAPI. 😎
/// tip | Astuce
Alerte spoiler : vous pouvez aussi utiliser des modèles Pydantic pour déclarer des cookies et des en-têtes, mais vous lirez cela plus tard dans le tutoriel. 🤫
///
================================================
FILE: docs/fr/docs/tutorial/query-params-str-validations.md
================================================
# Paramètres de requête et validations de chaînes de caractères { #query-parameters-and-string-validations }
**FastAPI** vous permet de déclarer des informations et des validations supplémentaires pour vos paramètres.
Prenons cette application comme exemple :
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Le paramètre de requête `q` est de type `str | None`, cela signifie qu’il est de type `str` mais peut aussi être `None`, et en effet, la valeur par défaut est `None`, donc FastAPI saura qu’il n’est pas requis.
/// note | Remarque
FastAPI saura que la valeur de `q` n’est pas requise grâce à la valeur par défaut `= None`.
Avoir `str | None` permettra à votre éditeur de vous offrir un meilleur support et de détecter les erreurs.
///
## Validation additionnelle { #additional-validation }
Nous allons imposer que, même si `q` est optionnel, dès qu’il est fourni, **sa longueur n’excède pas 50 caractères**.
### Importer `Query` et `Annotated` { #import-query-and-annotated }
Pour ce faire, importez d’abord :
- `Query` depuis `fastapi`
- `Annotated` depuis `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info
FastAPI a ajouté la prise en charge de `Annotated` (et a commencé à le recommander) dans la version 0.95.0.
Si vous avez une version plus ancienne, vous obtiendrez des erreurs en essayant d’utiliser `Annotated`.
Assurez-vous de [mettre à niveau la version de FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) vers au moins 0.95.1 avant d’utiliser `Annotated`.
///
## Utiliser `Annotated` dans le type pour le paramètre `q` { #use-annotated-in-the-type-for-the-q-parameter }
Vous vous souvenez que je vous ai dit plus tôt que `Annotated` peut être utilisé pour ajouter des métadonnées à vos paramètres dans l’[Introduction aux types Python](../python-types.md#type-hints-with-metadata-annotations) ?
C’est le moment de l’utiliser avec FastAPI. 🚀
Nous avions cette annotation de type :
```Python
q: str | None = None
```
Ce que nous allons faire, c’est l’englober avec `Annotated`, de sorte que cela devienne :
```Python
q: Annotated[str | None] = None
```
Les deux versions signifient la même chose, `q` est un paramètre qui peut être une `str` ou `None`, et par défaut, c’est `None`.
Passons maintenant aux choses amusantes. 🎉
## Ajouter `Query` à `Annotated` dans le paramètre `q` { #add-query-to-annotated-in-the-q-parameter }
Maintenant que nous avons cet `Annotated` dans lequel nous pouvons mettre plus d’informations (dans ce cas une validation supplémentaire), ajoutez `Query` à l’intérieur de `Annotated`, et définissez le paramètre `max_length` à `50` :
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Remarquez que la valeur par défaut est toujours `None`, donc le paramètre est toujours optionnel.
Mais maintenant, avec `Query(max_length=50)` à l’intérieur de `Annotated`, nous indiquons à FastAPI que nous voulons **une validation supplémentaire** pour cette valeur, nous voulons qu’elle ait au maximum 50 caractères. 😎
/// tip | Astuce
Ici nous utilisons `Query()` parce qu’il s’agit d’un **paramètre de requête**. Plus tard nous verrons d’autres comme `Path()`, `Body()`, `Header()` et `Cookie()`, qui acceptent également les mêmes arguments que `Query()`.
///
FastAPI va maintenant :
- **Valider** les données en s’assurant que la longueur maximale est de 50 caractères
- Afficher une **erreur claire** au client quand les données ne sont pas valides
- **Documenter** le paramètre dans la *chemin d'accès* du schéma OpenAPI (il apparaîtra donc dans l’**interface de documentation automatique**)
## Alternative (ancienne) : `Query` comme valeur par défaut { #alternative-old-query-as-the-default-value }
Les versions précédentes de FastAPI (avant 0.95.0) exigeaient d’utiliser `Query` comme valeur par défaut de votre paramètre, au lieu de le mettre dans `Annotated`. Il y a de fortes chances que vous voyiez du code qui l’utilise encore, je vais donc vous l’expliquer.
/// tip | Astuce
Pour du nouveau code et dès que possible, utilisez `Annotated` comme expliqué ci-dessus. Il y a de multiples avantages (expliqués ci-dessous) et aucun inconvénient. 🍰
///
Voici comment vous utiliseriez `Query()` comme valeur par défaut du paramètre de votre fonction, en définissant le paramètre `max_length` à 50 :
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Comme, dans ce cas (sans utiliser `Annotated`), nous devons remplacer la valeur par défaut `None` dans la fonction par `Query()`, nous devons maintenant définir la valeur par défaut avec le paramètre `Query(default=None)`, cela sert le même objectif de définir cette valeur par défaut (au moins pour FastAPI).
Donc :
```Python
q: str | None = Query(default=None)
```
... rend le paramètre optionnel, avec une valeur par défaut de `None`, comme :
```Python
q: str | None = None
```
Mais la version avec `Query` le déclare explicitement comme étant un paramètre de requête.
Ensuite, nous pouvons passer plus de paramètres à `Query`. Dans ce cas, le paramètre `max_length` qui s’applique aux chaînes de caractères :
```Python
q: str | None = Query(default=None, max_length=50)
```
Cela validera les données, affichera une erreur claire lorsque les données ne sont pas valides et documentera le paramètre dans la *chemin d'accès* du schéma OpenAPI.
### `Query` comme valeur par défaut ou dans `Annotated` { #query-as-the-default-value-or-in-annotated }
Gardez à l’esprit qu’en utilisant `Query` à l’intérieur de `Annotated`, vous ne pouvez pas utiliser le paramètre `default` de `Query`.
Utilisez à la place la valeur par défaut réelle du paramètre de fonction. Sinon, ce serait incohérent.
Par exemple, ceci n’est pas autorisé :
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
... parce qu’il n’est pas clair si la valeur par défaut doit être « rick » ou « morty ».
Donc, vous utiliseriez (de préférence) :
```Python
q: Annotated[str, Query()] = "rick"
```
... ou dans des bases de code plus anciennes, vous trouverez :
```Python
q: str = Query(default="rick")
```
### Avantages de `Annotated` { #advantages-of-annotated }
**L’utilisation de `Annotated` est recommandée** plutôt que la valeur par défaut dans les paramètres de fonction, c’est **mieux** pour plusieurs raisons. 🤓
La valeur **par défaut** du **paramètre de fonction** est la **vraie valeur par défaut**, c’est plus intuitif en Python en général. 😌
Vous pouvez **appeler** cette même fonction dans **d’autres endroits** sans FastAPI, et elle **fonctionnera comme prévu**. S’il y a un paramètre **requis** (sans valeur par défaut), votre **éditeur** vous le signalera avec une erreur, **Python** se plaindra aussi si vous l’exécutez sans passer le paramètre requis.
Quand vous n’utilisez pas `Annotated` et utilisez à la place l’**ancienne** méthode avec la **valeur par défaut**, si vous appelez cette fonction sans FastAPI dans **d’autres endroits**, vous devez **penser** à passer les arguments à la fonction pour qu’elle fonctionne correctement, sinon les valeurs seront différentes de ce que vous attendez (par ex. `QueryInfo` ou quelque chose de similaire au lieu d’une `str`). Et votre éditeur ne se plaindra pas, et Python ne se plaindra pas en exécutant cette fonction, seulement quand les opérations internes échoueront.
Comme `Annotated` peut avoir plus d’une annotation de métadonnées, vous pouvez maintenant même utiliser la même fonction avec d’autres outils, comme [Typer](https://typer.tiangolo.com/). 🚀
## Ajouter plus de validations { #add-more-validations }
Vous pouvez également ajouter un paramètre `min_length` :
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Ajouter des expressions régulières { #add-regular-expressions }
Vous pouvez définir un `pattern` d’expression régulière auquel le paramètre doit correspondre :
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Ce pattern d’expression régulière spécifique vérifie que la valeur reçue pour le paramètre :
- `^` : commence avec les caractères qui suivent, n’a pas de caractères avant.
- `fixedquery` : a exactement la valeur `fixedquery`.
- `$` : se termine là, n’a pas d’autres caractères après `fixedquery`.
Si vous vous sentez perdu avec toutes ces idées d’**« expression régulière »**, pas d’inquiétude. C’est un sujet difficile pour beaucoup. Vous pouvez déjà faire beaucoup de choses sans avoir besoin d’expressions régulières.
Désormais, vous savez que, lorsque vous en aurez besoin, vous pourrez les utiliser dans **FastAPI**.
## Valeurs par défaut { #default-values }
Vous pouvez, bien sûr, utiliser des valeurs par défaut autres que `None`.
Disons que vous voulez déclarer le paramètre de requête `q` avec un `min_length` de `3`, et avec une valeur par défaut de « fixedquery » :
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Remarque
Avoir une valeur par défaut de n’importe quel type, y compris `None`, rend le paramètre optionnel (non requis).
///
## Paramètres requis { #required-parameters }
Quand nous n’avons pas besoin de déclarer plus de validations ou de métadonnées, nous pouvons rendre le paramètre de requête `q` requis en n’indiquant simplement pas de valeur par défaut, comme :
```Python
q: str
```
au lieu de :
```Python
q: str | None = None
```
Mais maintenant nous le déclarons avec `Query`, par exemple ainsi :
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Donc, lorsque vous avez besoin de déclarer une valeur comme requise tout en utilisant `Query`, vous pouvez simplement ne pas déclarer de valeur par défaut :
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Requis, peut valoir `None` { #required-can-be-none }
Vous pouvez déclarer qu’un paramètre accepte `None`, mais qu’il est tout de même requis. Cela obligerait les clients à envoyer une valeur, même si la valeur est `None`.
Pour ce faire, vous pouvez déclarer que `None` est un type valide tout en ne déclarant pas de valeur par défaut :
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Liste de paramètres de requête / valeurs multiples { #query-parameter-list-multiple-values }
Quand vous définissez un paramètre de requête explicitement avec `Query`, vous pouvez aussi déclarer qu’il reçoit une liste de valeurs, autrement dit, qu’il reçoit des valeurs multiples.
Par exemple, pour déclarer un paramètre de requête `q` qui peut apparaître plusieurs fois dans l’URL, vous pouvez écrire :
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Ensuite, avec une URL comme :
```
http://localhost:8000/items/?q=foo&q=bar
```
vous recevriez les valeurs des multiples paramètres de requête `q` (`foo` et `bar`) dans une `list` Python à l’intérieur de votre fonction de *chemin d'accès*, dans le *paramètre de fonction* `q`.
Donc, la réponse pour cette URL serait :
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Astuce
Pour déclarer un paramètre de requête avec un type `list`, comme dans l’exemple ci-dessus, vous devez explicitement utiliser `Query`, sinon il serait interprété comme faisant partie du corps de la requête.
///
L’interface de documentation interactive de l’API sera mise à jour en conséquence, pour autoriser plusieurs valeurs :
### Liste de paramètres de requête / valeurs multiples avec valeurs par défaut { #query-parameter-list-multiple-values-with-defaults }
Vous pouvez également définir une `list` de valeurs par défaut si aucune n’est fournie :
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Si vous allez à :
```
http://localhost:8000/items/
```
la valeur par défaut de `q` sera : `["foo", "bar"]` et votre réponse sera :
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Utiliser simplement `list` { #using-just-list }
Vous pouvez aussi utiliser `list` directement au lieu de `list[str]` :
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Remarque
Gardez à l’esprit que dans ce cas, FastAPI ne vérifiera pas le contenu de la liste.
Par exemple, `list[int]` vérifierait (et documenterait) que le contenu de la liste est composé d’entiers. Mais un simple `list` ne le ferait pas.
///
## Déclarer plus de métadonnées { #declare-more-metadata }
Vous pouvez ajouter plus d’informations à propos du paramètre.
Ces informations seront incluses dans l’OpenAPI généré et utilisées par les interfaces de documentation et les outils externes.
/// note | Remarque
Gardez à l’esprit que différents outils peuvent avoir des niveaux de prise en charge d’OpenAPI différents.
Certains d’entre eux pourraient ne pas encore afficher toutes les informations supplémentaires déclarées, bien que, dans la plupart des cas, la fonctionnalité manquante soit déjà prévue au développement.
///
Vous pouvez ajouter un `title` :
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
Et une `description` :
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Paramètres avec alias { #alias-parameters }
Imaginez que vous vouliez que le paramètre soit `item-query`.
Comme dans :
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Mais `item-query` n’est pas un nom de variable Python valide.
Le plus proche serait `item_query`.
Mais vous avez quand même besoin que ce soit exactement `item-query` ...
Vous pouvez alors déclarer un `alias`, et cet alias sera utilisé pour trouver la valeur du paramètre :
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Déprécier des paramètres { #deprecating-parameters }
Disons que vous n’aimez plus ce paramètre.
Vous devez le laisser là quelque temps car des clients l’utilisent, mais vous voulez que les documents l’affichent clairement comme déprécié.
Passez alors le paramètre `deprecated=True` à `Query` :
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
Les documents l’afficheront ainsi :
## Exclure des paramètres d’OpenAPI { #exclude-parameters-from-openapi }
Pour exclure un paramètre de requête du schéma OpenAPI généré (et donc, des systèmes de documentation automatiques), définissez le paramètre `include_in_schema` de `Query` à `False` :
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Validation personnalisée { #custom-validation }
Il peut y avoir des cas où vous devez faire une **validation personnalisée** qui ne peut pas être réalisée avec les paramètres montrés ci-dessus.
Dans ces cas, vous pouvez utiliser une **fonction de validation personnalisée** qui est appliquée après la validation normale (par ex. après avoir validé que la valeur est une `str`).
Vous pouvez y parvenir en utilisant [`AfterValidator` de Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) à l’intérieur de `Annotated`.
/// tip | Astuce
Pydantic a aussi [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) et d’autres. 🤓
///
Par exemple, ce validateur personnalisé vérifie que l’ID d’item commence par `isbn-` pour un numéro de livre ISBN ou par `imdb-` pour un ID d’URL de film IMDB :
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info
C’est disponible avec Pydantic version 2 ou supérieure. 😎
///
/// tip | Astuce
Si vous devez faire un type de validation qui nécessite de communiquer avec un **composant externe**, comme une base de données ou une autre API, vous devez plutôt utiliser les **Dépendances de FastAPI**, vous en apprendrez davantage plus tard.
Ces validateurs personnalisés sont destinés aux éléments qui peuvent être vérifiés **uniquement** avec les **mêmes données** fournies dans la requête.
///
### Comprendre ce code { #understand-that-code }
Le point important est simplement d’utiliser **`AfterValidator` avec une fonction à l’intérieur de `Annotated`**. N’hésitez pas à passer cette partie. 🤸
---
Mais si vous êtes curieux de cet exemple de code spécifique et que vous êtes toujours partant, voici quelques détails supplémentaires.
#### Chaîne avec `value.startswith()` { #string-with-value-startswith }
Avez-vous remarqué ? Une chaîne utilisant `value.startswith()` peut prendre un tuple, et elle vérifiera chaque valeur du tuple :
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Un élément aléatoire { #a-random-item }
Avec `data.items()` nous obtenons un objet itérable avec des tuples contenant la clé et la valeur pour chaque élément du dictionnaire.
Nous convertissons cet objet itérable en une `list` propre avec `list(data.items())`.
Ensuite, avec `random.choice()` nous pouvons obtenir une **valeur aléatoire** depuis la liste, nous obtenons donc un tuple `(id, name)`. Ce sera quelque chose comme `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Puis nous **affectons ces deux valeurs** du tuple aux variables `id` et `name`.
Ainsi, si l’utilisateur n’a pas fourni d’ID d’item, il recevra quand même une suggestion aléatoire.
... nous faisons tout cela en **une seule ligne simple**. 🤯 Vous n’adorez pas Python ? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Récapitulatif { #recap }
Vous pouvez déclarer des validations et des métadonnées supplémentaires pour vos paramètres.
Validations et métadonnées génériques :
- `alias`
- `title`
- `description`
- `deprecated`
Validations spécifiques aux chaînes :
- `min_length`
- `max_length`
- `pattern`
Validations personnalisées avec `AfterValidator`.
Dans ces exemples, vous avez vu comment déclarer des validations pour des valeurs `str`.
Voyez les prochains chapitres pour apprendre à déclarer des validations pour d’autres types, comme les nombres.
================================================
FILE: docs/fr/docs/tutorial/query-params.md
================================================
# Paramètres de requête { #query-parameters }
Quand vous déclarez d'autres paramètres de fonction qui ne font pas partie des paramètres de chemin, ils sont automatiquement interprétés comme des paramètres de « query ».
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
La query est l'ensemble des paires clé-valeur placées après le `?` dans une URL, séparées par des caractères `&`.
Par exemple, dans l'URL :
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
... les paramètres de requête sont :
* `skip` : avec une valeur de `0`
* `limit` : avec une valeur de `10`
Comme ils font partie de l'URL, ce sont « naturellement » des chaînes de caractères.
Mais lorsque vous les déclarez avec des types Python (dans l'exemple ci-dessus, en tant que `int`), ils sont convertis vers ce type et validés par rapport à celui-ci.
Tous les mêmes processus qui s'appliquaient aux paramètres de chemin s'appliquent aussi aux paramètres de requête :
* Prise en charge de l'éditeur (évidemment)
* « parsing » des données
* Validation des données
* Documentation automatique
## Valeurs par défaut { #defaults }
Comme les paramètres de requête ne sont pas une partie fixe d'un chemin, ils peuvent être optionnels et avoir des valeurs par défaut.
Dans l'exemple ci-dessus, ils ont des valeurs par défaut `skip=0` et `limit=10`.
Donc, accéder à l'URL :
```
http://127.0.0.1:8000/items/
```
serait équivalent à accéder à :
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Mais si vous accédez, par exemple, à :
```
http://127.0.0.1:8000/items/?skip=20
```
Les valeurs des paramètres dans votre fonction seront :
* `skip=20` : car vous l'avez défini dans l'URL
* `limit=10` : car c'était la valeur par défaut
## Paramètres optionnels { #optional-parameters }
De la même façon, vous pouvez déclarer des paramètres de requête optionnels, en définissant leur valeur par défaut à `None` :
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
Dans ce cas, le paramètre de fonction `q` sera optionnel et vaudra `None` par défaut.
/// check | Vérifications
Notez également que **FastAPI** est suffisamment intelligent pour remarquer que le paramètre de chemin `item_id` est un paramètre de chemin et que `q` ne l'est pas, c'est donc un paramètre de requête.
///
## Conversion des types des paramètres de requête { #query-parameter-type-conversion }
Vous pouvez aussi déclarer des types `bool`, ils seront convertis :
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Dans ce cas, si vous allez sur :
```
http://127.0.0.1:8000/items/foo?short=1
```
ou
```
http://127.0.0.1:8000/items/foo?short=True
```
ou
```
http://127.0.0.1:8000/items/foo?short=true
```
ou
```
http://127.0.0.1:8000/items/foo?short=on
```
ou
```
http://127.0.0.1:8000/items/foo?short=yes
```
ou n'importe quelle autre variation de casse (tout en majuscules, uniquement la première lettre en majuscule, etc.), votre fonction verra le paramètre `short` avec une valeur `bool` à `True`. Sinon la valeur sera à `False`.
## Multiples paramètres de chemin et de requête { #multiple-path-and-query-parameters }
Vous pouvez déclarer plusieurs paramètres de chemin et paramètres de requête en même temps, **FastAPI** sait lequel est lequel.
Et vous n'avez pas besoin de les déclarer dans un ordre spécifique.
Ils seront détectés par leur nom :
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Paramètres de requête requis { #required-query-parameters }
Quand vous déclarez une valeur par défaut pour des paramètres qui ne sont pas des paramètres de chemin (pour l'instant, nous n'avons vu que les paramètres de requête), alors ils ne sont pas requis.
Si vous ne voulez pas leur donner de valeur spécifique mais simplement les rendre optionnels, définissez la valeur par défaut à `None`.
Mais si vous voulez rendre un paramètre de requête obligatoire, vous pouvez simplement ne déclarer aucune valeur par défaut :
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Ici, le paramètre de requête `needy` est un paramètre de requête requis de type `str`.
Si vous ouvrez dans votre navigateur une URL comme :
```
http://127.0.0.1:8000/items/foo-item
```
... sans ajouter le paramètre requis `needy`, vous verrez une erreur comme :
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Comme `needy` est un paramètre requis, vous devez le définir dans l'URL :
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
... cela fonctionnerait :
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
Et bien sûr, vous pouvez définir certains paramètres comme requis, certains avec une valeur par défaut et certains entièrement optionnels :
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
Dans ce cas, il y a 3 paramètres de requête :
* `needy`, un `str` requis.
* `skip`, un `int` avec une valeur par défaut de `0`.
* `limit`, un `int` optionnel.
/// tip | Astuce
Vous pourriez aussi utiliser des `Enum`s de la même façon qu'avec les [Paramètres de chemin](path-params.md#predefined-values).
///
================================================
FILE: docs/fr/docs/tutorial/request-files.md
================================================
# Envoyer des fichiers { #request-files }
Vous pouvez définir des fichiers à téléverser par le client en utilisant `File`.
/// info
Pour recevoir des fichiers téléversés, installez d'abord [`python-multipart`](https://github.com/Kludex/python-multipart).
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md), de l'activer, puis d'installer le paquet, par exemple :
```console
$ pip install python-multipart
```
C'est parce que les fichiers téléversés sont envoyés en « données de formulaire ».
///
## Importer `File` { #import-file }
Importez `File` et `UploadFile` depuis `fastapi` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Définir des paramètres `File` { #define-file-parameters }
Créez des paramètres de fichier de la même manière que pour `Body` ou `Form` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info
`File` est une classe qui hérite directement de `Form`.
Mais souvenez-vous que lorsque vous importez `Query`, `Path`, `File` et d'autres depuis `fastapi`, ce sont en réalité des fonctions qui renvoient des classes spéciales.
///
/// tip | Astuce
Pour déclarer des fichiers dans le corps de la requête, vous devez utiliser `File`, sinon les paramètres seraient interprétés comme des paramètres de requête ou des paramètres de corps (JSON).
///
Les fichiers seront téléversés en « données de formulaire ».
Si vous déclarez le type de votre paramètre de *fonction de chemin d'accès* comme `bytes`, **FastAPI** lira le fichier pour vous et vous recevrez le contenu sous forme de `bytes`.
Gardez à l'esprit que cela signifie que tout le contenu sera stocké en mémoire. Cela fonctionnera bien pour de petits fichiers.
Mais dans plusieurs cas, vous pourriez bénéficier de l'utilisation d'`UploadFile`.
## Paramètres de fichier avec `UploadFile` { #file-parameters-with-uploadfile }
Définissez un paramètre de fichier de type `UploadFile` :
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Utiliser `UploadFile` présente plusieurs avantages par rapport à `bytes` :
- Vous n'avez pas besoin d'utiliser `File()` comme valeur par défaut du paramètre.
- Il utilise un fichier « spooled » :
- Un fichier stocké en mémoire jusqu'à une taille maximale, puis, au-delà de cette limite, stocké sur le disque.
- Cela fonctionne donc bien pour des fichiers volumineux comme des images, des vidéos, de gros binaires, etc., sans consommer toute la mémoire.
- Vous pouvez obtenir des métadonnées à partir du fichier téléversé.
- Il offre une interface `async` de type [file-like](https://docs.python.org/3/glossary.html#term-file-like-object).
- Il expose un véritable objet Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) que vous pouvez passer directement à d'autres bibliothèques qui attendent un objet « file-like ».
### `UploadFile` { #uploadfile }
`UploadFile` a les attributs suivants :
- `filename` : une `str` contenant le nom de fichier original téléversé (par ex. `myimage.jpg`).
- `content_type` : une `str` avec le type de contenu (type MIME / type média) (par ex. `image/jpeg`).
- `file` : un [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (un objet [file-like](https://docs.python.org/3/glossary.html#term-file-like-object)). C'est l'objet fichier Python réel que vous pouvez passer directement à d'autres fonctions ou bibliothèques qui attendent un objet « file-like ».
`UploadFile` a les méthodes `async` suivantes. Elles appellent toutes les méthodes correspondantes du fichier sous-jacent (en utilisant le `SpooledTemporaryFile` interne).
- `write(data)` : écrit `data` (`str` ou `bytes`) dans le fichier.
- `read(size)` : lit `size` (`int`) octets/caractères du fichier.
- `seek(offset)` : se déplace à la position d'octet `offset` (`int`) dans le fichier.
- Par ex., `await myfile.seek(0)` irait au début du fichier.
- C'est particulièrement utile si vous exécutez `await myfile.read()` une fois puis devez relire le contenu.
- `close()` : ferme le fichier.
Comme toutes ces méthodes sont `async`, vous devez les « await ».
Par exemple, à l'intérieur d'une *fonction de chemin d'accès* `async`, vous pouvez obtenir le contenu avec :
```Python
contents = await myfile.read()
```
Si vous êtes dans une *fonction de chemin d'accès* `def` normale, vous pouvez accéder directement à `UploadFile.file`, par exemple :
```Python
contents = myfile.file.read()
```
/// note | Détails techniques `async`
Lorsque vous utilisez les méthodes `async`, **FastAPI** exécute les méthodes de fichier dans un pool de threads et les attend.
///
/// note | Détails techniques Starlette
L'`UploadFile` de **FastAPI** hérite directement de l'`UploadFile` de **Starlette**, mais ajoute certaines parties nécessaires pour le rendre compatible avec **Pydantic** et les autres parties de FastAPI.
///
## Qu'est-ce que les « données de formulaire » { #what-is-form-data }
La façon dont les formulaires HTML (``) envoient les données au serveur utilise normalement un encodage « spécial » pour ces données, différent de JSON.
**FastAPI** s'assure de lire ces données au bon endroit plutôt que depuis JSON.
/// note | Détails techniques
Les données des formulaires sont normalement encodées avec le « type de média » `application/x-www-form-urlencoded` lorsqu'elles n'incluent pas de fichiers.
Mais lorsque le formulaire inclut des fichiers, il est encodé en `multipart/form-data`. Si vous utilisez `File`, **FastAPI** saura qu'il doit récupérer les fichiers depuis la partie appropriée du corps.
Si vous souhaitez en savoir plus sur ces encodages et les champs de formulaire, consultez la [MDN Web Docs pour `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `File` et `Form` dans un *chemin d'accès*, mais vous ne pouvez pas également déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `multipart/form-data` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Téléversement de fichier facultatif { #optional-file-upload }
Vous pouvez rendre un fichier facultatif en utilisant des annotations de type standard et en définissant une valeur par défaut à `None` :
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` avec des métadonnées supplémentaires { #uploadfile-with-additional-metadata }
Vous pouvez aussi utiliser `File()` avec `UploadFile`, par exemple pour définir des métadonnées supplémentaires :
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Téléverser plusieurs fichiers { #multiple-file-uploads }
Il est possible de téléverser plusieurs fichiers en même temps.
Ils seraient associés au même « champ de formulaire » envoyé en « données de formulaire ».
Pour cela, déclarez une `list` de `bytes` ou d'`UploadFile` :
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Vous recevrez, comme déclaré, une `list` de `bytes` ou d'`UploadFile`.
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette.responses import HTMLResponse`.
**FastAPI** fournit les mêmes `starlette.responses` sous `fastapi.responses` simplement pour votre convenance en tant que développeur. Mais la plupart des réponses disponibles proviennent directement de Starlette.
///
### Téléversements multiples avec métadonnées supplémentaires { #multiple-file-uploads-with-additional-metadata }
Et de la même manière que précédemment, vous pouvez utiliser `File()` pour définir des paramètres supplémentaires, même pour `UploadFile` :
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Récapitulatif { #recap }
Utilisez `File`, `bytes` et `UploadFile` pour déclarer des fichiers à téléverser dans la requête, envoyés en « données de formulaire ».
================================================
FILE: docs/fr/docs/tutorial/request-form-models.md
================================================
# Modèles de formulaire { #form-models }
Vous pouvez utiliser des **modèles Pydantic** pour déclarer des **champs de formulaire** dans FastAPI.
/// info
Pour utiliser les formulaires, installez d'abord [`python-multipart`](https://github.com/Kludex/python-multipart).
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md), de l'activer, puis d'installer le paquet, par exemple :
```console
$ pip install python-multipart
```
///
/// note | Remarque
Ceci est pris en charge depuis la version `0.113.0` de FastAPI. 🤓
///
## Modèles Pydantic pour les formulaires { #pydantic-models-for-forms }
Vous avez simplement besoin de déclarer un **modèle Pydantic** avec les champs que vous souhaitez recevoir comme **champs de formulaire**, puis de déclarer le paramètre comme `Form` :
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** va **extraire** les données pour **chaque champ** à partir des **données de formulaire** de la requête et vous fournir le modèle Pydantic que vous avez défini.
## Consulter les documents { #check-the-docs }
Vous pouvez le vérifier dans l'interface des documents à `/docs` :
## Interdire les champs de formulaire supplémentaires { #forbid-extra-form-fields }
Dans certains cas d'utilisation particuliers (probablement peu courants), vous pourriez vouloir **restreindre** les champs de formulaire à ceux déclarés dans le modèle Pydantic, et **interdire** tout champ **supplémentaire**.
/// note | Remarque
Ceci est pris en charge depuis la version `0.114.0` de FastAPI. 🤓
///
Vous pouvez utiliser la configuration du modèle Pydantic pour `forbid` tout champ `extra` :
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Si un client tente d'envoyer des données supplémentaires, il recevra une **réponse d'erreur**.
Par exemple, si le client essaie d'envoyer les champs de formulaire :
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Il recevra une réponse d'erreur lui indiquant que le champ `extra` n'est pas autorisé :
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Résumer { #summary }
Vous pouvez utiliser des modèles Pydantic pour déclarer des champs de formulaire dans FastAPI. 😎
================================================
FILE: docs/fr/docs/tutorial/request-forms-and-files.md
================================================
# Utiliser des formulaires et des fichiers de requête { #request-forms-and-files }
Vous pouvez définir des fichiers et des champs de formulaire en même temps à l'aide de `File` et `Form`.
/// info
Pour recevoir des fichiers téléversés et/ou des données de formulaire, installez d'abord [`python-multipart`](https://github.com/Kludex/python-multipart).
Vous devez créer un [environnement virtuel](../virtual-environments.md), l'activer, puis installer ce paquet, par exemple :
```console
$ pip install python-multipart
```
///
## Importer `File` et `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Définir des paramètres `File` et `Form` { #define-file-and-form-parameters }
Créez des paramètres de fichier et de formulaire de la même manière que pour `Body` ou `Query` :
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Les fichiers et les champs de formulaire seront téléversés en tant que données de formulaire et vous les recevrez.
Et vous pouvez déclarer certains fichiers comme `bytes` et d'autres comme `UploadFile`.
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `File` et `Form` dans un *chemin d'accès*, mais vous ne pouvez pas aussi déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `multipart/form-data` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Récapitulatif { #recap }
Utilisez `File` et `Form` ensemble lorsque vous devez recevoir des données et des fichiers dans la même requête.
================================================
FILE: docs/fr/docs/tutorial/request-forms.md
================================================
# Données de formulaire { #form-data }
Lorsque vous devez recevoir des champs de formulaire au lieu de JSON, vous pouvez utiliser `Form`.
/// info
Pour utiliser les formulaires, installez d'abord [`python-multipart`](https://github.com/Kludex/python-multipart).
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md), de l'activer, puis installez-le, par exemple :
```console
$ pip install python-multipart
```
///
## Importer `Form` { #import-form }
Importez `Form` depuis `fastapi` :
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Définir les paramètres `Form` { #define-form-parameters }
Créez des paramètres de formulaire comme vous le feriez pour `Body` ou `Query` :
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Par exemple, dans l'une des manières dont la spécification OAuth2 peut être utilisée (appelée « password flow »), il est requis d'envoyer un `username` et un `password` comme champs de formulaire.
La spécification exige que les champs soient exactement nommés `username` et `password`, et qu'ils soient envoyés en tant que champs de formulaire, pas en JSON.
Avec `Form`, vous pouvez déclarer les mêmes configurations que pour `Body` (ainsi que `Query`, `Path`, `Cookie`), y compris la validation, des exemples, un alias (p. ex. `user-name` au lieu de `username`), etc.
/// info
`Form` est une classe qui hérite directement de `Body`.
///
/// tip | Astuce
Pour déclarer des corps de formulaire, vous devez utiliser `Form` explicitement, car sinon les paramètres seraient interprétés comme des paramètres de requête ou des paramètres de corps (JSON).
///
## À propos des « champs de formulaire » { #about-form-fields }
La manière dont les formulaires HTML (``) envoient les données au serveur utilise normalement un encodage « spécial » pour ces données, différent de JSON.
**FastAPI** s'assure de lire ces données au bon endroit au lieu de JSON.
/// note | Détails techniques
Les données issues des formulaires sont normalement encodées avec le « type de média » `application/x-www-form-urlencoded`.
Mais lorsque le formulaire inclut des fichiers, il est encodé en `multipart/form-data`. Vous lirez la gestion des fichiers dans le chapitre suivant.
Si vous voulez en savoir plus sur ces encodages et les champs de formulaire, consultez la [MDN web docs pour `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Alertes
Vous pouvez déclarer plusieurs paramètres `Form` dans un chemin d'accès, mais vous ne pouvez pas aussi déclarer des champs `Body` que vous vous attendez à recevoir en JSON, car la requête aura le corps encodé en `application/x-www-form-urlencoded` au lieu de `application/json`.
Ce n'est pas une limitation de **FastAPI**, cela fait partie du protocole HTTP.
///
## Récapitulatif { #recap }
Utilisez `Form` pour déclarer les paramètres d'entrée des données de formulaire.
================================================
FILE: docs/fr/docs/tutorial/response-model.md
================================================
# Modèle de réponse - Type de retour { #response-model-return-type }
Vous pouvez déclarer le type utilisé pour la réponse en annotant le **type de retour** de la *fonction de chemin d'accès*.
Vous pouvez utiliser des **annotations de type** de la même manière que pour les données d'entrée dans les **paramètres** de fonction. Vous pouvez utiliser des modèles Pydantic, des listes, des dictionnaires, des valeurs scalaires comme des entiers, des booléens, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI utilisera ce type de retour pour :
* **Valider** les données renvoyées.
* Si les données sont invalides (par exemple, il manque un champ), cela signifie que le code de *votre* application est défectueux, qu'il ne renvoie pas ce qu'il devrait, et un erreur serveur sera renvoyée au lieu de renvoyer des données incorrectes. De cette façon, vous et vos clients pouvez être certains de recevoir les données attendues et avec la structure attendue.
* Ajouter un **JSON Schema** pour la réponse, dans l’OpenAPI du *chemin d'accès*.
* Ceci sera utilisé par la **documentation automatique**.
* Ceci sera également utilisé par les outils de génération automatique de code client.
* **Sérialiser** les données renvoyées en JSON en utilisant Pydantic, qui est écrit en **Rust**, ce qui sera **beaucoup plus rapide**.
Mais surtout :
* Il **limitera et filtrera** les données de sortie à ce qui est défini dans le type de retour.
* C'est particulièrement important pour la **sécurité**, nous verrons cela plus bas.
## Paramètre `response_model` { #response-model-parameter }
Il existe des cas où vous devez ou souhaitez renvoyer des données qui ne correspondent pas exactement à ce que déclare le type.
Par exemple, vous pourriez vouloir **renvoyer un dictionnaire** ou un objet de base de données, mais **le déclarer comme un modèle Pydantic**. Ainsi, le modèle Pydantic ferait toute la documentation des données, la validation, etc. pour l'objet que vous avez renvoyé (par exemple un dictionnaire ou un objet de base de données).
Si vous ajoutez l'annotation du type de retour, les outils et éditeurs se plaindront avec une (juste) erreur vous indiquant que votre fonction renvoie un type (par exemple un dict) différent de ce que vous avez déclaré (par exemple un modèle Pydantic).
Dans ces cas, vous pouvez utiliser le paramètre `response_model` du *décorateur de chemin d'accès* au lieu du type de retour.
Vous pouvez utiliser le paramètre `response_model` dans n'importe lequel des *chemins d'accès* :
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Remarque
Notez que `response_model` est un paramètre de la méthode « decorator » (`get`, `post`, etc.). Pas de votre *fonction de chemin d'accès*, comme tous les paramètres et le corps.
///
`response_model` reçoit le même type que vous déclareriez pour un champ de modèle Pydantic, il peut donc s'agir d'un modèle Pydantic, mais il peut aussi être, par exemple, une `list` de modèles Pydantic, comme `List[Item]`.
FastAPI utilisera ce `response_model` pour toute la documentation des données, la validation, etc. et aussi pour **convertir et filtrer les données de sortie** selon sa déclaration de type.
/// tip | Astuce
Si vous avez des vérifications de type strictes dans votre éditeur, mypy, etc., vous pouvez déclarer le type de retour de la fonction en `Any`.
Ainsi, vous indiquez à l'éditeur que vous renvoyez intentionnellement n'importe quoi. Mais FastAPI effectuera quand même la documentation, la validation, le filtrage, etc. des données avec `response_model`.
///
### Priorité de `response_model` { #response-model-priority }
Si vous déclarez à la fois un type de retour et un `response_model`, c'est `response_model` qui aura la priorité et sera utilisé par FastAPI.
De cette manière, vous pouvez ajouter des annotations de type correctes à vos fonctions même si vous renvoyez un type différent du modèle de réponse, pour qu'il soit utilisé par l'éditeur et des outils comme mypy. Et vous pouvez toujours laisser FastAPI faire la validation des données, la documentation, etc. avec `response_model`.
Vous pouvez également utiliser `response_model=None` pour désactiver la création d’un modèle de réponse pour ce *chemin d'accès* ; vous pourriez en avoir besoin si vous ajoutez des annotations de type pour des choses qui ne sont pas des champs valides Pydantic, vous verrez un exemple de cela dans une des sections ci-dessous.
## Renvoyer les mêmes données d'entrée { #return-the-same-input-data }
Ici, nous déclarons un modèle `UserIn`, il contiendra un mot de passe en clair :
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Info
Pour utiliser `EmailStr`, installez d'abord [`email-validator`](https://github.com/JoshData/python-email-validator).
Assurez-vous de créer un [environnement virtuel](../virtual-environments.md), de l'activer, puis de l'installer, par exemple :
```console
$ pip install email-validator
```
ou avec :
```console
$ pip install "pydantic[email]"
```
///
Et nous utilisons ce modèle pour déclarer notre entrée et le même modèle pour déclarer notre sortie :
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Désormais, chaque fois qu'un navigateur crée un utilisateur avec un mot de passe, l'API renverra le même mot de passe dans la réponse.
Dans ce cas, cela peut ne pas poser de problème, car c'est le même utilisateur qui envoie le mot de passe.
Mais si nous utilisons le même modèle pour un autre *chemin d'accès*, nous pourrions envoyer les mots de passe de nos utilisateurs à tous les clients.
/// danger | Danger
Ne stockez jamais le mot de passe en clair d'un utilisateur et ne l'envoyez pas dans une réponse de cette manière, à moins de connaître tous les écueils et de savoir exactement ce que vous faites.
///
## Ajouter un modèle de sortie { #add-an-output-model }
Nous pouvons à la place créer un modèle d'entrée avec le mot de passe en clair et un modèle de sortie sans celui-ci :
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Ici, même si notre *fonction de chemin d'accès* renvoie le même utilisateur d'entrée qui contient le mot de passe :
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
... nous avons déclaré `response_model` comme étant notre modèle `UserOut`, qui n'inclut pas le mot de passe :
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Ainsi, **FastAPI** se chargera de filtrer toutes les données qui ne sont pas déclarées dans le modèle de sortie (en utilisant Pydantic).
### `response_model` ou type de retour { #response-model-or-return-type }
Dans ce cas, comme les deux modèles sont différents, si nous annotions le type de retour de la fonction en `UserOut`, l’éditeur et les outils se plaindraient que nous renvoyons un type invalide, car ce sont des classes différentes.
C'est pourquoi, dans cet exemple, nous devons le déclarer dans le paramètre `response_model`.
... mais continuez à lire ci-dessous pour voir comment contourner cela.
## Type de retour et filtrage des données { #return-type-and-data-filtering }
Continuons l'exemple précédent. Nous voulions **annoter la fonction avec un type**, mais nous voulions pouvoir renvoyer depuis la fonction quelque chose qui inclut **plus de données**.
Nous voulons que FastAPI continue de **filtrer** les données à l’aide du modèle de réponse. Ainsi, même si la fonction renvoie plus de données, la réponse n’inclura que les champs déclarés dans le modèle de réponse.
Dans l'exemple précédent, comme les classes étaient différentes, nous avons dû utiliser le paramètre `response_model`. Mais cela signifie aussi que nous ne bénéficions pas de la prise en charge de l'éditeur et des outils pour la vérification du type de retour de la fonction.
Mais dans la plupart des cas où nous avons besoin de quelque chose comme cela, nous voulons que le modèle **filtre/supprime** simplement une partie des données comme dans cet exemple.
Et dans ces cas, nous pouvons utiliser des classes et l'héritage pour tirer parti des **annotations de type** de fonction afin d'obtenir une meilleure prise en charge dans l'éditeur et les outils, tout en bénéficiant du **filtrage de données** de FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Avec cela, nous obtenons la prise en charge des outils, des éditeurs et de mypy car ce code est correct en termes de types, et nous bénéficions également du filtrage des données par FastAPI.
Comment cela fonctionne-t-il ? Voyons cela. 🤓
### Annotations de type et outils { #type-annotations-and-tooling }
Voyons d'abord comment les éditeurs, mypy et autres outils considèreraient cela.
`BaseUser` a les champs de base. Puis `UserIn` hérite de `BaseUser` et ajoute le champ `password`, il inclura donc tous les champs des deux modèles.
Nous annotons le type de retour de la fonction en `BaseUser`, mais nous renvoyons en réalité une instance de `UserIn`.
L’éditeur, mypy et d'autres outils ne s’en plaindront pas car, en termes de typage, `UserIn` est une sous-classe de `BaseUser`, ce qui signifie que c’est un type *valide* lorsque ce qui est attendu est n'importe quoi de type `BaseUser`.
### Filtrage des données par FastAPI { #fastapi-data-filtering }
Maintenant, pour FastAPI, il verra le type de retour et s'assurera que ce que vous renvoyez inclut **uniquement** les champs qui sont déclarés dans le type.
FastAPI fait plusieurs choses en interne avec Pydantic pour s'assurer que ces mêmes règles d'héritage de classes ne sont pas utilisées pour le filtrage des données renvoyées, sinon vous pourriez finir par renvoyer beaucoup plus de données que prévu.
De cette façon, vous obtenez le meilleur des deux mondes : annotations de type avec **prise en charge par les outils** et **filtrage des données**.
## Le voir dans la documentation { #see-it-in-the-docs }
Dans la documentation automatique, vous pouvez vérifier que le modèle d'entrée et le modèle de sortie auront chacun leur propre JSON Schema :
Et les deux modèles seront utilisés pour la documentation API interactive :
## Autres annotations de type de retour { #other-return-type-annotations }
Il peut y avoir des cas où vous renvoyez quelque chose qui n'est pas un champ Pydantic valide et vous l'annotez dans la fonction, uniquement pour obtenir la prise en charge fournie par les outils (l’éditeur, mypy, etc.).
### Renvoyer directement une Response { #return-a-response-directly }
Le cas le plus courant serait [de renvoyer directement une Response comme expliqué plus loin dans la documentation avancée](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Ce cas simple est géré automatiquement par FastAPI car l'annotation du type de retour est la classe (ou une sous-classe de) `Response`.
Et les outils seront également satisfaits car `RedirectResponse` et `JSONResponse` sont des sous-classes de `Response`, donc l'annotation de type est correcte.
### Annoter une sous-classe de Response { #annotate-a-response-subclass }
Vous pouvez aussi utiliser une sous-classe de `Response` dans l'annotation de type :
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Cela fonctionnera également car `RedirectResponse` est une sous-classe de `Response`, et FastAPI gérera automatiquement ce cas simple.
### Annotations de type de retour invalides { #invalid-return-type-annotations }
Mais lorsque vous renvoyez un autre objet arbitraire qui n'est pas un type Pydantic valide (par exemple un objet de base de données) et que vous l'annotez ainsi dans la fonction, FastAPI essaiera de créer un modèle de réponse Pydantic à partir de cette annotation de type, et échouera.
Il en serait de même si vous aviez quelque chose comme une union entre différents types dont un ou plusieurs ne sont pas des types Pydantic valides, par exemple ceci échouerait 💥 :
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
... cela échoue parce que l'annotation de type n'est pas un type Pydantic et n'est pas juste une unique classe `Response` ou une sous-classe, c'est une union (l'un des deux) entre une `Response` et un `dict`.
### Désactiver le modèle de réponse { #disable-response-model }
En reprenant l'exemple ci-dessus, vous pourriez ne pas vouloir avoir la validation par défaut des données, la documentation, le filtrage, etc. effectués par FastAPI.
Mais vous pourriez vouloir tout de même conserver l’annotation du type de retour dans la fonction pour bénéficier de la prise en charge des outils comme les éditeurs et les vérificateurs de type (par exemple mypy).
Dans ce cas, vous pouvez désactiver la génération du modèle de réponse en définissant `response_model=None` :
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Cela fera en sorte que FastAPI ignore la génération du modèle de réponse et vous permettra ainsi d’avoir toutes les annotations de type de retour dont vous avez besoin sans que cela n’affecte votre application FastAPI. 🤓
## Paramètres d'encodage du modèle de réponse { #response-model-encoding-parameters }
Votre modèle de réponse peut avoir des valeurs par défaut, par exemple :
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (ou `str | None = None` en Python 3.10) a une valeur par défaut `None`.
* `tax: float = 10.5` a une valeur par défaut `10.5`.
* `tags: List[str] = []` a une valeur par défaut de liste vide : `[]`.
mais vous pourriez vouloir les omettre du résultat si elles n'ont pas été réellement stockées.
Par exemple, si vous avez des modèles avec de nombreux attributs optionnels dans une base NoSQL, mais que vous ne voulez pas envoyer de très longues réponses JSON remplies de valeurs par défaut.
### Utiliser le paramètre `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Vous pouvez définir le paramètre du *décorateur de chemin d'accès* `response_model_exclude_unset=True` :
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
et ces valeurs par défaut ne seront pas incluses dans la réponse, uniquement les valeurs effectivement définies.
Ainsi, si vous envoyez une requête à ce *chemin d'accès* pour l'article avec l'ID `foo`, la réponse (sans les valeurs par défaut) sera :
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Info
Vous pouvez également utiliser :
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
comme décrit dans [la documentation Pydantic](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) pour `exclude_defaults` et `exclude_none`.
///
#### Données avec des valeurs pour des champs avec des valeurs par défaut { #data-with-values-for-fields-with-defaults }
Mais si vos données ont des valeurs pour les champs du modèle avec des valeurs par défaut, comme l'article avec l'ID `bar` :
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
elles seront incluses dans la réponse.
#### Données avec les mêmes valeurs que les valeurs par défaut { #data-with-the-same-values-as-the-defaults }
Si les données ont les mêmes valeurs que les valeurs par défaut, comme l'article avec l'ID `baz` :
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI est suffisamment intelligent (en fait, Pydantic l’est) pour comprendre que, même si `description`, `tax` et `tags` ont les mêmes valeurs que les valeurs par défaut, elles ont été définies explicitement (au lieu d'être prises depuis les valeurs par défaut).
Elles seront donc incluses dans la réponse JSON.
/// tip | Astuce
Notez que les valeurs par défaut peuvent être n'importe quoi, pas seulement `None`.
Elles peuvent être une liste (`[]`), un `float` de `10.5`, etc.
///
### `response_model_include` et `response_model_exclude` { #response-model-include-and-response-model-exclude }
Vous pouvez également utiliser les paramètres du *décorateur de chemin d'accès* `response_model_include` et `response_model_exclude`.
Ils prennent un `set` de `str` avec les noms des attributs à inclure (en omettant le reste) ou à exclure (en incluant le reste).
Cela peut être utilisé comme un raccourci rapide si vous n'avez qu'un seul modèle Pydantic et que vous souhaitez supprimer certaines données de la sortie.
/// tip | Astuce
Mais il est toujours recommandé d'utiliser les idées ci-dessus, en utilisant plusieurs classes, plutôt que ces paramètres.
En effet, le JSON Schema généré dans l’OpenAPI de votre application (et la documentation) restera celui du modèle complet, même si vous utilisez `response_model_include` ou `response_model_exclude` pour omettre certains attributs.
Cela s'applique également à `response_model_by_alias` qui fonctionne de manière similaire.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Astuce
La syntaxe `{"name", "description"}` crée un `set` avec ces deux valeurs.
Elle est équivalente à `set(["name", "description"])`.
///
#### Utiliser des `list` au lieu de `set` { #using-lists-instead-of-sets }
Si vous oubliez d'utiliser un `set` et utilisez une `list` ou un `tuple` à la place, FastAPI le convertira quand même en `set` et cela fonctionnera correctement :
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Récapitulatif { #recap }
Utilisez le paramètre du *décorateur de chemin d'accès* `response_model` pour définir les modèles de réponse et surtout pour garantir que les données privées sont filtrées.
Utilisez `response_model_exclude_unset` pour ne renvoyer que les valeurs définies explicitement.
================================================
FILE: docs/fr/docs/tutorial/response-status-code.md
================================================
# Code d'état de la réponse { #response-status-code }
De la même manière que vous pouvez spécifier un modèle de réponse, vous pouvez également déclarer le code d'état HTTP utilisé pour la réponse avec le paramètre `status_code` dans n'importe lequel des chemins d'accès :
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Remarque
Remarquez que `status_code` est un paramètre de la méthode « decorator » (`get`, `post`, etc.). Pas de votre fonction de chemin d'accès, comme tous les paramètres et le corps.
///
Le paramètre `status_code` reçoit un nombre correspondant au code d'état HTTP.
/// info
`status_code` peut aussi recevoir un `IntEnum`, comme le [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) de Python.
///
Il va :
* Renvoyer ce code d'état dans la réponse.
* Le documenter comme tel dans le schéma OpenAPI (et donc dans les interfaces utilisateur) :
/// note | Remarque
Certains codes de réponse (voir la section suivante) indiquent que la réponse n'a pas de corps.
FastAPI le sait et produira une documentation OpenAPI indiquant qu'il n'y a pas de corps de réponse.
///
## À propos des codes d'état HTTP { #about-http-status-codes }
/// note | Remarque
Si vous savez déjà ce que sont les codes d'état HTTP, passez à la section suivante.
///
En HTTP, vous envoyez un code d'état numérique de 3 chiffres dans la réponse.
Ces codes d'état ont un nom associé pour les reconnaître, mais la partie importante est le nombre.
En bref :
* `100 - 199` sont pour « Information ». Vous les utilisez rarement directement. Les réponses avec ces codes d'état ne peuvent pas avoir de corps.
* **`200 - 299`** sont pour les réponses de « Succès ». Ce sont celles que vous utiliserez le plus.
* `200` est le code d'état par défaut, ce qui signifie que tout était « OK ».
* Un autre exemple est `201`, « Créé ». Il est couramment utilisé après la création d'un nouvel enregistrement dans la base de données.
* Un cas particulier est `204`, « Aucun contenu ». Cette réponse est utilisée lorsqu'il n'y a aucun contenu à renvoyer au client ; la réponse ne doit donc pas avoir de corps.
* **`300 - 399`** sont pour la « Redirection ». Les réponses avec ces codes d'état peuvent avoir ou non un corps, sauf `304`, « Non modifié », qui ne doit pas en avoir.
* **`400 - 499`** sont pour les réponses d'« Erreur côté client ». C'est probablement le deuxième type que vous utiliserez le plus.
* Un exemple est `404`, pour une réponse « Non trouvé ».
* Pour des erreurs génériques du client, vous pouvez simplement utiliser `400`.
* `500 - 599` sont pour les erreurs côté serveur. Vous ne les utilisez presque jamais directement. Lorsqu'un problème survient quelque part dans le code de votre application ou sur le serveur, il renverra automatiquement l'un de ces codes d'état.
/// tip | Astuce
Pour en savoir plus sur chaque code d'état et à quoi il correspond, consultez la [MDN documentation sur les codes d'état HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Raccourci pour se souvenir des noms { #shortcut-to-remember-the-names }
Reprenons l'exemple précédent :
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` est le code d'état pour « Créé ».
Mais vous n'avez pas à mémoriser la signification de chacun de ces codes.
Vous pouvez utiliser les variables pratiques de `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Elles ne sont qu'une commodité, elles contiennent le même nombre, mais de cette façon vous pouvez utiliser l'autocomplétion de l'éditeur pour les trouver :
/// note | Détails techniques
Vous pourriez aussi utiliser `from starlette import status`.
**FastAPI** fournit le même `starlette.status` que `fastapi.status`, uniquement pour votre commodité de développeur. Mais cela vient directement de Starlette.
///
## Modifier la valeur par défaut { #changing-the-default }
Plus tard, dans le [Guide utilisateur avancé](../advanced/response-change-status-code.md), vous verrez comment renvoyer un code d'état différent de celui par défaut que vous déclarez ici.
================================================
FILE: docs/fr/docs/tutorial/schema-extra-example.md
================================================
# Déclarer des exemples de données de requête { #declare-request-example-data }
Vous pouvez déclarer des exemples des données que votre application peut recevoir.
Voici plusieurs façons de le faire.
## Ajouter des données JSON Schema supplémentaires dans les modèles Pydantic { #extra-json-schema-data-in-pydantic-models }
Vous pouvez déclarer `examples` pour un modèle Pydantic qui seront ajoutés au JSON Schema généré.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Ces informations supplémentaires seront ajoutées telles quelles au **JSON Schema** de sortie pour ce modèle, et elles seront utilisées dans la documentation de l'API.
Vous pouvez utiliser l'attribut `model_config` qui accepte un `dict` comme décrit dans [Documentation de Pydantic : Configuration](https://docs.pydantic.dev/latest/api/config/).
Vous pouvez définir `"json_schema_extra"` avec un `dict` contenant toutes les données supplémentaires que vous souhaitez voir apparaître dans le JSON Schema généré, y compris `examples`.
/// tip | Astuce
Vous pouvez utiliser la même technique pour étendre le JSON Schema et ajouter vos propres informations supplémentaires personnalisées.
Par exemple, vous pourriez l'utiliser pour ajouter des métadonnées pour une interface utilisateur frontend, etc.
///
/// info
OpenAPI 3.1.0 (utilisé depuis FastAPI 0.99.0) a ajouté la prise en charge de `examples`, qui fait partie du standard **JSON Schema**.
Avant cela, seule la clé `example` avec un exemple unique était prise en charge. Elle l'est toujours par OpenAPI 3.1.0, mais elle est dépréciée et ne fait pas partie du standard JSON Schema. Vous êtes donc encouragé à migrer de `example` vers `examples`. 🤓
Vous pouvez en lire davantage à la fin de cette page.
///
## Arguments supplémentaires de `Field` { #field-additional-arguments }
Lorsque vous utilisez `Field()` avec des modèles Pydantic, vous pouvez également déclarer des `examples` supplémentaires :
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` dans JSON Schema - OpenAPI { #examples-in-json-schema-openapi }
En utilisant l'un des éléments suivants :
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
vous pouvez également déclarer un groupe de `examples` avec des informations supplémentaires qui seront ajoutées à leurs **JSON Schemas** à l'intérieur d'**OpenAPI**.
### `Body` avec `examples` { #body-with-examples }
Ici, nous passons `examples` contenant un exemple des données attendues dans `Body()` :
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Exemple dans l'interface des documents { #example-in-the-docs-ui }
Avec l'une des méthodes ci-dessus, cela ressemblerait à ceci dans le `/docs` :
### `Body` avec plusieurs `examples` { #body-with-multiple-examples }
Vous pouvez bien sûr aussi passer plusieurs `examples` :
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Lorsque vous faites cela, les exemples feront partie du **JSON Schema** interne pour ces données de corps.
Néanmoins, au moment de la rédaction, Swagger UI, l'outil chargé d'afficher l'interface des documents, ne prend pas en charge l'affichage de plusieurs exemples pour les données dans **JSON Schema**. Mais lisez ci-dessous pour un contournement.
### `examples` spécifiques à OpenAPI { #openapi-specific-examples }
Avant que **JSON Schema** ne prenne en charge `examples`, OpenAPI prenait déjà en charge un autre champ également appelé `examples`.
Ce `examples` **spécifique à OpenAPI** se trouve dans une autre section de la spécification OpenAPI. Il se trouve dans les **détails de chaque *chemin d'accès***, et non à l'intérieur de chaque JSON Schema.
Et Swagger UI prend en charge ce champ particulier `examples` depuis un certain temps. Vous pouvez donc l'utiliser pour **afficher** différents **exemples dans l'interface des documents**.
La forme de ce champ `examples` spécifique à OpenAPI est un `dict` avec **plusieurs exemples** (au lieu d'une `list`), chacun avec des informations supplémentaires qui seront également ajoutées à **OpenAPI**.
Cela ne va pas à l'intérieur de chaque JSON Schema contenu dans OpenAPI, cela se place à l'extérieur, directement dans le *chemin d'accès*.
### Utiliser le paramètre `openapi_examples` { #using-the-openapi-examples-parameter }
Vous pouvez déclarer le `examples` spécifique à OpenAPI dans FastAPI avec le paramètre `openapi_examples` pour :
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
Les clés du `dict` identifient chaque exemple, et chaque valeur est un autre `dict`.
Chaque `dict` d'exemple spécifique dans `examples` peut contenir :
* `summary` : une courte description de l'exemple.
* `description` : une description longue qui peut contenir du texte Markdown.
* `value` : c'est l'exemple réel affiché, par ex. un `dict`.
* `externalValue` : alternative à `value`, une URL pointant vers l'exemple. Cependant, cela pourrait ne pas être pris en charge par autant d'outils que `value`.
Vous pouvez l'utiliser ainsi :
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Exemples OpenAPI dans l'interface des documents { #openapi-examples-in-the-docs-ui }
Avec `openapi_examples` ajouté à `Body()`, le `/docs` ressemblerait à :
## Détails techniques { #technical-details }
/// tip | Astuce
Si vous utilisez déjà **FastAPI** en version **0.99.0 ou supérieure**, vous pouvez probablement **passer** ces détails.
Ils sont plus pertinents pour les versions plus anciennes, avant que OpenAPI 3.1.0 ne soit disponible.
Vous pouvez considérer ceci comme une courte leçon d'histoire d'OpenAPI et de JSON Schema. 🤓
///
/// warning | Alertes
Ce sont des détails très techniques au sujet des standards **JSON Schema** et **OpenAPI**.
Si les idées ci-dessus fonctionnent déjà pour vous, cela pourrait suffire, et vous n'avez probablement pas besoin de ces détails, n'hésitez pas à les ignorer.
///
Avant OpenAPI 3.1.0, OpenAPI utilisait une version plus ancienne et modifiée de **JSON Schema**.
JSON Schema n'avait pas `examples`, donc OpenAPI a ajouté son propre champ `example` à sa version modifiée.
OpenAPI a également ajouté les champs `example` et `examples` à d'autres parties de la spécification :
* [`Parameter Object` (dans la spécification)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object) qui était utilisé par les éléments FastAPI :
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, dans le champ `content`, sur le `Media Type Object` (dans la spécification)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object) qui était utilisé par les éléments FastAPI :
* `Body()`
* `File()`
* `Form()`
/// info
Ce paramètre `examples` ancien et spécifique à OpenAPI est désormais `openapi_examples` depuis FastAPI `0.103.0`.
///
### Le champ `examples` de JSON Schema { #json-schemas-examples-field }
Ensuite, JSON Schema a ajouté un champ [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) dans une nouvelle version de la spécification.
Puis le nouveau OpenAPI 3.1.0 s'est basé sur la dernière version (JSON Schema 2020-12) qui incluait ce nouveau champ `examples`.
Et désormais, ce nouveau champ `examples` a priorité sur l'ancien champ unique (et personnalisé) `example`, qui est maintenant déprécié.
Ce nouveau champ `examples` dans JSON Schema est **juste une `list`** d'exemples, et non pas un dict avec des métadonnées supplémentaires comme dans les autres endroits d'OpenAPI (décrits ci-dessus).
/// info
Même après la sortie d'OpenAPI 3.1.0 avec cette nouvelle intégration plus simple avec JSON Schema, pendant un temps, Swagger UI, l'outil qui fournit la documentation automatique, ne prenait pas en charge OpenAPI 3.1.0 (il le fait depuis la version 5.0.0 🎉).
À cause de cela, les versions de FastAPI antérieures à 0.99.0 utilisaient encore des versions d'OpenAPI inférieures à 3.1.0.
///
### `examples` avec Pydantic et FastAPI { #pydantic-and-fastapi-examples }
Lorsque vous ajoutez `examples` dans un modèle Pydantic, en utilisant `schema_extra` ou `Field(examples=["something"])`, cet exemple est ajouté au **JSON Schema** de ce modèle Pydantic.
Et ce **JSON Schema** du modèle Pydantic est inclus dans l'**OpenAPI** de votre API, puis il est utilisé dans l'interface de la documentation.
Dans les versions de FastAPI antérieures à 0.99.0 (0.99.0 et supérieures utilisent le nouveau OpenAPI 3.1.0), lorsque vous utilisiez `example` ou `examples` avec l'une des autres utilitaires (`Query()`, `Body()`, etc.), ces exemples n'étaient pas ajoutés au JSON Schema qui décrit ces données (pas même à la version de JSON Schema propre à OpenAPI), ils étaient ajoutés directement à la déclaration du *chemin d'accès* dans OpenAPI (en dehors des parties d'OpenAPI qui utilisent JSON Schema).
Mais maintenant que FastAPI 0.99.0 et supérieures utilisent OpenAPI 3.1.0, qui utilise JSON Schema 2020-12, et Swagger UI 5.0.0 et supérieures, tout est plus cohérent et les exemples sont inclus dans JSON Schema.
### Swagger UI et `examples` spécifiques à OpenAPI { #swagger-ui-and-openapi-specific-examples }
Comme Swagger UI ne prenait pas en charge plusieurs exemples JSON Schema (au 2023-08-26), les utilisateurs n'avaient pas de moyen d'afficher plusieurs exemples dans les documents.
Pour résoudre cela, FastAPI `0.103.0` a **ajouté la prise en charge** de la déclaration du même ancien champ `examples` **spécifique à OpenAPI** avec le nouveau paramètre `openapi_examples`. 🤓
### Résumé { #summary }
Je disais que je n'aimais pas trop l'histoire ... et me voilà maintenant à donner des leçons d'« tech history ». 😅
En bref, **mettez à niveau vers FastAPI 0.99.0 ou supérieur**, et les choses sont bien plus **simples, cohérentes et intuitives**, et vous n'avez pas besoin de connaître tous ces détails historiques. 😎
================================================
FILE: docs/fr/docs/tutorial/security/first-steps.md
================================================
# Sécurité - Premiers pas { #security-first-steps }
Imaginons que vous ayez votre API de **backend** sur un certain domaine.
Et vous avez un **frontend** sur un autre domaine ou dans un chemin différent du même domaine (ou dans une application mobile).
Et vous voulez que le **frontend** puisse s'authentifier auprès du **backend**, en utilisant un **username** et un **password**.
Nous pouvons utiliser **OAuth2** pour construire cela avec **FastAPI**.
Mais épargnons-vous le temps de lire toute la spécification complète juste pour trouver les petites informations dont vous avez besoin.
Utilisons les outils fournis par **FastAPI** pour gérer la sécurité.
## Voir à quoi cela ressemble { #how-it-looks }
Commençons par utiliser le code et voir comment cela fonctionne, puis nous reviendrons pour comprendre ce qui se passe.
## Créer `main.py` { #create-main-py }
Copiez l'exemple dans un fichier `main.py` :
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Exécuter { #run-it }
/// info
Le package [`python-multipart`](https://github.com/Kludex/python-multipart) est installé automatiquement avec **FastAPI** lorsque vous exécutez la commande `pip install "fastapi[standard]"`.
Cependant, si vous utilisez la commande `pip install fastapi`, le package `python-multipart` n'est pas inclus par défaut.
Pour l'installer manuellement, vous devez vous assurer de créer un [environnement virtuel](../../virtual-environments.md), de l'activer, puis de l'installer avec :
```console
$ pip install python-multipart
```
Cela est dû au fait que **OAuth2** utilise des « form data » pour envoyer le `username` et le `password`.
///
Exécutez l'exemple avec :
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Vérifier { #check-it }
Allez à la documentation interactive à l'adresse : [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez quelque chose comme ceci :
/// check | Bouton « Authorize » !
Vous avez déjà un tout nouveau bouton « Authorize ».
Et votre *chemin d'accès* a un petit cadenas dans le coin supérieur droit sur lequel vous pouvez cliquer.
///
Et si vous cliquez dessus, vous obtenez un petit formulaire d'autorisation pour saisir un `username` et un `password` (et d'autres champs optionnels) :
/// note | Remarque
Peu importe ce que vous saisissez dans le formulaire, cela ne fonctionnera pas encore. Mais nous y viendrons.
///
Ce n'est bien sûr pas le frontend pour les utilisateurs finaux, mais c'est un excellent outil automatique pour documenter de manière interactive toute votre API.
Il peut être utilisé par l'équipe frontend (qui peut aussi être vous-même).
Il peut être utilisé par des applications et des systèmes tiers.
Et il peut aussi être utilisé par vous-même, pour déboguer, vérifier et tester la même application.
## Le flux `password` { #the-password-flow }
Revenons un peu en arrière et comprenons de quoi il s'agit.
Le « flux » `password` est l'une des manières (« flows ») définies dans OAuth2 pour gérer la sécurité et l'authentification.
OAuth2 a été conçu pour que le backend ou l'API puisse être indépendant du serveur qui authentifie l'utilisateur.
Mais dans ce cas, la même application **FastAPI** gérera l'API et l'authentification.
Voyons cela selon ce point de vue simplifié :
- L'utilisateur saisit le `username` et le `password` dans le frontend, puis appuie sur Entrée.
- Le frontend (exécuté dans le navigateur de l'utilisateur) envoie ce `username` et ce `password` vers une URL spécifique de notre API (déclarée avec `tokenUrl="token"`).
- L'API vérifie ce `username` et ce `password`, et répond avec un « token » (nous n'avons encore rien implémenté de tout cela).
- Un « token » n'est qu'une chaîne contenant des informations que nous pouvons utiliser plus tard pour vérifier cet utilisateur.
- Normalement, un token est configuré pour expirer après un certain temps.
- Ainsi, l'utilisateur devra se reconnecter à un moment donné.
- Et si le token est volé, le risque est moindre. Ce n'est pas une clé permanente qui fonctionnerait indéfiniment (dans la plupart des cas).
- Le frontend stocke ce token temporairement quelque part.
- L'utilisateur clique dans le frontend pour aller vers une autre section de l'application web frontend.
- Le frontend doit récupérer d'autres données depuis l'API.
- Mais cela nécessite une authentification pour cet endpoint spécifique.
- Donc, pour s'authentifier auprès de notre API, il envoie un en-tête `Authorization` avec une valeur `Bearer ` suivie du token.
- Si le token contient `foobar`, le contenu de l'en-tête `Authorization` serait : `Bearer foobar`.
## Le `OAuth2PasswordBearer` de **FastAPI** { #fastapis-oauth2passwordbearer }
**FastAPI** fournit plusieurs outils, à différents niveaux d'abstraction, pour implémenter ces fonctionnalités de sécurité.
Dans cet exemple, nous allons utiliser **OAuth2**, avec le flux **Password**, en utilisant un token **Bearer**. Nous le faisons avec la classe `OAuth2PasswordBearer`.
/// info
Un token « bearer » n'est pas la seule option.
Mais c'est la meilleure pour notre cas d'utilisation.
Et cela pourrait être la meilleure pour la plupart des cas, sauf si vous êtes expert en OAuth2 et savez exactement pourquoi une autre option convient mieux à vos besoins.
Dans ce cas, **FastAPI** vous fournit aussi les outils pour la construire.
///
Lorsque nous créons une instance de la classe `OAuth2PasswordBearer`, nous passons le paramètre `tokenUrl`. Ce paramètre contient l'URL que le client (le frontend s'exécutant dans le navigateur de l'utilisateur) utilisera pour envoyer le `username` et le `password` afin d'obtenir un token.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Astuce
Ici `tokenUrl="token"` fait référence à une URL relative `token` que nous n'avons pas encore créée. Comme c'est une URL relative, elle est équivalente à `./token`.
Parce que nous utilisons une URL relative, si votre API se trouvait à `https://example.com/`, alors elle ferait référence à `https://example.com/token`. Mais si votre API se trouvait à `https://example.com/api/v1/`, alors elle ferait référence à `https://example.com/api/v1/token`.
Utiliser une URL relative est important pour vous assurer que votre application continue de fonctionner même dans un cas d'usage avancé comme [Derrière un proxy](../../advanced/behind-a-proxy.md).
///
Ce paramètre ne crée pas cet endpoint / *chemin d'accès*, mais déclare que l'URL `/token` sera celle que le client doit utiliser pour obtenir le token. Cette information est utilisée dans OpenAPI, puis dans les systèmes de documentation API interactifs.
Nous créerons bientôt aussi le véritable chemin d'accès.
/// info
Si vous êtes un « Pythonista » très strict, vous pourriez ne pas apprécier le style du nom de paramètre `tokenUrl` au lieu de `token_url`.
C'est parce qu'il utilise le même nom que dans la spécification OpenAPI. Ainsi, si vous devez approfondir l'un de ces schémas de sécurité, vous pouvez simplement copier-coller pour trouver plus d'informations à ce sujet.
///
La variable `oauth2_scheme` est une instance de `OAuth2PasswordBearer`, mais c'est aussi un « callable ».
Elle pourrait être appelée ainsi :
```Python
oauth2_scheme(some, parameters)
```
Ainsi, elle peut être utilisée avec `Depends`.
### Utiliser { #use-it }
Vous pouvez maintenant passer ce `oauth2_scheme` en dépendance avec `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Cette dépendance fournira une `str` qui est affectée au paramètre `token` de la fonction de *chemin d'accès*.
**FastAPI** saura qu'il peut utiliser cette dépendance pour définir un « schéma de sécurité » dans le schéma OpenAPI (et la documentation API automatique).
/// info | Détails techniques
**FastAPI** saura qu'il peut utiliser la classe `OAuth2PasswordBearer` (déclarée dans une dépendance) pour définir le schéma de sécurité dans OpenAPI parce qu'elle hérite de `fastapi.security.oauth2.OAuth2`, qui hérite à son tour de `fastapi.security.base.SecurityBase`.
Tous les utilitaires de sécurité qui s'intègrent à OpenAPI (et à la documentation API automatique) héritent de `SecurityBase`, c'est ainsi que **FastAPI** sait comment les intégrer dans OpenAPI.
///
## Ce que cela fait { #what-it-does }
Il va chercher dans la requête cet en-tête `Authorization`, vérifier si la valeur est `Bearer ` plus un token, et renverra le token en tant que `str`.
S'il ne voit pas d'en-tête `Authorization`, ou si la valeur n'a pas de token `Bearer `, il répondra directement avec une erreur de code d'état 401 (`UNAUTHORIZED`).
Vous n'avez même pas à vérifier si le token existe pour renvoyer une erreur. Vous pouvez être sûr que si votre fonction est exécutée, elle aura une `str` dans ce token.
Vous pouvez déjà l'essayer dans la documentation interactive :
Nous ne vérifions pas encore la validité du token, mais c'est déjà un début.
## Récapitulatif { #recap }
Ainsi, en seulement 3 ou 4 lignes supplémentaires, vous disposez déjà d'une forme primitive de sécurité.
================================================
FILE: docs/fr/docs/tutorial/security/get-current-user.md
================================================
# Obtenir l'utilisateur actuel { #get-current-user }
Dans le chapitre précédent, le système de sécurité (basé sur le système d'injection de dépendances) fournissait à la *fonction de chemin d'accès* un `token` en tant que `str` :
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Mais ce n'est pas encore très utile.
Faisons en sorte qu'il nous fournisse l'utilisateur actuel.
## Créer un modèle d'utilisateur { #create-a-user-model }
Commençons par créer un modèle d'utilisateur Pydantic.
De la même manière que nous utilisons Pydantic pour déclarer des corps de requête, nous pouvons l'utiliser ailleurs :
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Créer une dépendance `get_current_user` { #create-a-get-current-user-dependency }
Créons une dépendance `get_current_user`.
Rappelez-vous que les dépendances peuvent avoir des sous-dépendances ?
`get_current_user` aura une dépendance avec le même `oauth2_scheme` que nous avons créé précédemment.
Comme nous le faisions auparavant directement dans le *chemin d'accès*, notre nouvelle dépendance `get_current_user` recevra un `token` en tant que `str` de la sous-dépendance `oauth2_scheme` :
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Récupérer l'utilisateur { #get-the-user }
`get_current_user` utilisera une fonction utilitaire (factice) que nous avons créée, qui prend un token en `str` et retourne notre modèle Pydantic `User` :
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Injecter l'utilisateur actuel { #inject-the-current-user }
Nous pouvons donc utiliser le même `Depends` avec notre `get_current_user` dans le *chemin d'accès* :
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Remarquez que nous déclarons le type de `current_user` comme le modèle Pydantic `User`.
Cela nous aidera dans la fonction avec toute l'autocomplétion et les vérifications de type.
/// tip | Astuce
Vous vous souvenez peut-être que les corps de requête sont également déclarés avec des modèles Pydantic.
Ici, **FastAPI** ne s'y trompera pas car vous utilisez `Depends`.
///
/// check | Vérifications
La manière dont ce système de dépendances est conçu nous permet d'avoir différentes dépendances (différents « dependables ») qui retournent toutes un modèle `User`.
Nous ne sommes pas limités à une seule dépendance pouvant retourner ce type de données.
///
## Autres modèles { #other-models }
Vous pouvez maintenant obtenir l'utilisateur actuel directement dans les *fonctions de chemin d'accès* et gérer les mécanismes de sécurité au niveau de l'**Injection de dépendances**, en utilisant `Depends`.
Et vous pouvez utiliser n'importe quel modèle ou données pour les exigences de sécurité (dans ce cas, un modèle Pydantic `User`).
Mais vous n'êtes pas limité à un modèle, une classe ou un type de données spécifique.
Voulez-vous avoir un `id` et `email` et ne pas avoir de `username` dans votre modèle ? Bien sûr. Vous pouvez utiliser ces mêmes outils.
Voulez-vous simplement avoir un `str` ? Ou juste un `dict` ? Ou directement une instance d'un modèle de classe de base de données ? Tout fonctionne de la même manière.
Vous n'avez en fait pas d'utilisateurs qui se connectent à votre application, mais des robots, bots ou d'autres systèmes, qui n'ont qu'un jeton d'accès ? Là encore, tout fonctionne de la même façon.
Utilisez simplement tout type de modèle, toute sorte de classe, tout type de base de données dont vous avez besoin pour votre application. **FastAPI** vous couvre avec le système d'injection de dépendances.
## Taille du code { #code-size }
Cet exemple peut sembler verbeux. Gardez à l'esprit que nous mélangeons sécurité, modèles de données, fonctions utilitaires et *chemins d'accès* dans le même fichier.
Mais voici le point clé.
La partie sécurité et injection de dépendances est écrite une seule fois.
Et vous pouvez la rendre aussi complexe que vous le souhaitez. Et malgré tout, ne l'écrire qu'une seule fois, en un seul endroit. Avec toute la flexibilité.
Mais vous pouvez avoir des milliers d'endpoints (*chemins d'accès*) utilisant le même système de sécurité.
Et tous (ou seulement une partie d'entre eux, si vous le souhaitez) peuvent profiter de la réutilisation de ces dépendances ou de toute autre dépendance que vous créez.
Et tous ces milliers de *chemins d'accès* peuvent tenir en seulement 3 lignes :
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Récapitulatif { #recap }
Vous pouvez désormais obtenir l'utilisateur actuel directement dans votre *fonction de chemin d'accès*.
Nous avons déjà fait la moitié du chemin.
Il nous suffit d'ajouter un *chemin d'accès* pour que l'utilisateur/client envoie effectivement le `username` et le `password`.
C'est pour la suite.
================================================
FILE: docs/fr/docs/tutorial/security/index.md
================================================
# Sécurité { #security }
Il existe de nombreuses façons de gérer la sécurité, l'authentification et l'autorisation.
Et c'est normalement un sujet complexe et « difficile ».
Dans de nombreux frameworks et systèmes, le simple fait de gérer la sécurité et l'authentification demande beaucoup d'efforts et de code (dans de nombreux cas, cela peut représenter 50 % ou plus de tout le code écrit).
**FastAPI** fournit plusieurs outils pour vous aider à gérer la **Sécurité** facilement, rapidement, de manière standard, sans avoir à étudier et apprendre toutes les spécifications de sécurité.
Mais d'abord, voyons quelques notions.
## Pressé ? { #in-a-hurry }
Si ces termes ne vous intéressent pas et que vous avez simplement besoin d'ajouter une sécurité avec une authentification basée sur un nom d'utilisateur et un mot de passe immédiatement, passez aux chapitres suivants.
## OAuth2 { #oauth2 }
OAuth2 est une spécification qui définit plusieurs façons de gérer l'authentification et l'autorisation.
C'est une spécification assez vaste qui couvre plusieurs cas d'utilisation complexes.
Elle inclut des moyens de s'authentifier en utilisant un « tiers ».
C'est ce que tous les systèmes avec « connexion avec Facebook, Google, X (Twitter), GitHub » utilisent en arrière-plan.
### OAuth 1 { #oauth-1 }
Il y a eu un OAuth 1, très différent d'OAuth2, et plus complexe, car il incluait des spécifications directes sur la manière de chiffrer la communication.
Il n'est plus très populaire ni utilisé de nos jours.
OAuth2 ne spécifie pas comment chiffrer la communication ; il suppose que votre application est servie en HTTPS.
/// tip | Astuce
Dans la section sur le déploiement, vous verrez comment configurer HTTPS gratuitement, en utilisant Traefik et Let's Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect est une autre spécification, basée sur **OAuth2**.
Elle étend simplement OAuth2 en précisant certains points relativement ambigus dans OAuth2, afin d'essayer de la rendre plus interopérable.
Par exemple, la connexion Google utilise OpenID Connect (qui, en arrière-plan, utilise OAuth2).
Mais la connexion Facebook ne prend pas en charge OpenID Connect. Elle a sa propre variante d'OAuth2.
### OpenID (pas « OpenID Connect ») { #openid-not-openid-connect }
Il y avait aussi une spécification « OpenID ». Elle essayait de résoudre la même chose qu'**OpenID Connect**, mais n'était pas basée sur OAuth2.
C'était donc un système totalement distinct.
Il n'est plus très populaire ni utilisé de nos jours.
## OpenAPI { #openapi }
OpenAPI (précédemment connu sous le nom de Swagger) est la spécification ouverte pour construire des API (désormais partie de la Linux Foundation).
**FastAPI** est basé sur **OpenAPI**.
C'est ce qui rend possibles plusieurs interfaces de documentation interactive automatiques, la génération de code, etc.
OpenAPI propose une manière de définir plusieurs « schémas » de sécurité.
En les utilisant, vous pouvez tirer parti de tous ces outils basés sur des standards, y compris ces systèmes de documentation interactive.
OpenAPI définit les schémas de sécurité suivants :
* `apiKey` : une clé spécifique à l'application qui peut provenir :
* D'un paramètre de requête.
* D'un en-tête.
* D'un cookie.
* `http` : des systèmes d'authentification HTTP standards, notamment :
* `bearer` : un en-tête `Authorization` avec une valeur `Bearer ` plus un jeton. Hérité d'OAuth2.
* Authentification HTTP Basic.
* HTTP Digest, etc.
* `oauth2` : toutes les méthodes OAuth2 pour gérer la sécurité (appelées « flows »).
* Plusieurs de ces flows conviennent pour construire un fournisseur d'authentification OAuth 2.0 (comme Google, Facebook, X (Twitter), GitHub, etc.) :
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Mais il existe un « flow » spécifique qui peut parfaitement être utilisé pour gérer l'authentification directement dans la même application :
* `password` : certains des prochains chapitres couvriront des exemples à ce sujet.
* `openIdConnect` : propose un moyen de définir comment découvrir automatiquement les données d'authentification OAuth2.
* Cette découverte automatique est ce qui est défini dans la spécification OpenID Connect.
/// tip | Astuce
Intégrer d'autres fournisseurs d'authentification/autorisation comme Google, Facebook, X (Twitter), GitHub, etc. est également possible et relativement facile.
Le problème le plus complexe est de construire un fournisseur d'authentification/autorisation comme ceux-là, mais **FastAPI** vous donne les outils pour le faire facilement, tout en effectuant le gros du travail pour vous.
///
## Outils **FastAPI** { #fastapi-utilities }
FastAPI propose plusieurs outils pour chacun de ces schémas de sécurité dans le module fastapi.security qui simplifient l'utilisation de ces mécanismes de sécurité.
Dans les prochains chapitres, vous verrez comment ajouter de la sécurité à votre API en utilisant ces outils fournis par **FastAPI**.
Et vous verrez aussi comment cela s'intègre automatiquement au système de documentation interactive.
================================================
FILE: docs/fr/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 avec mot de passe (et hachage), Bearer avec des jetons JWT { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Maintenant que nous avons tout le flux de sécurité, rendons réellement l'application sécurisée, en utilisant des jetons JWT et un hachage de mot de passe sécurisé.
Ce code est utilisable dans votre application, enregistrez les hachages de mots de passe dans votre base de données, etc.
Nous allons repartir d'où nous nous sommes arrêtés dans le chapitre précédent et l'enrichir.
## À propos de JWT { #about-jwt }
JWT signifie « JSON Web Tokens ».
C'est une norme pour coder un objet JSON dans une longue chaîne compacte sans espaces. Cela ressemble à ceci :
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Il n'est pas chiffré ; ainsi, n'importe qui peut récupérer les informations à partir de son contenu.
Mais il est signé. Ainsi, quand vous recevez un jeton que vous avez émis, vous pouvez vérifier que vous l'avez bien émis.
De cette façon, vous pouvez créer un jeton avec une expiration d'une semaine, par exemple. Et quand l'utilisateur revient le lendemain avec ce jeton, vous savez qu'il est toujours connecté à votre système.
Après une semaine, le jeton aura expiré et l'utilisateur ne sera pas autorisé et devra se reconnecter pour obtenir un nouveau jeton. Et si l'utilisateur (ou un tiers) essayait de modifier le jeton pour changer l'expiration, vous pourriez le détecter, car les signatures ne correspondraient pas.
Si vous voulez expérimenter avec des jetons JWT et voir comment ils fonctionnent, consultez [https://jwt.io](https://jwt.io/).
## Installer `PyJWT` { #install-pyjwt }
Nous devons installer `PyJWT` pour générer et vérifier les jetons JWT en Python.
Assurez-vous de créer un [environnement virtuel](../../virtual-environments.md), de l'activer, puis d'installer `pyjwt` :
```console
$ pip install pyjwt
---> 100%
```
/// info
Si vous prévoyez d'utiliser des algorithmes de signature numérique comme RSA ou ECDSA, vous devez installer la dépendance de bibliothèque de cryptographie `pyjwt[crypto]`.
Vous pouvez en lire davantage dans la [documentation d'installation de PyJWT](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Hachage de mot de passe { #password-hashing }
« Hachage » signifie convertir un contenu (un mot de passe dans ce cas) en une séquence d'octets (juste une chaîne) qui ressemble à du charabia.
Chaque fois que vous fournissez exactement le même contenu (exactement le même mot de passe), vous obtenez exactement le même charabia.
Mais vous ne pouvez pas convertir le charabia en sens inverse vers le mot de passe.
### Pourquoi utiliser le hachage de mot passe { #why-use-password-hashing }
Si votre base de données est volée, le voleur n'aura pas les mots de passe en clair de vos utilisateurs, seulement les hachages.
Ainsi, le voleur ne pourra pas essayer d'utiliser ce mot de passe dans un autre système (comme beaucoup d'utilisateurs utilisent le même mot de passe partout, ce serait dangereux).
## Installer `pwdlib` { #install-pwdlib }
pwdlib est un excellent package Python pour gérer les hachages de mots de passe.
Il prend en charge de nombreux algorithmes de hachage sécurisés et des utilitaires pour travailler avec eux.
L'algorithme recommandé est « Argon2 ».
Assurez-vous de créer un [environnement virtuel](../../virtual-environments.md), de l'activer, puis d'installer pwdlib avec Argon2 :
/// tip | Astuce
Avec `pwdlib`, vous pouvez même le configurer pour pouvoir lire des mots de passe créés par **Django**, un plug-in de sécurité **Flask** ou bien d'autres.
Ainsi, vous seriez par exemple en mesure de partager les mêmes données d'une application Django dans une base de données avec une application FastAPI. Ou de migrer progressivement une application Django en utilisant la même base de données.
Et vos utilisateurs pourraient se connecter depuis votre application Django ou depuis votre application **FastAPI**, en même temps.
///
## Hacher et vérifier les mots de passe { #hash-and-verify-the-passwords }
Importez les outils nécessaires depuis `pwdlib`.
Créez une instance PasswordHash avec les réglages recommandés ; elle sera utilisée pour hacher et vérifier les mots de passe.
/// tip | Astuce
pwdlib prend également en charge l'algorithme de hachage bcrypt, mais n'inclut pas les algorithmes hérités. Pour travailler avec des hachages obsolètes, il est recommandé d'utiliser la bibliothèque passlib.
Par exemple, vous pourriez l'utiliser pour lire et vérifier des mots de passe générés par un autre système (comme Django), mais hacher tous les nouveaux mots de passe avec un autre algorithme comme Argon2 ou Bcrypt.
Et rester compatible avec tous en même temps.
///
Créez une fonction utilitaire pour hacher un mot de passe fourni par l'utilisateur.
Et une autre pour vérifier si un mot de passe reçu correspond au hachage stocké.
Et une autre pour authentifier et renvoyer un utilisateur.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Lorsque `authenticate_user` est appelée avec un nom d'utilisateur qui n'existe pas dans la base de données, nous exécutons tout de même `verify_password` contre un hachage factice.
Cela garantit que le point de terminaison met approximativement le même temps à répondre que le nom d'utilisateur soit valide ou non, empêchant des **attaques temporelles** qui pourraient être utilisées pour énumérer les noms d'utilisateur existants.
/// note | Remarque
Si vous consultez la nouvelle (fausse) base de données `fake_users_db`, vous verrez à quoi ressemble maintenant le mot de passe haché : `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Gérer les jetons JWT { #handle-jwt-tokens }
Importez les modules installés.
Créez une clé secrète aléatoire qui sera utilisée pour signer les jetons JWT.
Pour générer une clé secrète aléatoire sécurisée, utilisez la commande :
Et copiez la sortie dans la variable `SECRET_KEY` (n'utilisez pas celle de l'exemple).
Créez une variable `ALGORITHM` avec l'algorithme utilisé pour signer le jeton JWT, et définissez-la à `"HS256"`.
Créez une variable pour l'expiration du jeton.
Définissez un modèle Pydantic qui sera utilisé dans le point de terminaison du jeton pour la réponse.
Créez une fonction utilitaire pour générer un nouveau jeton d'accès.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Mettre à jour les dépendances { #update-the-dependencies }
Mettez à jour `get_current_user` pour recevoir le même jeton qu'auparavant, mais cette fois en utilisant des jetons JWT.
Décodez le jeton reçu, vérifiez-le, et renvoyez l'utilisateur courant.
Si le jeton est invalide, renvoyez immédiatement une erreur HTTP.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Mettre à jour le *chemin d'accès* `/token` { #update-the-token-path-operation }
Créez un `timedelta` avec la durée d'expiration du jeton.
Créez un véritable jeton d'accès JWT et renvoyez-le.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Détails techniques au sujet du « subject » JWT `sub` { #technical-details-about-the-jwt-subject-sub }
La spécification JWT indique qu'il existe une clé `sub`, contenant le sujet du jeton.
Son utilisation est facultative, mais c'est là que vous placeriez l'identifiant de l'utilisateur ; nous l'utilisons donc ici.
Les JWT peuvent être utilisés pour d'autres choses que l'identification d'un utilisateur et l'autorisation d'effectuer des opérations directement sur votre API.
Par exemple, vous pourriez identifier une « voiture » ou un « article de blog ».
Vous pourriez ensuite ajouter des permissions sur cette entité, comme « conduire » (pour la voiture) ou « modifier » (pour le blog).
Vous pourriez alors donner ce jeton JWT à un utilisateur (ou un bot), et il pourrait l'utiliser pour effectuer ces actions (conduire la voiture, ou modifier l'article de blog) sans même avoir besoin d'avoir un compte, uniquement avec le jeton JWT que votre API a généré pour cela.
En utilisant ces idées, les JWT peuvent servir à des scénarios bien plus sophistiqués.
Dans ces cas, plusieurs de ces entités peuvent avoir le même identifiant, disons `foo` (un utilisateur `foo`, une voiture `foo`, et un article de blog `foo`).
Donc, pour éviter les collisions d'identifiants, lors de la création du jeton JWT pour l'utilisateur, vous pouvez préfixer la valeur de la clé `sub`, par exemple avec `username:`. Ainsi, dans cet exemple, la valeur de `sub` aurait pu être : `username:johndoe`.
L'important à garder à l'esprit est que la clé `sub` doit contenir un identifiant unique dans toute l'application, et ce doit être une chaîne de caractères.
## Vérifier { #check-it }
Lancez le serveur et allez à la documentation : [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Vous verrez l'interface utilisateur suivante :
Autorisez l'application de la même manière qu'auparavant.
En utilisant les identifiants :
Nom d'utilisateur : `johndoe`
Mot de passe : `secret`
/// check | Vérifications
Remarquez qu'à aucun endroit du code le mot de passe en clair « secret » n'apparaît, nous n'avons que la version hachée.
///
Appelez le point de terminaison `/users/me/`, vous obtiendrez la réponse suivante :
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Si vous ouvrez les outils de développement, vous pouvez voir que les données envoyées n'incluent que le jeton ; le mot de passe n'est envoyé que dans la première requête pour authentifier l'utilisateur et obtenir ce jeton d'accès, mais plus ensuite :
/// note | Remarque
Remarquez l'en-tête `Authorization`, avec une valeur qui commence par `Bearer `.
///
## Utilisation avancée avec `scopes` { #advanced-usage-with-scopes }
OAuth2 comporte la notion de « scopes ».
Vous pouvez les utiliser pour ajouter un ensemble spécifique d'autorisations à un jeton JWT.
Vous pouvez ensuite donner ce jeton directement à un utilisateur ou à un tiers, pour interagir avec votre API avec un ensemble de restrictions.
Vous pouvez apprendre à les utiliser et comment ils sont intégrés à **FastAPI** plus tard dans le **Guide de l'utilisateur avancé**.
## Récapitulatif { #recap }
Avec ce que vous avez vu jusqu'à présent, vous pouvez configurer une application **FastAPI** sécurisée en utilisant des standards comme OAuth2 et JWT.
Dans presque n'importe quel framework, la gestion de la sécurité devient assez rapidement un sujet plutôt complexe.
De nombreux packages qui la simplifient beaucoup doivent faire de nombreux compromis avec le modèle de données, la base de données et les fonctionnalités disponibles. Et certains de ces packages qui simplifient trop les choses comportent en fait des failles de sécurité sous-jacentes.
---
**FastAPI** ne fait aucun compromis avec une base de données, un modèle de données ni un outil.
Il vous donne toute la flexibilité pour choisir ceux qui conviennent le mieux à votre projet.
Et vous pouvez utiliser directement de nombreux packages bien maintenus et largement utilisés comme `pwdlib` et `PyJWT`, car **FastAPI** n'exige aucun mécanisme complexe pour intégrer des packages externes.
Mais il vous fournit les outils pour simplifier le processus autant que possible sans compromettre la flexibilité, la robustesse ou la sécurité.
Et vous pouvez utiliser et implémenter des protocoles sécurisés et standard, comme OAuth2, de manière relativement simple.
Vous pouvez en apprendre davantage dans le **Guide de l'utilisateur avancé** sur la façon d'utiliser les « scopes » OAuth2, pour un système d'autorisations plus fin, en suivant ces mêmes standards. OAuth2 avec scopes est le mécanisme utilisé par de nombreux grands fournisseurs d'authentification, comme Facebook, Google, GitHub, Microsoft, X (Twitter), etc., pour autoriser des applications tierces à interagir avec leurs API au nom de leurs utilisateurs.
================================================
FILE: docs/fr/docs/tutorial/security/simple-oauth2.md
================================================
# OAuth2 simple avec Password et Bearer { #simple-oauth2-with-password-and-bearer }
Construisons maintenant à partir du chapitre précédent et ajoutons les éléments manquants pour avoir un flux de sécurité complet.
## Obtenir `username` et `password` { #get-the-username-and-password }
Nous allons utiliser les utilités de sécurité de **FastAPI** pour obtenir `username` et `password`.
OAuth2 spécifie que lorsqu'on utilise le « password flow » (ce que nous utilisons), le client/utilisateur doit envoyer des champs `username` et `password` en tant que données de formulaire.
Et la spécification indique que les champs doivent porter exactement ces noms. Ainsi, `user-name` ou `email` ne fonctionneraient pas.
Mais ne vous inquiétez pas, vous pouvez l'afficher comme vous le souhaitez à vos utilisateurs finaux dans le frontend.
Et vos modèles de base de données peuvent utiliser les noms que vous voulez.
Mais pour le chemin d'accès de connexion, nous devons utiliser ces noms pour être compatibles avec la spécification (et pouvoir, par exemple, utiliser le système de documentation API intégré).
La spécification précise également que `username` et `password` doivent être envoyés en données de formulaire (donc pas de JSON ici).
### `scope` { #scope }
La spécification indique aussi que le client peut envoyer un autre champ de formulaire « scope ».
Le nom du champ de formulaire est `scope` (au singulier), mais il s'agit en fait d'une longue chaîne contenant des « scopes » séparés par des espaces.
Chaque « scope » n'est qu'une chaîne (sans espaces).
Ils sont normalement utilisés pour déclarer des permissions de sécurité spécifiques, par exemple :
* `users:read` ou `users:write` sont des exemples courants.
* `instagram_basic` est utilisé par Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` est utilisé par Google.
/// info
En OAuth2, un « scope » est simplement une chaîne qui déclare une permission spécifique requise.
Peu importe s'il contient d'autres caractères comme `:` ou si c'est une URL.
Ces détails dépendent de l'implémentation.
Pour OAuth2, ce ne sont que des chaînes.
///
## Écrire le code pour obtenir `username` et `password` { #code-to-get-the-username-and-password }
Utilisons maintenant les utilités fournies par **FastAPI** pour gérer cela.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Tout d'abord, importez `OAuth2PasswordRequestForm`, et utilisez-la en tant que dépendance avec `Depends` dans le chemin d'accès pour `/token` :
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` est une dépendance de classe qui déclare un corps de formulaire avec :
* Le `username`.
* Le `password`.
* Un champ optionnel `scope` sous forme d'une grande chaîne, composée de chaînes séparées par des espaces.
* Un `grant_type` optionnel.
/// tip | Astuce
La spécification OAuth2 exige en réalité un champ `grant_type` avec la valeur fixe `password`, mais `OAuth2PasswordRequestForm` ne l'impose pas.
Si vous avez besoin de l'imposer, utilisez `OAuth2PasswordRequestFormStrict` au lieu de `OAuth2PasswordRequestForm`.
///
* Un `client_id` optionnel (nous n'en avons pas besoin pour notre exemple).
* Un `client_secret` optionnel (nous n'en avons pas besoin pour notre exemple).
/// info
La classe `OAuth2PasswordRequestForm` n'est pas une classe spéciale pour **FastAPI** comme l'est `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` indique à **FastAPI** qu'il s'agit d'un schéma de sécurité. Il est donc ajouté de cette façon à OpenAPI.
Mais `OAuth2PasswordRequestForm` est simplement une dépendance de classe que vous auriez pu écrire vous‑même, ou vous auriez pu déclarer des paramètres `Form` directement.
Mais comme c'est un cas d'usage courant, elle est fournie directement par **FastAPI**, simplement pour vous faciliter la vie.
///
### Utiliser les données du formulaire { #use-the-form-data }
/// tip | Astuce
L'instance de la classe de dépendance `OAuth2PasswordRequestForm` n'aura pas d'attribut `scope` contenant la longue chaîne séparée par des espaces ; elle aura plutôt un attribut `scopes` avec la liste réelle des chaînes pour chaque scope envoyé.
Nous n'utilisons pas `scopes` dans cet exemple, mais la fonctionnalité est disponible si vous en avez besoin.
///
Récupérez maintenant les données utilisateur depuis la (fausse) base de données, en utilisant le `username` du champ de formulaire.
S'il n'existe pas d'utilisateur, nous renvoyons une erreur indiquant « Incorrect username or password ».
Pour l'erreur, nous utilisons l'exception `HTTPException` :
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Vérifier le mot de passe { #check-the-password }
À ce stade, nous avons les données utilisateur depuis notre base, mais nous n'avons pas encore vérifié le mot de passe.
Mettons d'abord ces données dans le modèle Pydantic `UserInDB`.
Vous ne devez jamais enregistrer des mots de passe en clair ; nous allons donc utiliser le système (factice) de hachage de mot de passe.
Si les mots de passe ne correspondent pas, nous renvoyons la même erreur.
#### Hachage de mot de passe { #password-hashing }
Le « hachage » signifie : convertir un contenu (un mot de passe, dans ce cas) en une séquence d'octets (juste une chaîne) qui ressemble à du charabia.
Chaque fois que vous fournissez exactement le même contenu (exactement le même mot de passe), vous obtenez exactement le même charabia.
Mais vous ne pouvez pas convertir ce charabia pour retrouver le mot de passe.
##### Pourquoi utiliser le hachage de mot de passe { #why-use-password-hashing }
Si votre base de données est volée, le voleur n'aura pas les mots de passe en clair de vos utilisateurs, seulement les hachages.
Ainsi, il ne pourra pas essayer d'utiliser ces mêmes mots de passe dans un autre système (comme beaucoup d'utilisateurs utilisent le même mot de passe partout, ce serait dangereux).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### À propos de `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` signifie :
Passez les clés et valeurs de `user_dict` directement comme arguments clé‑valeur, équivalent à :
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info
Pour une explication plus complète de `**user_dict`, consultez [la documentation pour **Modèles supplémentaires**](../extra-models.md#about-user-in-dict).
///
## Renvoyer le jeton { #return-the-token }
La réponse de l'endpoint `token` doit être un objet JSON.
Il doit contenir un `token_type`. Dans notre cas, comme nous utilisons des jetons « Bearer », le type de jeton doit être « bearer ».
Et il doit contenir un `access_token`, avec une chaîne contenant notre jeton d'accès.
Pour cet exemple simple, nous allons faire quelque chose de complètement non sécurisé et renvoyer le même `username` comme jeton.
/// tip | Astuce
Dans le prochain chapitre, vous verrez une véritable implémentation sécurisée, avec du hachage de mot de passe et des jetons JWT.
Mais pour l'instant, concentrons‑nous sur les détails spécifiques dont nous avons besoin.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Astuce
D'après la spécification, vous devez renvoyer un JSON avec un `access_token` et un `token_type`, comme dans cet exemple.
C'est quelque chose que vous devez faire vous‑même dans votre code, et vous devez vous assurer d'utiliser ces clés JSON.
C'est presque la seule chose que vous devez vous rappeler de faire correctement vous‑même pour être conforme aux spécifications.
Pour le reste, **FastAPI** s'en charge pour vous.
///
## Mettre à jour les dépendances { #update-the-dependencies }
Nous allons maintenant mettre à jour nos dépendances.
Nous voulons obtenir `current_user` uniquement si cet utilisateur est actif.
Nous créons donc une dépendance supplémentaire `get_current_active_user` qui utilise à son tour `get_current_user` comme dépendance.
Ces deux dépendances renverront simplement une erreur HTTP si l'utilisateur n'existe pas, ou s'il est inactif.
Ainsi, dans notre endpoint, nous n'obtiendrons un utilisateur que si l'utilisateur existe, a été correctement authentifié et est actif :
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info
L'en‑tête supplémentaire `WWW-Authenticate` avec la valeur `Bearer` que nous renvoyons ici fait également partie de la spécification.
Il est prévu qu'un code d'état HTTP (d'erreur) 401 « UNAUTHORIZED » renvoie également un en‑tête `WWW-Authenticate`.
Dans le cas des jetons bearer (notre cas), la valeur de cet en‑tête doit être `Bearer`.
Vous pouvez en réalité omettre cet en‑tête supplémentaire et cela fonctionnerait quand même.
Mais il est fourni ici pour être conforme aux spécifications.
De plus, il peut exister des outils qui l'attendent et l'utilisent (maintenant ou à l'avenir) et cela pourrait vous être utile, à vous ou à vos utilisateurs, maintenant ou à l'avenir.
C'est l'avantage des standards ...
///
## Voir en action { #see-it-in-action }
Ouvrez la documentation interactive : [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### S'authentifier { #authenticate }
Cliquez sur le bouton « Authorize ».
Utilisez les identifiants :
Utilisateur : `johndoe`
Mot de passe : `secret`
Après vous être authentifié dans le système, vous verrez ceci :
### Obtenir vos propres données utilisateur { #get-your-own-user-data }
Utilisez maintenant l'opération `GET` avec le chemin `/users/me`.
Vous obtiendrez les données de votre utilisateur, par exemple :
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Si vous cliquez sur l'icône de cadenas et vous vous déconnectez, puis réessayez la même opération, vous obtiendrez une erreur HTTP 401 :
```JSON
{
"detail": "Not authenticated"
}
```
### Utilisateur inactif { #inactive-user }
Essayez maintenant avec un utilisateur inactif, authentifiez‑vous avec :
Utilisateur : `alice`
Mot de passe : `secret2`
Et essayez d'utiliser l'opération `GET` avec le chemin `/users/me`.
Vous obtiendrez une erreur « Inactive user », par exemple :
```JSON
{
"detail": "Inactive user"
}
```
## Récapitulatif { #recap }
Vous avez maintenant les outils pour implémenter un système de sécurité complet basé sur `username` et `password` pour votre API.
En utilisant ces outils, vous pouvez rendre le système de sécurité compatible avec n'importe quelle base de données et avec n'importe quel modèle d'utilisateur ou de données.
Le seul détail manquant est qu'il n'est pas encore réellement « sécurisé ».
Dans le prochain chapitre, vous verrez comment utiliser une bibliothèque de hachage de mot de passe sécurisée et des jetons JWT.
================================================
FILE: docs/fr/docs/tutorial/server-sent-events.md
================================================
# Événements envoyés par le serveur (SSE) { #server-sent-events-sse }
Vous pouvez diffuser des données vers le client en utilisant les **Server-Sent Events** (SSE).
C'est similaire à [Diffuser des JSON Lines](stream-json-lines.md), mais cela utilise le format `text/event-stream`, pris en charge nativement par les navigateurs via l’API [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info | Info
Ajouté dans FastAPI 0.135.0.
///
## Que sont les Server-Sent Events ? { #what-are-server-sent-events }
SSE est un standard pour diffuser des données du serveur au client via HTTP.
Chaque événement est un petit bloc de texte avec des « champs » comme `data`, `event`, `id` et `retry`, séparés par des lignes vides.
Cela ressemble à ceci :
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
Les SSE sont couramment utilisés pour le streaming de chat IA, les notifications en direct, les journaux et l’observabilité, et d’autres cas où le serveur envoie des mises à jour au client.
/// tip | Astuce
Si vous souhaitez diffuser des données binaires, par exemple de la vidéo ou de l’audio, consultez le guide avancé : [Diffuser des données](../advanced/stream-data.md).
///
## Diffuser des SSE avec FastAPI { #stream-sse-with-fastapi }
Pour diffuser des SSE avec FastAPI, utilisez `yield` dans votre *fonction de chemin d'accès* et définissez `response_class=EventSourceResponse`.
Importez `EventSourceResponse` depuis `fastapi.sse` :
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Chaque élément produit avec `yield` est encodé en JSON et envoyé dans le champ `data:` d’un événement SSE.
Si vous déclarez le type de retour comme `AsyncIterable[Item]`, FastAPI l’utilisera pour **valider**, **documenter** et **sérialiser** les données avec Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Astuce
Comme Pydantic le sérialisera du côté **Rust**, vous obtiendrez une **performance** bien supérieure que si vous ne déclarez pas de type de retour.
///
### Fonctions de chemin d'accès non async { #non-async-path-operation-functions }
Vous pouvez aussi utiliser des fonctions `def` normales (sans `async`), et utiliser `yield` de la même façon.
FastAPI s’assure qu’elles s’exécutent correctement pour ne pas bloquer la boucle d’événements.
Dans ce cas la fonction n’est pas async, le type de retour approprié serait `Iterable[Item]` :
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Sans type de retour { #no-return-type }
Vous pouvez aussi omettre le type de retour. FastAPI utilisera le [`jsonable_encoder`](./encoder.md) pour convertir les données et les envoyer.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Si vous devez définir des champs SSE comme `event`, `id`, `retry` ou `comment`, vous pouvez produire des objets `ServerSentEvent` au lieu de données brutes.
Importez `ServerSentEvent` depuis `fastapi.sse` :
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
Le champ `data` est toujours encodé en JSON. Vous pouvez passer toute valeur sérialisable en JSON, y compris des modèles Pydantic.
## Données brutes { #raw-data }
Si vous devez envoyer des données **sans** encodage JSON, utilisez `raw_data` au lieu de `data`.
C’est utile pour envoyer du texte préformaté, des lignes de log, ou des valeurs « sentinelle » spéciales comme `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Remarque
`data` et `raw_data` s’excluent mutuellement. Vous ne pouvez en définir qu’un seul par `ServerSentEvent`.
///
## Reprendre avec `Last-Event-ID` { #resuming-with-last-event-id }
Quand un navigateur se reconnecte après une coupure, il envoie le dernier `id` reçu dans l’en-tête `Last-Event-ID`.
Vous pouvez le lire comme paramètre d’en-tête et l’utiliser pour reprendre le flux là où le client s’était arrêté :
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE avec POST { #sse-with-post }
SSE fonctionne avec **n’importe quelle méthode HTTP**, pas seulement `GET`.
C’est utile pour des protocoles comme [MCP](https://modelcontextprotocol.io) qui diffusent des SSE via `POST` :
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Détails techniques { #technical-details }
FastAPI met en œuvre certaines bonnes pratiques SSE prêtes à l’emploi.
- Envoyer un commentaire **« keep alive » `ping`** toutes les 15 secondes quand aucun message n’a été émis, pour éviter que certains proxys ne ferment la connexion, comme suggéré dans la [Spécification HTML : Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
- Définir l’en-tête `Cache-Control: no-cache` pour **empêcher la mise en cache** du flux.
- Définir un en-tête spécial `X-Accel-Buffering: no` pour **empêcher le buffering** dans certains proxys comme Nginx.
Vous n’avez rien à faire, cela fonctionne prêt à l’emploi. 🤓
================================================
FILE: docs/fr/docs/tutorial/sql-databases.md
================================================
# Bases de données SQL (relationnelles) { #sql-relational-databases }
**FastAPI** ne vous oblige pas à utiliser une base de données SQL (relationnelle). Mais vous pouvez utiliser **n'importe quelle base de données** que vous voulez.
Ici, nous allons voir un exemple utilisant [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** est construit au-dessus de [SQLAlchemy](https://www.sqlalchemy.org/) et de Pydantic. Il a été créé par le même auteur que **FastAPI** pour être l'accord parfait pour les applications FastAPI qui ont besoin d'utiliser des **bases de données SQL**.
/// tip | Astuce
Vous pouvez utiliser toute autre bibliothèque SQL ou NoSQL que vous voulez (dans certains cas appelées « ORMs »), FastAPI ne vous impose rien. 😎
///
Comme SQLModel est basé sur SQLAlchemy, vous pouvez facilement utiliser **toute base prise en charge** par SQLAlchemy (ce qui les rend également prises en charge par SQLModel), comme :
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, etc.
Dans cet exemple, nous utiliserons **SQLite**, car il utilise un seul fichier et Python a un support intégré. Ainsi, vous pouvez copier cet exemple et l'exécuter tel quel.
Plus tard, pour votre application de production, vous voudrez peut-être utiliser un serveur de base de données comme **PostgreSQL**.
/// tip | Astuce
Il existe un générateur de projet officiel avec **FastAPI** et **PostgreSQL**, incluant un frontend et plus d'outils : [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Il s'agit d'un tutoriel très simple et court ; si vous souhaitez apprendre sur les bases de données en général, sur SQL, ou des fonctionnalités plus avancées, allez voir la [documentation SQLModel](https://sqlmodel.tiangolo.com/).
## Installer `SQLModel` { #install-sqlmodel }
D'abord, assurez-vous de créer votre [environnement virtuel](../virtual-environments.md), de l'activer, puis d'installer `sqlmodel` :
```console
$ pip install sqlmodel
---> 100%
```
## Créer l'application avec un modèle unique { #create-the-app-with-a-single-model }
Nous allons d'abord créer la première version la plus simple de l'application avec un seul modèle **SQLModel**.
Ensuite, nous l'améliorerons en augmentant la sécurité et la polyvalence avec **plusieurs modèles** ci-dessous. 🤓
### Créer les modèles { #create-models }
Importez `SQLModel` et créez un modèle de base de données :
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
La classe `Hero` est très similaire à un modèle Pydantic (en fait, en dessous, c'est réellement un modèle Pydantic).
Il y a quelques différences :
* `table=True` indique à SQLModel qu'il s'agit d'un *modèle de table*, il doit représenter une **table** dans la base SQL, ce n'est pas seulement un *modèle de données* (comme le serait n'importe quelle autre classe Pydantic classique).
* `Field(primary_key=True)` indique à SQLModel que `id` est la **clé primaire** dans la base SQL (vous pouvez en savoir plus sur les clés primaires SQL dans la documentation SQLModel).
Remarque : nous utilisons `int | None` pour le champ clé primaire afin qu'en Python nous puissions *créer un objet sans `id`* (`id=None`), en supposant que la base *le génère à l'enregistrement*. SQLModel comprend que la base fournira l'`id` et *définit la colonne comme un `INTEGER` non nul* dans le schéma de base. Voir la [documentation SQLModel sur les clés primaires](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) pour plus de détails.
* `Field(index=True)` indique à SQLModel qu'il doit créer un **index SQL** pour cette colonne, ce qui permettra des recherches plus rapides dans la base lors de la lecture de données filtrées par cette colonne.
SQLModel saura que quelque chose déclaré comme `str` sera une colonne SQL de type `TEXT` (ou `VARCHAR`, selon la base).
### Créer un engine { #create-an-engine }
Un `engine` SQLModel (en dessous c'est en fait un `engine` SQLAlchemy) est ce qui **détient les connexions** à la base de données.
Vous devez avoir **un seul objet `engine`** pour tout votre code afin de se connecter à la même base.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
L'utilisation de `check_same_thread=False` permet à FastAPI d'utiliser la même base SQLite dans différents threads. C'est nécessaire car **une seule requête** peut utiliser **plus d'un thread** (par exemple dans des dépendances).
Ne vous inquiétez pas, avec la structure du code, nous nous assurerons d'utiliser **une seule *session* SQLModel par requête** plus loin, c'est en fait ce que `check_same_thread` essaie d'assurer.
### Créer les tables { #create-the-tables }
Nous ajoutons ensuite une fonction qui utilise `SQLModel.metadata.create_all(engine)` pour **créer les tables** pour tous les *modèles de table*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Créer une dépendance de session { #create-a-session-dependency }
Une **`Session`** est ce qui stocke les **objets en mémoire** et suit les modifications nécessaires des données, puis **utilise l'`engine`** pour communiquer avec la base.
Nous allons créer une **dépendance** FastAPI avec `yield` qui fournira une nouvelle `Session` pour chaque requête. C'est ce qui garantit que nous utilisons une seule session par requête. 🤓
Puis nous créons une dépendance `Annotated` `SessionDep` pour simplifier le reste du code qui utilisera cette dépendance.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Créer les tables de base au démarrage { #create-database-tables-on-startup }
Nous allons créer les tables de base de données au démarrage de l'application.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Ici, nous créons les tables lors d'un événement de démarrage de l'application.
En production, vous utiliseriez probablement un script de migration qui s'exécute avant de démarrer votre application. 🤓
/// tip | Astuce
SQLModel aura des utilitaires de migration enveloppant Alembic, mais pour l'instant, vous pouvez utiliser [Alembic](https://alembic.sqlalchemy.org/en/latest/) directement.
///
### Créer un héros { #create-a-hero }
Comme chaque modèle SQLModel est aussi un modèle Pydantic, vous pouvez l'utiliser dans les mêmes **annotations de type** que vous utiliseriez pour des modèles Pydantic.
Par exemple, si vous déclarez un paramètre de type `Hero`, il sera lu depuis le **corps JSON**.
De la même manière, vous pouvez le déclarer comme **type de retour** de la fonction, et alors la forme des données apparaîtra dans l'UI automatique de documentation de l'API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Ici, nous utilisons la dépendance `SessionDep` (une `Session`) pour ajouter le nouveau `Hero` à l'instance de `Session`, valider les changements dans la base, rafraîchir les données dans `hero`, puis le retourner.
### Lire les héros { #read-heroes }
Nous pouvons **lire** des `Hero` depuis la base en utilisant un `select()`. Nous pouvons inclure une `limit` et un `offset` pour paginer les résultats.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Lire un héros { #read-one-hero }
Nous pouvons **lire** un seul `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Supprimer un héros { #delete-a-hero }
Nous pouvons aussi **supprimer** un `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Exécuter l'application { #run-the-app }
Vous pouvez exécuter l'application :
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Ensuite, allez sur l'UI `/docs`, vous verrez que **FastAPI** utilise ces **modèles** pour **documenter** l'API, et les utilisera aussi pour **sérialiser** et **valider** les données.
## Mettre à jour l'application avec plusieurs modèles { #update-the-app-with-multiple-models }
Maintenant, **refactorisons** un peu cette application pour augmenter la **sécurité** et la **polyvalence**.
Si vous vérifiez l'application précédente, dans l'UI vous pouvez voir que, jusqu'à présent, elle laisse le client décider de l'`id` du `Hero` à créer. 😱
Nous ne devrions pas laisser cela se produire, ils pourraient écraser un `id` que nous avons déjà attribué dans la base. Décider de l'`id` doit être fait par le **backend** ou la **base**, **pas par le client**.
De plus, nous créons un `secret_name` pour le héros, mais jusqu'ici, nous le renvoyons partout, ce n'est pas très « secret » ... 😅
Nous allons corriger ces choses en ajoutant quelques **modèles supplémentaires**. C'est là que SQLModel brille. ✨
### Créer plusieurs modèles { #create-multiple-models }
Dans **SQLModel**, toute classe de modèle qui a `table=True` est un **modèle de table**.
Et toute classe de modèle qui n'a pas `table=True` est un **modèle de données**, ceux-ci sont en réalité juste des modèles Pydantic (avec deux petites fonctionnalités en plus). 🤓
Avec SQLModel, nous pouvons utiliser **l'héritage** pour **éviter de dupliquer** tous les champs dans tous les cas.
#### `HeroBase` - la classe de base { #herobase-the-base-class }
Commençons avec un modèle `HeroBase` qui a tous les **champs partagés** par tous les modèles :
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - le *modèle de table* { #hero-the-table-model }
Créons ensuite `Hero`, le *modèle de table* proprement dit, avec les **champs supplémentaires** qui ne sont pas toujours dans les autres modèles :
* `id`
* `secret_name`
Comme `Hero` hérite de `HeroBase`, il **a aussi** les **champs** déclarés dans `HeroBase`, donc tous les champs de `Hero` sont :
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - le *modèle de données* public { #heropublic-the-public-data-model }
Ensuite, nous créons un modèle `HeroPublic`, c'est celui qui sera **retourné** aux clients de l'API.
Il a les mêmes champs que `HeroBase`, il n'inclura donc pas `secret_name`.
Enfin, l'identité de nos héros est protégée ! 🥷
Il redéclare aussi `id: int`. Ce faisant, nous faisons un **contrat** avec les clients de l'API, afin qu'ils puissent toujours s'attendre à ce que `id` soit présent et soit un `int` (il ne sera jamais `None`).
/// tip | Astuce
Avoir le modèle de retour qui garantit qu'une valeur est toujours disponible et toujours `int` (pas `None`) est très utile pour les clients de l'API, ils peuvent écrire un code beaucoup plus simple avec cette certitude.
De plus, les **clients générés automatiquement** auront des interfaces plus simples, afin que les développeurs qui communiquent avec votre API puissent travailler bien plus facilement avec votre API. 😎
///
Tous les champs de `HeroPublic` sont les mêmes que dans `HeroBase`, avec `id` déclaré comme `int` (pas `None`) :
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - le *modèle de données* pour créer un héros { #herocreate-the-data-model-to-create-a-hero }
Nous créons maintenant un modèle `HeroCreate`, c'est celui qui **validera** les données provenant des clients.
Il a les mêmes champs que `HeroBase`, et il a aussi `secret_name`.
Maintenant, lorsque les clients **créent un nouveau héros**, ils enverront `secret_name`, il sera stocké dans la base, mais ces noms secrets ne seront pas renvoyés dans l'API aux clients.
/// tip | Astuce
C'est ainsi que vous géreriez les **mots de passe**. Les recevoir, mais ne pas les renvoyer dans l'API.
Vous **hacherez** aussi les valeurs des mots de passe avant de les stocker, **ne les stockez jamais en texte en clair**.
///
Les champs de `HeroCreate` sont :
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - le *modèle de données* pour mettre à jour un héros { #heroupdate-the-data-model-to-update-a-hero }
Nous n'avions pas de moyen de **mettre à jour un héros** dans la version précédente de l'application, mais maintenant avec **plusieurs modèles**, nous pouvons le faire. 🎉
Le *modèle de données* `HeroUpdate` est un peu spécial, il a **tous les mêmes champs** qui seraient nécessaires pour créer un nouveau héros, mais tous les champs sont **optionnels** (ils ont tous une valeur par défaut). Ainsi, lorsque vous mettez à jour un héros, vous pouvez n'envoyer que les champs que vous souhaitez mettre à jour.
Comme tous les **champs changent réellement** (le type inclut désormais `None` et ils ont maintenant une valeur par défaut de `None`), nous devons les **redéclarer**.
Nous n'avons pas vraiment besoin d'hériter de `HeroBase` puisque nous redéclarons tous les champs. Je le laisse hériter juste pour la cohérence, mais ce n'est pas nécessaire. C'est plutôt une question de goût personnel. 🤷
Les champs de `HeroUpdate` sont :
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Créer avec `HeroCreate` et retourner un `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Maintenant que nous avons **plusieurs modèles**, nous pouvons mettre à jour les parties de l'application qui les utilisent.
Nous recevons dans la requête un *modèle de données* `HeroCreate`, et à partir de celui-ci, nous créons un *modèle de table* `Hero`.
Ce nouveau *modèle de table* `Hero` aura les champs envoyés par le client, et aura aussi un `id` généré par la base.
Nous retournons ensuite le même *modèle de table* `Hero` tel quel depuis la fonction. Mais comme nous déclarons le `response_model` avec le *modèle de données* `HeroPublic`, **FastAPI** utilisera `HeroPublic` pour valider et sérialiser les données.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Astuce
Nous utilisons maintenant `response_model=HeroPublic` au lieu de l'**annotation de type de retour** `-> HeroPublic` car la valeur que nous renvoyons n'est en réalité *pas* un `HeroPublic`.
Si nous avions déclaré `-> HeroPublic`, votre éditeur et votre linter se plaindraient (à juste titre) que vous retournez un `Hero` au lieu d'un `HeroPublic`.
En le déclarant dans `response_model`, nous disons à **FastAPI** de faire son travail, sans interférer avec les annotations de type et l'aide de votre éditeur et d'autres outils.
///
### Lire des héros avec `HeroPublic` { #read-heroes-with-heropublic }
Nous pouvons faire la même chose qu'avant pour **lire** des `Hero`, à nouveau, nous utilisons `response_model=list[HeroPublic]` pour garantir que les données sont correctement validées et sérialisées.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Lire un héros avec `HeroPublic` { #read-one-hero-with-heropublic }
Nous pouvons **lire** un héros unique :
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Mettre à jour un héros avec `HeroUpdate` { #update-a-hero-with-heroupdate }
Nous pouvons **mettre à jour un héros**. Pour cela, nous utilisons une opération HTTP `PATCH`.
Et dans le code, nous obtenons un `dict` avec toutes les données envoyées par le client, **uniquement les données envoyées par le client**, en excluant toute valeur qui serait là simplement parce que c'est la valeur par défaut. Pour ce faire, nous utilisons `exclude_unset=True`. C'est l'astuce principale. 🪄
Nous utilisons ensuite `hero_db.sqlmodel_update(hero_data)` pour mettre à jour `hero_db` avec les données de `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Supprimer un héros (bis) { #delete-a-hero-again }
**Supprimer** un héros reste pratiquement identique.
Nous n'allons pas céder à l'envie de tout refactoriser pour celui-ci. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Exécuter l'application à nouveau { #run-the-app-again }
Vous pouvez exécuter l'application à nouveau :
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Si vous allez sur l'UI `/docs` de l'API, vous verrez qu'elle est maintenant à jour, et qu'elle n'attendra plus de recevoir l'`id` du client lors de la création d'un héros, etc.
## Récapitulatif { #recap }
Vous pouvez utiliser [**SQLModel**](https://sqlmodel.tiangolo.com/) pour interagir avec une base SQL et simplifier le code avec des *modèles de données* et des *modèles de table*.
Vous pouvez en apprendre beaucoup plus dans la documentation **SQLModel**, il y a un mini [tutoriel plus long sur l'utilisation de SQLModel avec **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/fr/docs/tutorial/static-files.md
================================================
# Fichiers statiques { #static-files }
Vous pouvez servir des fichiers statiques automatiquement à partir d'un répertoire en utilisant `StaticFiles`.
## Utiliser `StaticFiles` { #use-staticfiles }
- Importer `StaticFiles`.
- « Mount » une instance `StaticFiles()` sur un chemin spécifique.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Détails techniques
Vous pouvez également utiliser `from starlette.staticfiles import StaticFiles`.
**FastAPI** fournit le même `starlette.staticfiles` sous le nom `fastapi.staticfiles` uniquement pour votre commodité, en tant que développeur. Mais cela provient en réalité directement de Starlette.
///
### Qu'est-ce que « Mounting » { #what-is-mounting }
« Mounting » signifie ajouter une application complète « indépendante » sur un chemin spécifique, qui se chargera ensuite de gérer tous les sous-chemins.
Cela diffère de l'utilisation d'un `APIRouter`, car une application montée est complètement indépendante. L'OpenAPI et les documents de votre application principale n'incluront rien provenant de l'application montée, etc.
Vous pouvez en lire davantage à ce sujet dans le [Guide utilisateur avancé](../advanced/index.md).
## Détails { #details }
Le premier `"/static"` fait référence au sous-chemin sur lequel cette « sous-application » sera « montée ». Ainsi, tout chemin qui commence par `"/static"` sera géré par elle.
Le `directory="static"` fait référence au nom du répertoire qui contient vos fichiers statiques.
Le `name="static"` lui donne un nom utilisable en interne par **FastAPI**.
Tous ces paramètres peuvent être différents de « `static` », adaptez-les aux besoins et aux détails spécifiques de votre propre application.
## Plus d'informations { #more-info }
Pour plus de détails et d'options, consultez la [documentation de Starlette sur les fichiers statiques](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/fr/docs/tutorial/stream-json-lines.md
================================================
# Diffuser des JSON Lines { #stream-json-lines }
Vous pouvez avoir une séquence de données que vous souhaitez envoyer en « flux » ; vous pouvez le faire avec « JSON Lines ».
/// info
Ajouté dans FastAPI 0.134.0.
///
## Qu'est-ce qu'un flux ? { #what-is-a-stream }
La « diffusion en continu » de données signifie que votre application commence à envoyer des éléments de données au client sans attendre que l'ensemble de la séquence soit prêt.
Ainsi, elle enverra le premier élément, le client le recevra et commencera à le traiter, et vous pourriez être encore en train de produire l'élément suivant.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Cela peut même être un flux infini, où vous continuez à envoyer des données.
## JSON Lines { #json-lines }
Dans ces cas, il est courant d'envoyer des « JSON Lines », qui est un format où vous envoyez un objet JSON par ligne.
Une réponse aurait un type de contenu `application/jsonl` (au lieu de `application/json`) et le corps ressemblerait à ceci :
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
C'est très similaire à un tableau JSON (équivalent d'une liste Python), mais au lieu d'être entouré de `[]` et d'avoir des `,` entre les éléments, il y a un objet JSON par ligne, ils sont séparés par un caractère de saut de ligne.
/// info
Le point important est que votre application pourra produire chaque ligne à son tour, tandis que le client consomme les lignes précédentes.
///
/// note | Détails techniques
Comme chaque objet JSON sera séparé par un saut de ligne, ils ne peuvent pas contenir de caractères de saut de ligne littéraux dans leur contenu, mais ils peuvent contenir des sauts de ligne échappés (`\n`), ce qui fait partie du standard JSON.
Mais normalement, vous n'avez pas à vous en soucier, c'est fait automatiquement, continuez la lecture. 🤓
///
## Cas d'utilisation { #use-cases }
Vous pouvez utiliser cela pour diffuser des données depuis un service **AI LLM**, depuis des **journaux** ou de la **télémétrie**, ou depuis d'autres types de données pouvant être structurées en éléments **JSON**.
/// tip | Astuce
Si vous voulez diffuser des données binaires, par exemple de la vidéo ou de l'audio, consultez le guide avancé : [Diffuser des données](../advanced/stream-data.md).
///
## Diffuser des JSON Lines avec FastAPI { #stream-json-lines-with-fastapi }
Pour diffuser des JSON Lines avec FastAPI, au lieu d'utiliser `return` dans votre fonction de chemin d'accès, utilisez `yield` pour produire chaque élément à tour de rôle.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Si chaque élément JSON que vous voulez renvoyer est de type `Item` (un modèle Pydantic) et que c'est une fonction async, vous pouvez déclarer le type de retour comme `AsyncIterable[Item]` :
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Si vous déclarez le type de retour, FastAPI l'utilisera pour **valider** les données, les **documenter** dans OpenAPI, les **filtrer**, et les **sérialiser** avec Pydantic.
/// tip | Astuce
Comme Pydantic les sérialisera côté **Rust**, vous obtiendrez une **performance** bien supérieure que si vous ne déclarez pas de type de retour.
///
### Fonctions de chemin d'accès non asynchrones { #non-async-path-operation-functions }
Vous pouvez aussi utiliser des fonctions `def` classiques (sans `async`), et utiliser `yield` de la même manière.
FastAPI s'assure qu'elle s'exécute correctement afin de ne pas bloquer la boucle d'événements.
Comme dans ce cas la fonction n'est pas async, le bon type de retour serait `Iterable[Item]` :
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Sans type de retour { #no-return-type }
Vous pouvez également omettre le type de retour. FastAPI utilisera alors [`jsonable_encoder`](./encoder.md) pour convertir les données en quelque chose qui peut être sérialisé en JSON, puis les enverra en JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Événements envoyés par le serveur (SSE) { #server-sent-events-sse }
FastAPI propose également une prise en charge native des Server-Sent Events (SSE), qui sont assez proches mais avec quelques détails supplémentaires. Vous pouvez en apprendre davantage dans le chapitre suivant : [Événements envoyés par le serveur (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/fr/docs/tutorial/testing.md
================================================
# Tester { #testing }
Grâce à [Starlette](https://www.starlette.dev/testclient/), tester des applications **FastAPI** est simple et agréable.
C’est basé sur [HTTPX](https://www.python-httpx.org), dont la conception s’inspire de Requests, ce qui le rend très familier et intuitif.
Avec cela, vous pouvez utiliser [pytest](https://docs.pytest.org/) directement avec **FastAPI**.
## Utiliser `TestClient` { #using-testclient }
/// info
Pour utiliser `TestClient`, installez d’abord [`httpx`](https://www.python-httpx.org).
Vous devez créer un [environnement virtuel](../virtual-environments.md), l’activer, puis y installer le paquet, par exemple :
```console
$ pip install httpx
```
///
Importez `TestClient`.
Créez un `TestClient` en lui passant votre application **FastAPI**.
Créez des fonctions dont le nom commence par `test_` (c’est la convention standard de `pytest`).
Utilisez l’objet `TestClient` de la même manière que vous utilisez `httpx`.
Écrivez de simples instructions `assert` avec les expressions Python standard que vous devez vérifier (là encore, standard `pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Astuce
Remarquez que les fonctions de test sont des `def` normales, pas des `async def`.
Et les appels au client sont aussi des appels normaux, sans utiliser `await`.
Cela vous permet d’utiliser `pytest` directement sans complications.
///
/// note | Détails techniques
Vous pouvez aussi utiliser `from starlette.testclient import TestClient`.
**FastAPI** fournit le même `starlette.testclient` sous le nom `fastapi.testclient` uniquement pour votre commodité, en tant que développeur. Mais cela vient directement de Starlette.
///
/// tip | Astuce
Si vous souhaitez appeler des fonctions `async` dans vos tests en dehors de l’envoi de requêtes à votre application FastAPI (par exemple des fonctions de base de données asynchrones), consultez les [Tests asynchrones](../advanced/async-tests.md) dans le tutoriel avancé.
///
## Séparer les tests { #separating-tests }
Dans une application réelle, vous auriez probablement vos tests dans un fichier différent.
Et votre application **FastAPI** pourrait aussi être composée de plusieurs fichiers/modules, etc.
### Fichier d’application **FastAPI** { #fastapi-app-file }
Supposons que vous ayez une structure de fichiers comme décrit dans [Applications plus grandes](bigger-applications.md) :
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
Dans le fichier `main.py`, vous avez votre application **FastAPI** :
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Fichier de test { #testing-file }
Vous pourriez alors avoir un fichier `test_main.py` avec vos tests. Il pourrait vivre dans le même package Python (le même répertoire avec un fichier `__init__.py`) :
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Comme ce fichier se trouve dans le même package, vous pouvez utiliser des imports relatifs pour importer l’objet `app` depuis le module `main` (`main.py`) :
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
… et avoir le code des tests comme précédemment.
## Tester : exemple étendu { #testing-extended-example }
Étendons maintenant cet exemple et ajoutons plus de détails pour voir comment tester différentes parties.
### Fichier d’application **FastAPI** étendu { #extended-fastapi-app-file }
Continuons avec la même structure de fichiers qu’auparavant :
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Supposons que désormais le fichier `main.py` avec votre application **FastAPI** contienne d’autres **chemins d’accès**.
Il a une opération `GET` qui pourrait renvoyer une erreur.
Il a une opération `POST` qui pourrait renvoyer plusieurs erreurs.
Les deux chemins d’accès requièrent un en-tête `X-Token`.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Fichier de test étendu { #extended-testing-file }
Vous pourriez ensuite mettre à jour `test_main.py` avec les tests étendus :
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Chaque fois que vous avez besoin que le client transmette des informations dans la requête et que vous ne savez pas comment faire, vous pouvez chercher (Google) comment le faire avec `httpx`, ou même comment le faire avec `requests`, puisque la conception de HTTPX est basée sur celle de Requests.
Ensuite, vous faites simplement la même chose dans vos tests.
Par exemple :
* Pour passer un paramètre de chemin ou un paramètre de requête, ajoutez-le directement à l’URL.
* Pour passer un corps JSON, passez un objet Python (par exemple un `dict`) au paramètre `json`.
* Si vous devez envoyer des *Form Data* au lieu de JSON, utilisez le paramètre `data` à la place.
* Pour passer des en-têtes, utilisez un `dict` dans le paramètre `headers`.
* Pour les cookies, un `dict` dans le paramètre `cookies`.
Pour plus d’informations sur la manière de transmettre des données au backend (en utilisant `httpx` ou le `TestClient`), consultez la [documentation HTTPX](https://www.python-httpx.org).
/// info
Notez que le `TestClient` reçoit des données qui peuvent être converties en JSON, pas des modèles Pydantic.
Si vous avez un modèle Pydantic dans votre test et que vous souhaitez envoyer ses données à l’application pendant les tests, vous pouvez utiliser le `jsonable_encoder` décrit dans [Encodeur compatible JSON](encoder.md).
///
## Exécuter { #run-it }
Après cela, vous avez simplement besoin d’installer `pytest`.
Vous devez créer un [environnement virtuel](../virtual-environments.md), l’activer, puis y installer le paquet, par exemple :
```console
$ pip install pytest
---> 100%
```
Il détectera automatiquement les fichiers et les tests, les exécutera et vous communiquera les résultats.
Exécutez les tests avec :
================================================
FILE: docs/fr/docs/virtual-environments.md
================================================
# Environnements virtuels { #virtual-environments }
Lorsque vous travaillez sur des projets Python, vous devriez probablement utiliser un environnement virtuel (ou un mécanisme similaire) pour isoler les packages que vous installez pour chaque projet.
/// info
Si vous connaissez déjà les environnements virtuels, comment les créer et les utiliser, vous pouvez passer cette section. 🤓
///
/// tip | Astuce
Un environnement virtuel est différent d’une variable d’environnement.
Une variable d’environnement est une variable du système qui peut être utilisée par des programmes.
Un environnement virtuel est un répertoire contenant certains fichiers.
///
/// info
Cette page vous apprendra à utiliser les environnements virtuels et à comprendre leur fonctionnement.
Si vous êtes prêt à adopter un outil qui gère tout pour vous (y compris l’installation de Python), essayez [uv](https://github.com/astral-sh/uv).
///
## Créer un projet { #create-a-project }
Commencez par créer un répertoire pour votre projet.
Ce que je fais généralement, c’est créer un répertoire nommé `code` dans mon répertoire personnel/utilisateur.
Et à l’intérieur, je crée un répertoire par projet.
```console
// Aller au répertoire personnel
$ cd
// Créer un répertoire pour tous vos projets de code
$ mkdir code
// Entrer dans ce répertoire code
$ cd code
// Créer un répertoire pour ce projet
$ mkdir awesome-project
// Entrer dans ce répertoire de projet
$ cd awesome-project
```
## Créer un environnement virtuel { #create-a-virtual-environment }
Lorsque vous commencez à travailler sur un projet Python pour la première fois, créez un environnement virtuel dans votre projet.
/// tip | Astuce
Vous n’avez besoin de faire cela qu’une seule fois par projet, pas à chaque fois que vous travaillez.
///
//// tab | `venv`
Pour créer un environnement virtuel, vous pouvez utiliser le module `venv` fourni avec Python.
```console
$ python -m venv .venv
```
/// details | Que signifie cette commande
* `python` : utiliser le programme nommé `python`
* `-m` : appeler un module comme un script, nous préciserons ensuite quel module
* `venv` : utiliser le module nommé `venv` qui est normalement installé avec Python
* `.venv` : créer l’environnement virtuel dans le nouveau répertoire `.venv`
///
////
//// tab | `uv`
Si vous avez installé [`uv`](https://github.com/astral-sh/uv), vous pouvez l’utiliser pour créer un environnement virtuel.
```console
$ uv venv
```
/// tip | Astuce
Par défaut, `uv` créera un environnement virtuel dans un répertoire appelé `.venv`.
Mais vous pouvez le personnaliser en passant un argument supplémentaire avec le nom du répertoire.
///
////
Cette commande crée un nouvel environnement virtuel dans un répertoire appelé `.venv`.
/// details | `.venv` ou autre nom
Vous pourriez créer l’environnement virtuel dans un autre répertoire, mais il est d’usage de l’appeler `.venv`.
///
## Activer l’environnement virtuel { #activate-the-virtual-environment }
Activez le nouvel environnement virtuel afin que toute commande Python que vous exécutez ou tout package que vous installez l’utilise.
/// tip | Astuce
Faites cela à chaque fois que vous démarrez une nouvelle session de terminal pour travailler sur le projet.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ou si vous utilisez Bash pour Windows (par exemple [Git Bash](https://gitforwindows.org/)) :
```console
$ source .venv/Scripts/activate
```
////
/// tip | Astuce
Chaque fois que vous installez un nouveau package dans cet environnement, activez de nouveau l’environnement.
Vous vous assurez ainsi que si vous utilisez un programme de terminal (CLI) installé par ce package, vous utilisez celui de votre environnement virtuel et non un autre qui pourrait être installé globalement, probablement avec une version différente de celle dont vous avez besoin.
///
## Vérifier que l’environnement virtuel est actif { #check-the-virtual-environment-is-active }
Vérifiez que l’environnement virtuel est actif (la commande précédente a fonctionné).
/// tip | Astuce
C’est facultatif, mais c’est une bonne manière de vérifier que tout fonctionne comme prévu et que vous utilisez l’environnement virtuel voulu.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
S’il affiche le binaire `python` à `.venv/bin/python`, dans votre projet (dans cet exemple `awesome-project`), alors cela a fonctionné. 🎉
////
//// tab | Windows PowerShell
S’il affiche le binaire `python` à `.venv\Scripts\python`, dans votre projet (dans cet exemple `awesome-project`), alors cela a fonctionné. 🎉
////
## Mettre à niveau `pip` { #upgrade-pip }
/// tip | Astuce
Si vous utilisez [`uv`](https://github.com/astral-sh/uv), vous l’utiliserez pour installer des éléments à la place de `pip`, vous n’avez donc pas besoin de mettre `pip` à niveau. 😎
///
Si vous utilisez `pip` pour installer des packages (il est fourni par défaut avec Python), vous devez le mettre à niveau vers la dernière version.
Beaucoup d’erreurs exotiques lors de l’installation d’un package se résolvent simplement en mettant d’abord `pip` à niveau.
/// tip | Astuce
Vous feriez normalement cela une seule fois, juste après avoir créé l’environnement virtuel.
///
Vous devez vous assurer que l’environnement virtuel est actif (avec la commande ci-dessus), puis exécuter :
/// tip | Astuce
Parfois, vous pourriez obtenir une erreur **`No module named pip`** en essayant de mettre à niveau pip.
Si cela arrive, installez et mettez à niveau pip avec la commande ci-dessous :
Cette commande installera pip s’il n’est pas déjà installé et garantit aussi que la version de pip installée est au moins aussi récente que celle disponible dans `ensurepip`.
///
## Ajouter `.gitignore` { #add-gitignore }
Si vous utilisez Git (vous devriez), ajoutez un fichier `.gitignore` pour exclure tout ce qui se trouve dans votre `.venv` de Git.
/// tip | Astuce
Si vous avez utilisé [`uv`](https://github.com/astral-sh/uv) pour créer l’environnement virtuel, il l’a déjà fait pour vous, vous pouvez passer cette étape. 😎
///
/// tip | Astuce
Faites cela une seule fois, juste après avoir créé l’environnement virtuel.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Que signifie cette commande
* `echo "*"` : va « afficher » le texte `*` dans le terminal (la partie suivante change un peu cela)
* `>` : tout ce qui est affiché dans le terminal par la commande à gauche de `>` ne doit pas être affiché mais écrit dans le fichier à droite de `>`
* `.gitignore` : le nom du fichier dans lequel le texte doit être écrit
Et `*` signifie pour Git « tout ». Ainsi, il ignorera tout dans le répertoire `.venv`.
Cette commande créera un fichier `.gitignore` avec le contenu :
```gitignore
*
```
///
## Installer des packages { #install-packages }
Après avoir activé l’environnement, vous pouvez y installer des packages.
/// tip | Astuce
Faites cela une seule fois lorsque vous installez ou mettez à niveau les packages nécessaires à votre projet.
Si vous devez mettre à niveau une version ou ajouter un nouveau package, vous le referez.
///
### Installer des packages directement { #install-packages-directly }
Si vous êtes pressé et ne souhaitez pas utiliser un fichier pour déclarer les dépendances de votre projet, vous pouvez les installer directement.
/// tip | Astuce
C’est une très bonne idée de placer les packages et leurs versions nécessaires à votre programme dans un fichier (par exemple `requirements.txt` ou `pyproject.toml`).
///
//// tab | `pip`
////
### Installer depuis `requirements.txt` { #install-from-requirements-txt }
Si vous avez un `requirements.txt`, vous pouvez maintenant l’utiliser pour installer ses packages.
//// tab | `pip`
////
/// details | `requirements.txt`
Un `requirements.txt` avec quelques packages pourrait ressembler à :
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Exécuter votre programme { #run-your-program }
Après avoir activé l’environnement virtuel, vous pouvez exécuter votre programme, et il utilisera le Python de votre environnement virtuel avec les packages que vous y avez installés.
```console
$ python main.py
Hello World
```
## Configurer votre éditeur { #configure-your-editor }
Vous utiliserez probablement un éditeur, assurez-vous de le configurer pour utiliser le même environnement virtuel que vous avez créé (il le détectera probablement automatiquement) afin d’avoir l’autocomplétion et les erreurs inline.
Par exemple :
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Astuce
Vous devez normalement faire cela une seule fois, lorsque vous créez l’environnement virtuel.
///
## Désactiver l’environnement virtuel { #deactivate-the-virtual-environment }
Une fois que vous avez fini de travailler sur votre projet, vous pouvez désactiver l’environnement virtuel.
```console
$ deactivate
```
Ainsi, lorsque vous exécutez `python`, il n’essaiera pas de l’exécuter depuis cet environnement virtuel avec les packages qui y sont installés.
## Prêt à travailler { #ready-to-work }
Vous êtes maintenant prêt à commencer à travailler sur votre projet.
/// tip | Astuce
Voulez-vous comprendre tout ce qui précède ?
Continuez la lecture. 👇🤓
///
## Pourquoi des environnements virtuels { #why-virtual-environments }
Pour travailler avec FastAPI, vous devez installer [Python](https://www.python.org/).
Ensuite, vous devrez installer FastAPI et tout autre package que vous souhaitez utiliser.
Pour installer des packages, vous utiliseriez normalement la commande `pip` fournie avec Python (ou des alternatives similaires).
Néanmoins, si vous utilisez simplement `pip` directement, les packages seraient installés dans votre environnement Python global (l’installation globale de Python).
### Le problème { #the-problem }
Alors, quel est le problème d’installer des packages dans l’environnement Python global ?
À un moment donné, vous finirez probablement par écrire de nombreux programmes différents qui dépendent de packages différents. Et certains de ces projets sur lesquels vous travaillez dépendront de versions différentes du même package. 😱
Par exemple, vous pourriez créer un projet appelé `philosophers-stone`, ce programme dépend d’un autre package appelé **`harry`, en version `1`**. Vous devez donc installer `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Puis, plus tard, vous créez un autre projet appelé `prisoner-of-azkaban`, et ce projet dépend aussi de `harry`, mais il a besoin de **`harry` en version `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Mais maintenant, le problème est que, si vous installez les packages globalement (dans l’environnement global) au lieu de dans un environnement virtuel local, vous devrez choisir quelle version de `harry` installer.
Si vous voulez exécuter `philosophers-stone`, vous devrez d’abord installer `harry` en version `1`, par exemple avec :
```console
$ pip install "harry==1"
```
Et vous vous retrouverez avec `harry` en version `1` installé dans votre environnement Python global.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Mais si vous voulez ensuite exécuter `prisoner-of-azkaban`, vous devrez désinstaller `harry` version `1` et installer `harry` version `3` (ou bien installer la version `3` désinstallerait automatiquement la version `1`).
```console
$ pip install "harry==3"
```
Et vous vous retrouverez alors avec `harry` version `3` installé dans votre environnement Python global.
Et si vous essayez d’exécuter à nouveau `philosophers-stone`, il y a une chance que cela ne fonctionne pas car il a besoin de `harry` version `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Astuce
Il est très courant que les packages Python fassent de leur mieux pour éviter les changements cassants dans les nouvelles versions, mais il vaut mieux jouer la sécurité et installer de nouvelles versions intentionnellement et lorsque vous pouvez exécuter les tests pour vérifier que tout fonctionne correctement.
///
Maintenant, imaginez cela avec beaucoup d’autres packages dont tous vos projets dépendent. C’est très difficile à gérer. Et vous finiriez probablement par exécuter certains projets avec des versions incompatibles des packages, sans savoir pourquoi quelque chose ne fonctionne pas.
De plus, selon votre système d’exploitation (par exemple Linux, Windows, macOS), il se peut qu’il soit livré avec Python déjà installé. Et dans ce cas, il avait probablement des packages préinstallés avec des versions spécifiques nécessaires à votre système. Si vous installez des packages dans l’environnement Python global, vous pourriez finir par casser certains des programmes fournis avec votre système d’exploitation.
## Où les packages sont-ils installés { #where-are-packages-installed }
Lorsque vous installez Python, il crée des répertoires avec des fichiers sur votre ordinateur.
Certains de ces répertoires sont chargés de contenir tous les packages que vous installez.
Lorsque vous exécutez :
```console
// Ne l’exécutez pas maintenant, c’est juste un exemple 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Cela téléchargera un fichier compressé avec le code de FastAPI, normalement depuis [PyPI](https://pypi.org/project/fastapi/).
Il téléchargera également des fichiers pour d’autres packages dont FastAPI dépend.
Ensuite, il extraira tous ces fichiers et les placera dans un répertoire de votre ordinateur.
Par défaut, il placera ces fichiers téléchargés et extraits dans le répertoire fourni avec votre installation de Python, c’est l’environnement global.
## Qu’est-ce qu’un environnement virtuel { #what-are-virtual-environments }
La solution aux problèmes posés par le fait d’avoir tous les packages dans l’environnement global est d’utiliser un environnement virtuel pour chaque projet sur lequel vous travaillez.
Un environnement virtuel est un répertoire, très similaire à celui global, où vous pouvez installer les packages pour un projet.
De cette manière, chaque projet aura son propre environnement virtuel (répertoire `.venv`) avec ses propres packages.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Que signifie activer un environnement virtuel { #what-does-activating-a-virtual-environment-mean }
Lorsque vous activez un environnement virtuel, par exemple avec :
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ou si vous utilisez Bash pour Windows (par exemple [Git Bash](https://gitforwindows.org/)) :
```console
$ source .venv/Scripts/activate
```
////
Cette commande créera ou modifiera certaines [variables d’environnement](environment-variables.md) qui seront disponibles pour les prochaines commandes.
L’une de ces variables est la variable `PATH`.
/// tip | Astuce
Vous pouvez en savoir plus sur la variable d’environnement `PATH` dans la section [Variables d’environnement](environment-variables.md#path-environment-variable).
///
Activer un environnement virtuel ajoute son chemin `.venv/bin` (sur Linux et macOS) ou `.venv\Scripts` (sur Windows) à la variable d’environnement `PATH`.
Disons qu’avant d’activer l’environnement, la variable `PATH` ressemblait à ceci :
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Cela signifie que le système chercherait des programmes dans :
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Cela signifie que le système chercherait des programmes dans :
* `C:\Windows\System32`
////
Après avoir activé l’environnement virtuel, la variable `PATH` ressemblerait à quelque chose comme ceci :
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Cela signifie que le système commencera maintenant par chercher des programmes dans :
```plaintext
/home/user/code/awesome-project/.venv/bin
```
avant de chercher dans les autres répertoires.
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
et utilisera celui-ci.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Cela signifie que le système commencera maintenant par chercher des programmes dans :
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
avant de chercher dans les autres répertoires.
Ainsi, lorsque vous tapez `python` dans le terminal, le système trouvera le programme Python dans
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
et utilisera celui-ci.
////
Un détail important est qu’il placera le chemin de l’environnement virtuel au début de la variable `PATH`. Le système le trouvera avant de trouver tout autre Python disponible. Ainsi, lorsque vous exécutez `python`, il utilisera le Python de l’environnement virtuel au lieu de tout autre `python` (par exemple, un `python` d’un environnement global).
Activer un environnement virtuel change aussi deux ou trois autres choses, mais c’est l’un des points les plus importants.
## Vérifier un environnement virtuel { #checking-a-virtual-environment }
Lorsque vous vérifiez si un environnement virtuel est actif, par exemple avec :
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Cela signifie que le programme `python` qui sera utilisé est celui dans l’environnement virtuel.
Vous utilisez `which` sous Linux et macOS et `Get-Command` sous Windows PowerShell.
La façon dont cette commande fonctionne est qu’elle va vérifier la variable d’environnement `PATH`, en parcourant chaque chemin dans l’ordre, à la recherche du programme nommé `python`. Une fois trouvé, elle vous affichera le chemin vers ce programme.
La partie la plus importante est que lorsque vous appelez `python`, c’est exactement « `python` » qui sera exécuté.
Ainsi, vous pouvez confirmer si vous êtes dans le bon environnement virtuel.
/// tip | Astuce
Il est facile d’activer un environnement virtuel, d’obtenir un Python, puis d’aller vers un autre projet.
Et le second projet ne fonctionnerait pas parce que vous utilisez le Python incorrect, provenant d’un environnement virtuel d’un autre projet.
Il est utile de pouvoir vérifier quel `python` est utilisé. 🤓
///
## Pourquoi désactiver un environnement virtuel { #why-deactivate-a-virtual-environment }
Par exemple, vous pourriez travailler sur un projet `philosophers-stone`, activer cet environnement virtuel, installer des packages et travailler avec cet environnement.
Puis vous souhaitez travailler sur un autre projet `prisoner-of-azkaban`.
Vous allez vers ce projet :
```console
$ cd ~/code/prisoner-of-azkaban
```
Si vous ne désactivez pas l’environnement virtuel de `philosophers-stone`, lorsque vous exécutez `python` dans le terminal, il essaiera d’utiliser le Python de `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Erreur lors de l'import de sirius, il n'est pas installé 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Mais si vous désactivez l’environnement virtuel et activez le nouveau pour `prisoner-of-askaban`, alors lorsque vous exécuterez `python`, il utilisera le Python de l’environnement virtuel de `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// Vous n’avez pas besoin d’être dans l’ancien répertoire pour désactiver, vous pouvez le faire où que vous soyez, même après être allé dans l’autre projet 😎
$ deactivate
// Activer l’environnement virtuel dans prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Maintenant, lorsque vous exécutez python, il trouvera le package sirius installé dans cet environnement virtuel ✨
$ python main.py
I solemnly swear 🐺
```
## Alternatives { #alternatives }
Ceci est un guide simple pour vous lancer et vous montrer comment tout fonctionne en dessous.
Il existe de nombreuses alternatives pour gérer les environnements virtuels, les dépendances de packages (requirements), les projets.
Lorsque vous êtes prêt et souhaitez utiliser un outil pour gérer l’ensemble du projet, les dépendances, les environnements virtuels, etc., je vous suggère d’essayer [uv](https://github.com/astral-sh/uv).
`uv` peut faire beaucoup de choses, il peut :
* Installer Python pour vous, y compris différentes versions
* Gérer l’environnement virtuel pour vos projets
* Installer des packages
* Gérer les dépendances de packages et leurs versions pour votre projet
* Vous assurer d’avoir un ensemble exact de packages et de versions à installer, y compris leurs dépendances, afin que vous puissiez être certain d’exécuter votre projet en production exactement comme sur votre ordinateur pendant le développement, cela s’appelle le locking
* Et bien d’autres choses
## Conclusion { #conclusion }
Si vous avez lu et compris tout cela, vous en savez maintenant bien plus sur les environnements virtuels que beaucoup de développeurs. 🤓
Connaître ces détails vous sera très probablement utile à l’avenir lorsque vous déboguerez quelque chose qui semble complexe, mais vous saurez comment tout fonctionne en dessous. 😎
================================================
FILE: docs/fr/llm-prompt.md
================================================
### Target language
Translate to French (français).
Language code: fr.
### Grammar to use when talking to the reader
Use the formal grammar (use `vous` instead of `tu`).
Additionally, in instructional sentences, prefer the present tense for obligations:
- Prefer `vous devez …` over `vous devrez …`, unless the English source explicitly refers to a future requirement.
- When translating “make sure (that) … is …”, prefer the indicative after `vous assurer que` (e.g. `Vous devez vous assurer qu'il est …`) instead of the subjunctive (e.g. `qu'il soit …`).
### Quotes
- Convert neutral double quotes (`"`) to French guillemets (`«` and `»`).
- Do not convert quotes inside code blocks, inline code, paths, URLs, or anything wrapped in backticks.
Examples:
Source (English):
```
"Hello world"
“Hello Universe”
"He said: 'Hello'"
"The module is `__main__`"
```
Result (French):
```
"Hello world"
“Hello Universe”
"He said: 'Hello'"
"The module is `__main__`"
```
### Ellipsis
- Make sure there is a space between an ellipsis and a word following or preceding the ellipsis.
Examples:
Source (English):
```
...as we intended.
...this would work:
...etc.
others...
More to come...
```
Result (French):
```
... comme prévu.
... cela fonctionnerait :
... etc.
D'autres ...
La suite ...
```
- This does not apply in URLs, code blocks, and code snippets. Do not remove or add spaces there.
### Headings
- Prefer translating headings using the infinitive form (as is common in the existing French docs): `Créer…`, `Utiliser…`, `Ajouter…`.
- For headings that are instructions written in imperative in English (e.g. `Go check …`), keep them in imperative in French, using the formal grammar (e.g. `Allez voir …`).
### French instructions about technical terms
Do not try to translate everything. In particular, keep common programming terms (e.g. `framework`, `endpoint`, `plug-in`, `payload`).
Keep class names, function names, modules, file names, and CLI commands unchanged.
### List of English terms and their preferred French translations
Below is a list of English terms and their preferred French translations, separated by a colon (:). Use these translations, do not use your own. If an existing translation does not use these terms, update it to use them.
- /// note | Technical Details»: /// note | Détails techniques
- /// note: /// note | Remarque
- /// tip: /// tip | Astuce
- /// warning: /// warning | Alertes
- /// check: /// check | Vérifications
- /// info: /// info
- the docs: les documents
- the documentation: la documentation
- Exclude from OpenAPI: Exclusion d'OpenAPI
- framework: framework (do not translate to cadre)
- performance: performance
- type hints: annotations de type
- type annotations: annotations de type
- autocomplete: autocomplétion
- autocompletion: autocomplétion
- the request (what the client sends to the server): la requête
- the response (what the server sends back to the client): la réponse
- the request body: le corps de la requête
- the response body: le corps de la réponse
- path operation: chemin d'accès
- path operations (plural): chemins d'accès
- path operation function: fonction de chemin d'accès
- path operation decorator: décorateur de chemin d'accès
- path parameter: paramètre de chemin
- query parameter: paramètre de requête
- the `Request`: `Request` (keep as code identifier)
- the `Response`: `Response` (keep as code identifier)
- deployment: déploiement
- to upgrade: mettre à niveau
- deprecated: déprécié
- to deprecate: déprécier
- cheat sheet: aide-mémoire
- plug-in: plug-in
================================================
FILE: docs/fr/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/ja/docs/_llm-test.md
================================================
# LLM テストファイル { #llm-test-file }
このドキュメントは、ドキュメントを翻訳する LLM が、`scripts/translate.py` の `general_prompt` と、`docs/{language code}/llm-prompt.md` の言語固有プロンプトを理解しているかをテストします。言語固有プロンプトは `general_prompt` の末尾に追加されます。
ここに追加したテストは、すべての言語固有プロンプトの設計者が参照します。
使い方:
* 言語固有プロンプトを用意します - `docs/{language code}/llm-prompt.md`。
* この文書を希望するターゲット言語に新規で翻訳します(例: `translate.py` の `translate-page` コマンド)。これにより `docs/{language code}/docs/_llm-test.md` に翻訳が作成されます。
* 翻訳が問題ないか確認します。
* 必要であれば、言語固有プロンプト、general プロンプト、または英語ドキュメントを改善します。
* その後、翻訳に残っている問題を手動で修正し、良い翻訳にします。
* 良い翻訳を用意した状態でもう一度翻訳します。理想的な結果は、LLM が翻訳に一切変更を加えないことです。つまり general プロンプトと言語固有プロンプトが最良であることを意味します(時々いくつかランダムに見える変更を行うことがあります。理由は [LLM は決定論的アルゴリズムではない](https://doublespeak.chat/#/handbook#deterministic-output) ためです)。
テスト内容:
## コードスニペット { #code-snippets }
//// tab | テスト
これはコードスニペットです: `foo`。そしてこれもコードスニペットです: `bar`。さらにもう一つ: `baz quux`。
////
//// tab | 情報
コードスニペットの内容はそのままにしておく必要があります。
`scripts/translate.py` の general プロンプト内「### Content of code snippets」の節を参照してください。
////
## 引用 { #quotes }
//// tab | テスト
昨日、友人はこう書きました。「incorrectly を正しく綴れば、あなたはそれを間違って綴ったことになる」。それに対して私はこう答えました。「そのとおり。ただし『incorrectly』は誤りで、『"incorrectly"』ではありません」。
/// note | 備考
LLM はおそらくここを誤って翻訳します。重要なのは、再翻訳時に修正済みの翻訳を維持できるかどうかだけです。
///
////
//// tab | 情報
プロンプト設計者は、ストレートクォートをタイポグラフィックな引用符に変換するかどうかを選べます。そのままでも問題ありません。
例として `docs/de/llm-prompt.md` の「### Quotes」の節を参照してください。
////
## コードスニペット内の引用 { #quotes-in-code-snippets }
//// tab | テスト
`pip install "foo[bar]"`
コードスニペット中の文字列リテラルの例: `"this"`, `'that'`.
難しい文字列リテラルの例: `f"I like {'oranges' if orange else "apples"}"`
ハードコア: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | 情報
... ただし、コードスニペット内の引用符はそのままにしておく必要があります。
////
## コードブロック { #code-blocks }
//// tab | テスト
Bash のコード例です...
```bash
# 宇宙にあいさつを表示
echo "Hello universe"
```
...そしてコンソールのコード例です...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...さらに別のコンソールのコード例です...
```console
// ディレクトリ "Code" を作成
$ mkdir code
// そのディレクトリに移動
$ cd code
```
...そして Python のコード例です...
```Python
wont_work() # これは動作しません 😱
works(foo="bar") # これは動作します 🎉
```
...以上です。
////
//// tab | 情報
コードブロック内のコードは、コメントを除き、変更してはいけません。
`scripts/translate.py` の general プロンプト内「### Content of code blocks」の節を参照してください。
////
## タブと色付きボックス { #tabs-and-colored-boxes }
//// tab | テスト
/// info | 情報
いくつかのテキスト
///
/// note | 備考
いくつかのテキスト
///
/// note | 技術詳細
いくつかのテキスト
///
/// check | 確認
いくつかのテキスト
///
/// tip | 豆知識
いくつかのテキスト
///
/// warning | 注意
いくつかのテキスト
///
/// danger | 警告
いくつかのテキスト
///
////
//// tab | 情報
タブおよび `Info`/`Note`/`Warning` などのブロックには、タイトルの翻訳を縦棒(`|`)の後ろに追加します。
`scripts/translate.py` の general プロンプト内「### Special blocks」と「### Tab blocks」の節を参照してください。
////
## Web リンクと内部リンク { #web-and-internal-links }
//// tab | テスト
リンクのテキストは翻訳し、リンク先のアドレスは変更しないでください:
* [上の見出しへのリンク](#code-snippets)
* [内部リンク](index.md#installation)
* [外部リンク](https://sqlmodel.tiangolo.com/)
* [スタイルへのリンク](https://fastapi.tiangolo.com/css/styles.css)
* [スクリプトへのリンク](https://fastapi.tiangolo.com/js/logic.js)
* [画像へのリンク](https://fastapi.tiangolo.com/img/foo.jpg)
リンクのテキストは翻訳し、リンク先のアドレスは翻訳版を指すようにしてください:
* [FastAPI リンク](https://fastapi.tiangolo.com/ja/)
////
//// tab | 情報
リンクのテキストは翻訳し、アドレスは変更しないでください。例外は、FastAPI ドキュメントのページへの絶対 URL です。その場合は翻訳版へのリンクにします。
`scripts/translate.py` の general プロンプト内「### Links」の節を参照してください。
////
## HTML "abbr" 要素 { #html-abbr-elements }
//// tab | テスト
ここでは HTML の "abbr" 要素で包まれたものをいくつか示します(いくつかは架空です):
### abbr が完全な語句を示す { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### abbr が完全な語句と説明を示す { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | 情報
"abbr" 要素の "title" 属性は特定の指示に従って翻訳します。
翻訳は、英語の語を説明するために独自の "abbr" 要素を追加してもよく、LLM はそれらを削除してはいけません。
`scripts/translate.py` の general プロンプト内「### HTML abbr elements」の節を参照してください。
////
## HTML "dfn" 要素 { #html-dfn-elements }
* クラスター
* ディープラーニング
## 見出し { #headings }
//// tab | テスト
### Web アプリを開発する - チュートリアル { #develop-a-webapp-a-tutorial }
こんにちは。
### 型ヒントとアノテーション { #type-hints-and-annotations }
またこんにちは。
### スーパークラスとサブクラス { #super-and-subclasses }
またこんにちは。
////
//// tab | 情報
見出しに関する唯一の厳格なルールは、リンクが壊れないように、LLM が中括弧内のハッシュ部分を変更しないことです。
`scripts/translate.py` の general プロンプト内「### Headings」の節を参照してください。
言語固有の指示については、例として `docs/de/llm-prompt.md` の「### Headings」の節を参照してください。
////
## ドキュメントで使う用語 { #terms-used-in-the-docs }
//// tab | テスト
* you
* your
* e.g.
* etc.
* `foo` を `int` として
* `bar` を `str` として
* `baz` を `list` として
* チュートリアル - ユーザーガイド
* 上級ユーザーガイド
* SQLModel ドキュメント
* API ドキュメント
* 自動生成ドキュメント
* データサイエンス
* ディープラーニング
* 機械学習
* 依存性注入
* HTTP Basic 認証
* HTTP Digest
* ISO 形式
* JSON Schema 規格
* JSON スキーマ
* スキーマ定義
* Password Flow
* モバイル
* 非推奨
* 設計された
* 無効
* オンザフライ
* 標準
* デフォルト
* 大文字小文字を区別
* 大文字小文字を区別しない
* アプリケーションを提供する
* ページを配信する
* アプリ
* アプリケーション
* リクエスト
* レスポンス
* エラーレスポンス
* path operation
* path operation デコレータ
* path operation 関数
* ボディ
* リクエストボディ
* レスポンスボディ
* JSON ボディ
* フォームボディ
* ファイルボディ
* 関数本体
* パラメータ
* ボディパラメータ
* パスパラメータ
* クエリパラメータ
* Cookie パラメータ
* ヘッダーパラメータ
* フォームパラメータ
* 関数パラメータ
* イベント
* 起動イベント
* サーバーの起動
* シャットダウンイベント
* lifespan イベント
* ハンドラ
* イベントハンドラ
* 例外ハンドラ
* 処理する
* モデル
* Pydantic モデル
* データモデル
* データベースモデル
* フォームモデル
* モデルオブジェクト
* クラス
* 基底クラス
* 親クラス
* サブクラス
* 子クラス
* 兄弟クラス
* クラスメソッド
* ヘッダー
* ヘッダー(複数)
* 認可ヘッダー
* `Authorization` ヘッダー
* Forwarded ヘッダー
* 依存性注入システム
* 依存関係
* dependable
* dependant
* I/O バウンド
* CPU バウンド
* 同時実行性
* 並列性
* マルチプロセッシング
* env var
* 環境変数
* `PATH`
* `PATH` 環境変数
* 認証
* 認証プロバイダ
* 認可
* 認可フォーム
* 認可プロバイダ
* ユーザーが認証する
* システムがユーザーを認証する
* CLI
* コマンドラインインターフェース
* サーバー
* クライアント
* クラウドプロバイダ
* クラウドサービス
* 開発
* 開発段階
* dict
* 辞書
* 列挙型
* Enum
* 列挙メンバー
* エンコーダー
* デコーダー
* エンコードする
* デコードする
* 例外
* 送出する
* 式
* 文
* フロントエンド
* バックエンド
* GitHub ディスカッション
* GitHub Issue
* パフォーマンス
* パフォーマンス最適化
* 戻り値の型
* 戻り値
* セキュリティ
* セキュリティスキーム
* タスク
* バックグラウンドタスク
* タスク関数
* テンプレート
* テンプレートエンジン
* 型アノテーション
* 型ヒント
* サーバーワーカー
* Uvicorn ワーカー
* Gunicorn ワーカー
* ワーカープロセス
* ワーカークラス
* ワークロード
* デプロイ
* デプロイする
* SDK
* ソフトウェア開発キット
* `APIRouter`
* `requirements.txt`
* Bearer Token
* 破壊的変更
* バグ
* ボタン
* 呼び出し可能
* コード
* コミット
* コンテキストマネージャ
* コルーチン
* データベースセッション
* ディスク
* ドメイン
* エンジン
* フェイクの X
* HTTP GET メソッド
* アイテム
* ライブラリ
* ライフスパン
* ロック
* ミドルウェア
* モバイルアプリケーション
* モジュール
* マウント
* ネットワーク
* オリジン
* オーバーライド
* ペイロード
* プロセッサ
* プロパティ
* プロキシ
* プルリクエスト
* クエリ
* RAM
* リモートマシン
* ステータスコード
* 文字列
* タグ
* Web フレームワーク
* ワイルドカード
* 返す
* 検証する
////
//// tab | 情報
これはドキュメントで見られる(主に)技術用語の不完全かつ規範的でない一覧です。プロンプト設計者が、LLM がどの用語で手助けを必要としているかを把握するのに役立つかもしれません。例えば、良い翻訳を最適でない翻訳に戻してしまう場合や、あなたの言語での活用・格変化に問題がある場合などです。
`docs/de/llm-prompt.md` の「### List of English terms and their preferred German translations」の節を参照してください。
////
================================================
FILE: docs/ja/docs/about/index.md
================================================
# 概要 { #about }
FastAPI の概要、その設計やインスピレーションなどについて解説します。🤓
================================================
FILE: docs/ja/docs/advanced/additional-responses.md
================================================
# OpenAPI の追加レスポンス { #additional-responses-in-openapi }
/// warning | 注意
これは比較的高度なトピックです。
FastAPI を使い始めたばかりであれば、これは不要かもしれません。
///
追加のステータスコード、メディアタイプ、説明などを伴う追加レスポンスを宣言できます。
それらの追加レスポンスは OpenAPI スキーマに含まれ、API ドキュメントにも表示されます。
ただし、それらの追加レスポンスについては、ステータスコードとコンテンツを指定して `JSONResponse` などの `Response` を直接返す必要があります。
## `model` を使った追加レスポンス { #additional-response-with-model }
*path operation デコレータ*に `responses` パラメータを渡せます。
これは `dict` を受け取り、キーは各レスポンスのステータスコード(例: `200`)、値は各レスポンスの情報を含む別の `dict` です。
それぞれのレスポンス `dict` には、`response_model` と同様に Pydantic モデルを格納する `model` キーを含められます。
FastAPI はそのモデルから JSON Schema を生成し、OpenAPI の適切な場所に含めます。
例えば、ステータスコード `404` と Pydantic モデル `Message` を持つ別のレスポンスを宣言するには、次のように書けます:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | 備考
`JSONResponse` を直接返す必要がある点に注意してください。
///
/// info | 情報
`model` キーは OpenAPI の一部ではありません。
FastAPI はそこから Pydantic モデルを取得して JSON Schema を生成し、適切な場所に配置します。
適切な場所は次のとおりです:
- `content` キーの中。これは値として別の JSON オブジェクト(`dict`)を持ち、その中に次が含まれます:
- メディアタイプ(例: `application/json`)をキーとし、値としてさらに別の JSON オブジェクトを持ち、その中に次が含まれます:
- `schema` キー。値としてモデル由来の JSON Schema を持ち、ここが正しい配置場所です。
- FastAPI はここに、スキーマを直接埋め込む代わりに OpenAPI 内のグローバルな JSON Schema への参照を追加します。これにより、他のアプリケーションやクライアントがそれらの JSON Schema を直接利用し、より良いコード生成ツール等を提供できます。
///
この *path operation* のために OpenAPI に生成されるレスポンスは次のとおりです:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
スキーマは OpenAPI スキーマ内の別の場所への参照になります:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## メインのレスポンスに追加のメディアタイプ { #additional-media-types-for-the-main-response }
同じ `responses` パラメータを使って、同一のメインレスポンスに別のメディアタイプを追加できます。
例えば、`image/png` の追加メディアタイプを加え、あなたの *path operation* が JSON オブジェクト(メディアタイプ `application/json`)または PNG 画像を返せることを宣言できます:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | 備考
画像は `FileResponse` を使って直接返す必要がある点に注意してください。
///
/// info | 情報
`responses` パラメータで明示的に別のメディアタイプを指定しない限り、FastAPI はレスポンスがメインのレスポンスクラスと同じメディアタイプ(デフォルトは `application/json`)であるとみなします。
ただし、メディアタイプが `None` のカスタムレスポンスクラスを指定している場合、モデルが関連付けられた追加レスポンスには FastAPI は `application/json` を使用します。
///
## 情報の結合 { #combining-information }
`response_model`、`status_code`、`responses` パラメータなど、複数の場所からのレスポンス情報を組み合わせることもできます。
`response_model` を宣言し、デフォルトのステータスコード `200`(必要なら任意のコード)を使い、その同じレスポンスに対する追加情報を `responses` で OpenAPI スキーマに直接記述できます。
FastAPI は `responses` にある追加情報を保持し、モデルの JSON Schema と結合します。
例えば、Pydantic モデルを用い、独自の `description` を持つステータスコード `404` のレスポンスを宣言できます。
さらに、`response_model` を使うステータスコード `200` のレスポンスに独自の `example` を含めることもできます:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
これらはすべて結合されて OpenAPI に含まれ、API ドキュメントに表示されます:
## 事前定義レスポンスとカスタムの組み合わせ { #combine-predefined-responses-and-custom-ones }
多くの *path operations* に適用できる事前定義のレスポンスを用意しつつ、各 *path operation* ごとに必要なカスタムレスポンスと組み合わせたい場合があります。
そのような場合、Python の `**dict_to_unpack` による `dict` の「アンパック」テクニックを使えます:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
ここでは、`new_dict` には `old_dict` のすべてのキーと値に加え、新しいキーと値が含まれます:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
このテクニックを使うと、*path operations* で事前定義レスポンスを再利用し、さらにカスタムのレスポンスを組み合わせられます。
例えば:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## OpenAPI レスポンスの詳細 { #more-information-about-openapi-responses }
レスポンスに正確に何を含められるかは、OpenAPI 仕様の次のセクションを参照してください:
- [OpenAPI の Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object)、ここには `Response Object` が含まれます。
- [OpenAPI の Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object)、`responses` パラメータ内の各レスポンスに、ここで定義されている要素を直接含められます。`description`、`headers`、`content`(ここで異なるメディアタイプや JSON Schema を宣言します)、`links` など。
================================================
FILE: docs/ja/docs/advanced/additional-status-codes.md
================================================
# 追加のステータスコード { #additional-status-codes }
デフォルトでは、 **FastAPI** は `JSONResponse` を使ってレスポンスを返し、*path operation* から返した内容をその `JSONResponse` の中に入れます。
デフォルトのステータスコード、または *path operation* で設定したステータスコードが使用されます。
## 追加のステータスコード { #additional-status-codes_1 }
メインのステータスコードとは別に追加のステータスコードを返したい場合は、`JSONResponse` のような `Response` を直接返し、追加のステータスコードを直接設定できます。
たとえば、item を更新でき、成功時に HTTP ステータスコード 200 "OK" を返す *path operation* を作りたいとします。
しかし、新しい item も受け付けたいとします。そして、item が以前存在しなかった場合には作成し、HTTP ステータスコード 201「Created」を返します。
これを実現するには、`JSONResponse` をインポートし、望む `status_code` を設定して、そこで内容を直接返します。
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning
上の例のように `Response` を直接返すと、それはそのまま返されます。
モデルなどによってシリアライズされません。
必要なデータが含まれていること、そして(`JSONResponse` を使用している場合)値が有効な JSON であることを確認してください。
///
/// note | 技術詳細
`from starlette.responses import JSONResponse` を使うこともできます。
**FastAPI** は開発者の利便性のために、`fastapi.responses` と同じ `starlette.responses` を提供しています。しかし、利用可能なレスポンスのほとんどは Starlette から直接提供されています。`status` も同様です。
///
## OpenAPI と API ドキュメント { #openapi-and-api-docs }
追加のステータスコードとレスポンスを直接返す場合、それらは OpenAPI スキーマ(API ドキュメント)には含まれません。FastAPI には、事前に何が返されるかを知る方法がないからです。
しかし、[追加のレスポンス](additional-responses.md) を使ってコード内にドキュメント化できます。
================================================
FILE: docs/ja/docs/advanced/advanced-dependencies.md
================================================
# 高度な依存関係 { #advanced-dependencies }
## パラメータ化された依存関係 { #parameterized-dependencies }
これまで見てきた依存関係は、固定の関数またはクラスでした。
しかし、多くの異なる関数やクラスを宣言せずに、その依存関係にパラメータを設定したい場合があります。
たとえば、クエリパラメータ `q` に、ある固定の内容が含まれているかを検査する依存関係が欲しいとします。
ただし、その固定の内容はパラメータ化できるようにしたいです。
## "callable" なインスタンス { #a-callable-instance }
Python には、クラスのインスタンスを "callable" にする方法があります。
クラス自体(これはすでに callable です)ではなく、そのクラスのインスタンスです。
そのためには、`__call__` メソッドを宣言します:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
この場合、この `__call__` が、**FastAPI** が追加のパラメータやサブ依存関係を確認するために使うものになり、後であなたの *path operation 関数* のパラメータに値を渡すために呼び出されるものになります。
## インスタンスのパラメータ化 { #parameterize-the-instance }
そして、`__init__` を使って、依存関係を「パラメータ化」するために利用できるインスタンスのパラメータを宣言できます:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
この場合、**FastAPI** は `__init__` に触れたり気にかけたりすることはありません。私たちがコード内で直接使います。
## インスタンスの作成 { #create-an-instance }
このクラスのインスタンスは次のように作成できます:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
このようにして依存関係を「パラメータ化」できます。いまや `"bar"` が属性 `checker.fixed_content` として中に保持されています。
## インスタンスを依存関係として使う { #use-the-instance-as-a-dependency }
その後、`Depends(FixedContentQueryChecker)` の代わりに `Depends(checker)` でこの `checker` を使えます。依存関係はクラスそのものではなく、インスタンスである `checker` だからです。
依存関係を解決するとき、**FastAPI** はこの `checker` を次のように呼び出します:
```Python
checker(q="somequery")
```
...そして、その戻り値を *path operation 関数* 内の依存関係の値として、パラメータ `fixed_content_included` に渡します:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | 豆知識
ここまでの内容は回りくどく感じられるかもしれません。まだどのように役立つかが明確でないかもしれません。
これらの例は意図的に単純ですが、仕組みを示しています。
セキュリティの章では、同じやり方で実装されたユーティリティ関数があります。
ここまでを理解できていれば、そうしたセキュリティ用ユーティリティが内部でどのように動いているかも理解できています。
///
## `yield`、`HTTPException`、`except` とバックグラウンドタスクを伴う依存関係 { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | 注意
これらの技術的詳細は、ほとんどの場合は不要です。
主に、0.121.0 より前の FastAPI アプリケーションがあり、`yield` を使う依存関係で問題が発生している場合に有用です。
///
`yield` を使う依存関係は、さまざまなユースケースに対応し、いくつかの問題を修正するために時間とともに進化してきました。ここでは変更点の概要を説明します。
### `yield` と `scope` を伴う依存関係 { #dependencies-with-yield-and-scope }
バージョン 0.121.0 で、`yield` を使う依存関係に対して `Depends(scope="function")` がサポートされました。
`Depends(scope="function")` を使うと、`yield` の後の終了コードは、クライアントへレスポンスが返される前、*path operation 関数* が終了した直後に実行されます。
そして、`Depends(scope="request")`(デフォルト)を使う場合、`yield` の後の終了コードはレスポンス送信後に実行されます。
詳しくはドキュメント「[`yield` を使う依存関係 - 早期終了と `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope)」を参照してください。
### `yield` と `StreamingResponse` を伴う依存関係、技術詳細 { #dependencies-with-yield-and-streamingresponse-technical-details }
FastAPI 0.118.0 より前では、`yield` を使う依存関係を使用すると、*path operation 関数* が戻ってからレスポンス送信直前に終了コードが実行されていました。
これは、レスポンスがネットワーク上を移動するのを待っている間に、不要にリソースを保持しないようにする意図でした。
この変更により、`StreamingResponse` を返す場合、`yield` を持つ依存関係の終了コードはすでに実行されていることになりました。
たとえば、`yield` を持つ依存関係の中でデータベースセッションを持っていた場合、`StreamingResponse` はデータをストリーミングしている間にそのセッションを使えません。というのも、`yield` の後の終了コードでそのセッションがすでにクローズされているからです。
この挙動は 0.118.0 で元に戻され、`yield` の後の終了コードはレスポンス送信後に実行されるようになりました。
/// info | 情報
以下で見るように、これはバージョン 0.106.0 より前の挙動ととても似ていますが、いくつかのコーナーケースに対する改良とバグ修正が含まれています。
///
#### 早期終了コードのユースケース { #use-cases-with-early-exit-code }
特定の条件では、レスポンス送信前に `yield` を持つ依存関係の終了コードを実行する、古い挙動の恩恵を受けられるユースケースがあります。
例えば、`yield` を持つ依存関係でデータベースセッションを使ってユーザ検証だけを行い、その後は *path operation 関数* 内ではそのデータベースセッションを一切使わない、かつレスポンス送信に長い時間がかかる(例えばデータをゆっくり送る `StreamingResponse`)が、何らかの理由でデータベースは使わない、というケースです。
この場合、レスポンスの送信が終わるまでデータベースセッションが保持されますが、使わないのであれば保持する必要はありません。
次のようになります:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
終了コード、すなわち `Session` の自動クローズは:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...の部分で定義されており、遅いデータ送信が終わった後に実行されます:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
しかし、`generate_stream()` はデータベースセッションを使わないため、レスポンス送信中にセッションを開いたままにしておく必要は実際にはありません。
SQLModel(または SQLAlchemy)でこの特定のユースケースがある場合は、不要になった時点でセッションを明示的にクローズできます:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
このようにすると、セッションはデータベース接続を解放するため、他のリクエストがそれを使えるようになります。
`yield` を持つ依存関係で早期終了が必要な別のユースケースがある場合は、あなたの具体的なユースケースと、なぜ `yield` を持つ依存関係の早期クローズが有益かを説明して、[GitHub Discussion の質問](https://github.com/fastapi/fastapi/discussions/new?category=questions)を作成してください。
`yield` を持つ依存関係の早期クローズに納得できるユースケースがある場合は、早期クローズにオプトインする新しい方法を追加することを検討します。
### `yield` と `except` を伴う依存関係、技術詳細 { #dependencies-with-yield-and-except-technical-details }
FastAPI 0.110.0 より前では、`yield` を持つ依存関係を使い、その依存関係内で `except` によって例外を捕捉し、再度その例外を送出しなかった場合でも、その例外は自動的に送出(フォワード)され、任意の例外ハンドラまたは内部サーバエラーハンドラに渡されていました。
これは、ハンドラのないフォワードされた例外(内部サーバエラー)による未処理のメモリ消費を修正し、通常の Python コードの挙動と一貫性を持たせるため、バージョン 0.110.0 で変更されました。
### バックグラウンドタスクと `yield` を伴う依存関係、技術詳細 { #background-tasks-and-dependencies-with-yield-technical-details }
FastAPI 0.106.0 より前では、`yield` の後で例外を送出することはできませんでした。`yield` を持つ依存関係の終了コードはレスポンス送信「後」に実行されるため、[例外ハンドラ](../tutorial/handling-errors.md#install-custom-exception-handlers)はすでに実行済みでした。
これは主に、依存関係が "yield" した同じオブジェクトをバックグラウンドタスク内で利用できるようにするための設計でした。終了コードはバックグラウンドタスク完了後に実行されるからです。
これは、レスポンスがネットワーク上を移動するのを待っている間にリソースを保持しないようにする意図で、FastAPI 0.106.0 で変更されました。
/// tip | 豆知識
加えて、バックグラウンドタスクは通常、独立したロジックの集合であり、(例えば専用のデータベース接続など)それ自身のリソースで個別に扱うべきです。
そのため、このやり方の方がコードはおそらくよりクリーンになります。
///
この挙動に依存していた場合は、バックグラウンドタスク用のリソースをバックグラウンドタスク内部で作成し、`yield` を持つ依存関係のリソースに依存しないデータだけを内部で使用するようにしてください。
例えば、同じデータベースセッションを使うのではなく、バックグラウンドタスク内で新しいデータベースセッションを作成し、この新しいセッションでデータベースからオブジェクトを取得します。そして、バックグラウンドタスク関数の引数としてデータベースのオブジェクト自体を渡すのではなく、そのオブジェクトの ID を渡し、バックグラウンドタスク関数内でもう一度そのオブジェクトを取得します。
================================================
FILE: docs/ja/docs/advanced/advanced-python-types.md
================================================
# 高度な Python の型 { #advanced-python-types }
Python の型を扱うときに役立つ追加のアイデアをいくつか紹介します。
## `Union` または `Optional` の利用 { #using-union-or-optional }
何らかの理由で `|` が使えない場合、たとえば型アノテーションではなく `response_model=` のような場所では、縦棒(`|`)の代わりに `typing` の `Union` を使えます。
例えば、`str` または `None` になり得ることを宣言できます:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` には、`None` を取り得ることを宣言するための短縮形として `Optional` もあります。
ここからは私のとても主観的な提案です:
- 🚨 `Optional[SomeType]` の使用は避けましょう
- 代わりに ✨ **`Union[SomeType, None]` を使いましょう** ✨。
どちらも等価で内部的には同一ですが、「optional(任意)」という語が値が任意だと誤解させやすく、実際の意味は「`None` を取り得る」であり、任意ではなく依然として必須である場合でもそうです。そのため `Optional` より `Union` を勧めます。
`Union[SomeType, None]` の方が意味がより明確だと思います。
これは用語や名前付けの話に過ぎませんが、その言葉があなたやチームメイトのコードの捉え方に影響します。
例として次の関数を見てみましょう:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
パラメータ `name` は `Optional[str]` と定義されていますが、任意ではありません。このパラメータなしで関数を呼び出すことはできません:
```Python
say_hi() # あっ、これはエラーになります!😱
```
`name` パラメータにはデフォルト値がないため、依然として必須(任意ではない)です。ただし、`name` は値として `None` を受け付けます:
```Python
say_hi(name=None) # これは動作します。None は有効です 🎉
```
朗報として、多くの場合は単純に `|` を使って型の Union を定義できます:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
したがって、通常は `Optional` や `Union` といった名前を気にする必要はありません。😎
================================================
FILE: docs/ja/docs/advanced/async-tests.md
================================================
# 非同期テスト { #async-tests }
これまでに、提供されている `TestClient` を使って **FastAPI** アプリケーションをテストする方法を見てきました。ここまでは、`async` 関数を使わない同期テストのみでした。
テストで非同期関数を使えると、たとえばデータベースへ非同期にクエリする場合などに便利です。非同期データベースライブラリを使いながら、FastAPI アプリにリクエストを送り、その後バックエンドが正しいデータをデータベースに書き込めたかを検証したい、といったケースを想像してください。
その方法を見ていきます。
## pytest.mark.anyio { #pytest-mark-anyio }
テスト内で非同期関数を呼び出したい場合、テスト関数自体も非同期である必要があります。AnyIO はこれを実現するための便利なプラグインを提供しており、特定のテスト関数を非同期で呼び出すことを指定できます。
## HTTPX { #httpx }
**FastAPI** アプリケーションが通常の `def` 関数を使っていても、その内側は依然として `async` アプリケーションです。
`TestClient` は、標準の pytest を使って通常の `def` のテスト関数から非同期の FastAPI アプリを呼び出すための「おまじない」を内部で行います。しかし、その「おまじない」はテスト関数自体が非同期の場合には機能しません。テストを非同期で実行すると、テスト関数内で `TestClient` は使えなくなります。
`TestClient` は [HTTPX](https://www.python-httpx.org) を基に作られており、幸いなことに API のテストには HTTPX を直接利用できます。
## 例 { #example }
簡単な例として、[大きなアプリケーション](../tutorial/bigger-applications.md) と [テスト](../tutorial/testing.md) で説明したものに似たファイル構成を考えます:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
`main.py` は次のようになります:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
`test_main.py` は `main.py` のテストを持ち、次のようになります:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## 実行 { #run-it }
テストはいつも通り次で実行できます:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## デプロイ { #deploy }
では、**コマンド1つ** でアプリをデプロイします:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
その後、アプリをデプロイします:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
// Go to the home directory
$ cd
// Create a directory for all your code projects
$ mkdir code
// Enter into that code directory
$ cd code
// Create a directory for this project
$ mkdir awesome-project
// Enter into that project directory
$ cd awesome-project
```
```console
$ cd ~/code/prisoner-of-azkaban
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
$ deactivate
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Now when you run python, it will find the package sirius installed in this virtual environment ✨
$ python main.py
I solemnly swear 🐺
```
## 代替手段 { #alternatives }
これは、あらゆる仕組みを**根本から**学ぶためのシンプルな入門ガイドです。
仮想環境、パッケージの依存関係(requirements)、プロジェクトの管理には、多くの**代替手段**があります。
準備が整い、パッケージの依存関係、仮想環境など**プロジェクト全体の管理**ツールを使いたいと考えたら、[uv](https://github.com/astral-sh/uv) を試してみることをおすすめします。
`uv` では以下のような多くのことができます:
* 異なるバージョンも含めた**Python のインストール**
* プロジェクトごとの**仮想環境**の管理
* **パッケージ**のインストール
* プロジェクトのパッケージの**依存関係やバージョン**の管理
* パッケージとそのバージョンの、依存関係を含めた**厳密な**組み合わせを保持し、これによって、本番環境で、開発環境と全く同じようにプロジェクトを実行できる(これは**locking**と呼ばれます)
* その他のさまざまな機能
## まとめ { #conclusion }
ここまで読みすべて理解したなら、世間の多くの開発者と比べて、仮想環境について**あなたはより多くのことを知っています**。🤓
これらの詳細を知ることは、将来、複雑に見える何かのデバッグにきっと役立つでしょう。しかし、その頃には、あなたは**そのすべての動作を根本から**理解しているでしょう。😎
================================================
FILE: docs/ja/llm-prompt.md
================================================
### Target language
Translate to Japanese (日本語).
Language code: ja.
### Grammar and tone
- Use polite, instructional Japanese (です/ます調).
- Keep the tone concise and technical (match existing Japanese FastAPI docs).
### Headings
- Follow the existing Japanese style: short, descriptive headings (often noun phrases), e.g. 「チェック」.
- Do not add a trailing period at the end of headings.
### Quotes
- Prefer Japanese corner brackets 「」 in normal prose when quoting a term.
- Do not change quotes inside inline code, code blocks, URLs, or file paths.
### Ellipsis
- Keep ellipsis style consistent with existing Japanese docs (commonly `...`).
- Never change `...` in code, URLs, or CLI examples.
### Preferred translations / glossary
Use the following preferred translations when they apply in documentation prose:
- request (HTTP): リクエスト
- response (HTTP): レスポンス
- path operation: path operation (do not translate)
### `///` admonitions
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.).
2) If a title is present, prefer these canonical titles:
- `/// note | 備考`
- `/// note | 技術詳細`
- `/// tip | 豆知識`
- `/// warning | 注意`
- `/// info | 情報`
- `/// check | 確認`
- `/// danger | 警告`
================================================
FILE: docs/ja/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/ko/docs/_llm-test.md
================================================
# LLM 테스트 파일 { #llm-test-file }
이 문서는 문서를 번역하는 LLM이 `scripts/translate.py`의 `general_prompt`와 `docs/{language code}/llm-prompt.md`의 언어별 프롬프트를 이해하는지 테스트합니다. 언어별 프롬프트는 `general_prompt`에 추가됩니다.
여기에 추가된 테스트는 언어별 프롬프트를 설계하는 모든 사람이 보게 됩니다.
사용 방법은 다음과 같습니다:
* 언어별 프롬프트 `docs/{language code}/llm-prompt.md`를 준비합니다.
* 이 문서를 원하는 대상 언어로 새로 번역합니다(예: `translate.py`의 `translate-page` 명령). 그러면 `docs/{language code}/docs/_llm-test.md` 아래에 번역이 생성됩니다.
* 번역에서 문제가 없는지 확인합니다.
* 필요하다면 언어별 프롬프트, 일반 프롬프트, 또는 영어 문서를 개선합니다.
* 그런 다음 번역에서 남아 있는 문제를 수동으로 수정해 좋은 번역이 되게 합니다.
* 좋은 번역을 둔 상태에서 다시 번역합니다. 이상적인 결과는 LLM이 더 이상 번역에 변경을 만들지 않는 것입니다. 이는 일반 프롬프트와 언어별 프롬프트가 가능한 한 최선이라는 뜻입니다(때때로 몇 가지 seemingly random 변경을 할 수 있는데, 그 이유는 [LLM은 결정론적 알고리즘이 아니기 때문](https://doublespeak.chat/#/handbook#deterministic-output)입니다).
테스트:
## 코드 스니펫 { #code-snippets }
//// tab | 테스트
다음은 코드 스니펫입니다: `foo`. 그리고 이것은 또 다른 코드 스니펫입니다: `bar`. 그리고 또 하나: `baz quux`.
////
//// tab | 정보
코드 스니펫의 내용은 그대로 두어야 합니다.
`scripts/translate.py`의 일반 프롬프트에서 `### Content of code snippets` 섹션을 참고하세요.
////
## 따옴표 { #quotes }
//// tab | 테스트
어제 제 친구가 이렇게 썼습니다: "If you spell incorrectly correctly, you have spelled it incorrectly". 이에 저는 이렇게 답했습니다: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'".
/// note | 참고
LLM은 아마 이것을 잘못 번역할 것입니다. 흥미로운 점은 재번역할 때 고정된 번역을 유지하는지 여부뿐입니다.
///
////
//// tab | 정보
프롬프트 설계자는 중립 따옴표를 타이포그래피 따옴표로 변환할지 선택할 수 있습니다. 그대로 두어도 괜찮습니다.
예를 들어 `docs/de/llm-prompt.md`의 `### Quotes` 섹션을 참고하세요.
////
## 코드 스니펫의 따옴표 { #quotes-in-code-snippets }
//// tab | 테스트
`pip install "foo[bar]"`
코드 스니펫에서 문자열 리터럴의 예: `"this"`, `'that'`.
코드 스니펫에서 문자열 리터럴의 어려운 예: `f"I like {'oranges' if orange else "apples"}"`
하드코어: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | 정보
... 하지만 코드 스니펫 안의 따옴표는 그대로 유지되어야 합니다.
////
## 코드 블록 { #code-blocks }
//// tab | 테스트
Bash 코드 예시...
```bash
# 우주에 인사말 출력
echo "Hello universe"
```
...그리고 콘솔 코드 예시...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...그리고 또 다른 콘솔 코드 예시...
```console
// "Code" 디렉터리 생성
$ mkdir code
// 해당 디렉터리로 이동
$ cd code
```
...그리고 Python 코드 예시...
```Python
wont_work() # 이건 동작하지 않습니다 😱
works(foo="bar") # 이건 동작합니다 🎉
```
...이상입니다.
////
//// tab | 정보
코드 블록의 코드는(주석을 제외하고) 수정하면 안 됩니다.
`scripts/translate.py`의 일반 프롬프트에서 `### Content of code blocks` 섹션을 참고하세요.
////
## 탭과 색상 박스 { #tabs-and-colored-boxes }
//// tab | 테스트
/// info | 정보
일부 텍스트
///
/// note | 참고
일부 텍스트
///
/// note | 기술 세부사항
일부 텍스트
///
/// check | 확인
일부 텍스트
///
/// tip | 팁
일부 텍스트
///
/// warning | 경고
일부 텍스트
///
/// danger | 위험
일부 텍스트
///
////
//// tab | 정보
탭과 `Info`/`Note`/`Warning`/등의 블록은 제목 번역을 수직 막대(`|`) 뒤에 추가해야 합니다.
`scripts/translate.py`의 일반 프롬프트에서 `### Special blocks`와 `### Tab blocks` 섹션을 참고하세요.
////
## 웹 및 내부 링크 { #web-and-internal-links }
//// tab | 테스트
링크 텍스트는 번역되어야 하고, 링크 주소는 변경되지 않아야 합니다:
* [위의 제목으로 가는 링크](#code-snippets)
* [내부 링크](index.md#installation)
* [외부 링크](https://sqlmodel.tiangolo.com/)
* [스타일로 가는 링크](https://fastapi.tiangolo.com/css/styles.css)
* [스크립트로 가는 링크](https://fastapi.tiangolo.com/js/logic.js)
* [이미지로 가는 링크](https://fastapi.tiangolo.com/img/foo.jpg)
링크 텍스트는 번역되어야 하고, 링크 주소는 번역 페이지를 가리켜야 합니다:
* [FastAPI 링크](https://fastapi.tiangolo.com/ko/)
////
//// tab | 정보
링크는 번역되어야 하지만, 주소는 변경되지 않아야 합니다. 예외는 FastAPI 문서 페이지로 향하는 절대 링크이며, 이 경우 번역 페이지로 연결되어야 합니다.
`scripts/translate.py`의 일반 프롬프트에서 `### Links` 섹션을 참고하세요.
////
## HTML "abbr" 요소 { #html-abbr-elements }
//// tab | 테스트
여기 HTML "abbr" 요소로 감싼 몇 가지가 있습니다(일부는 임의로 만든 것입니다):
### abbr가 전체 문구를 제공 { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### abbr가 전체 문구와 설명을 제공 { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | 정보
"abbr" 요소의 "title" 속성은 몇 가지 구체적인 지침에 따라 번역됩니다.
번역에서는(영어 단어를 설명하기 위해) 자체 "abbr" 요소를 추가할 수 있으며, LLM은 이를 제거하면 안 됩니다.
`scripts/translate.py`의 일반 프롬프트에서 `### HTML abbr elements` 섹션을 참고하세요.
////
## HTML "dfn" 요소 { #html-dfn-elements }
* 클러스터
* 딥 러닝
## 제목 { #headings }
//// tab | 테스트
### 웹앱 개발하기 - 튜토리얼 { #develop-a-webapp-a-tutorial }
안녕하세요.
### 타입 힌트와 -애너테이션 { #type-hints-and-annotations }
다시 안녕하세요.
### super- 및 subclasses { #super-and-subclasses }
다시 안녕하세요.
////
//// tab | 정보
제목에 대한 유일한 강한 규칙은, LLM이 중괄호 안의 해시 부분을 변경하지 않아 링크가 깨지지 않게 하는 것입니다.
`scripts/translate.py`의 일반 프롬프트에서 `### Headings` 섹션을 참고하세요.
언어별 지침은 예를 들어 `docs/de/llm-prompt.md`의 `### Headings` 섹션을 참고하세요.
////
## 문서에서 사용되는 용어 { #terms-used-in-the-docs }
//// tab | 테스트
* 여러분
* 여러분의
* 예:
* 등
* `foo`로서의 `int`
* `bar`로서의 `str`
* `baz`로서의 `list`
* 튜토리얼 - 사용자 가이드
* 고급 사용자 가이드
* SQLModel 문서
* API 문서
* 자동 문서
* Data Science
* Deep Learning
* Machine Learning
* Dependency Injection
* HTTP Basic authentication
* HTTP Digest
* ISO format
* JSON Schema 표준
* JSON schema
* schema definition
* Password Flow
* Mobile
* deprecated
* designed
* invalid
* on the fly
* standard
* default
* case-sensitive
* case-insensitive
* 애플리케이션을 서빙하다
* 페이지를 서빙하다
* 앱
* 애플리케이션
* 요청
* 응답
* 오류 응답
* 경로 처리
* 경로 처리 데코레이터
* 경로 처리 함수
* body
* 요청 body
* 응답 body
* JSON body
* form body
* file body
* 함수 body
* parameter
* body parameter
* path parameter
* query parameter
* cookie parameter
* header parameter
* form parameter
* function parameter
* event
* startup event
* 서버 startup
* shutdown event
* lifespan event
* handler
* event handler
* exception handler
* 처리하다
* model
* Pydantic model
* data model
* database model
* form model
* model object
* class
* base class
* parent class
* subclass
* child class
* sibling class
* class method
* header
* headers
* authorization header
* `Authorization` header
* forwarded header
* dependency injection system
* dependency
* dependable
* dependant
* I/O bound
* CPU bound
* concurrency
* parallelism
* multiprocessing
* env var
* environment variable
* `PATH`
* `PATH` variable
* authentication
* authentication provider
* authorization
* authorization form
* authorization provider
* 사용자가 인증한다
* 시스템이 사용자를 인증한다
* CLI
* command line interface
* server
* client
* cloud provider
* cloud service
* development
* development stages
* dict
* dictionary
* enumeration
* enum
* enum member
* encoder
* decoder
* encode하다
* decode하다
* exception
* raise하다
* expression
* statement
* frontend
* backend
* GitHub discussion
* GitHub issue
* performance
* performance optimization
* return type
* return value
* security
* security scheme
* task
* background task
* task function
* template
* template engine
* type annotation
* type hint
* server worker
* Uvicorn worker
* Gunicorn Worker
* worker process
* worker class
* workload
* deployment
* deploy하다
* SDK
* software development kit
* `APIRouter`
* `requirements.txt`
* Bearer Token
* breaking change
* bug
* button
* callable
* code
* commit
* context manager
* coroutine
* database session
* disk
* domain
* engine
* fake X
* HTTP GET method
* item
* library
* lifespan
* lock
* middleware
* mobile application
* module
* mounting
* network
* origin
* override
* payload
* processor
* property
* proxy
* pull request
* query
* RAM
* remote machine
* status code
* string
* tag
* web framework
* wildcard
* return하다
* validate하다
////
//// tab | 정보
이것은 문서에서 보이는 (대부분) 기술 용어의 불완전하고 비규범적인 목록입니다. 프롬프트 설계자가 어떤 용어에 대해 LLM에 추가적인 도움이 필요한지 파악하는 데 유용할 수 있습니다. 예를 들어, 좋은 번역을 계속 덜 좋은 번역으로 되돌릴 때, 또는 언어에서 용어의 활용/변화를 처리하는 데 문제가 있을 때 도움이 됩니다.
예를 들어 `docs/de/llm-prompt.md`의 `### List of English terms and their preferred German translations` 섹션을 참고하세요.
////
================================================
FILE: docs/ko/docs/about/index.md
================================================
# 소개 { #about }
FastAPI, 그 디자인, 영감 등에 대해 🤓
================================================
FILE: docs/ko/docs/advanced/additional-responses.md
================================================
# OpenAPI에서 추가 응답 { #additional-responses-in-openapi }
/// warning | 경고
이는 꽤 고급 주제입니다.
**FastAPI**를 막 시작했다면, 이 내용이 필요 없을 수도 있습니다.
///
추가 상태 코드, 미디어 타입, 설명 등을 포함한 추가 응답을 선언할 수 있습니다.
이러한 추가 응답은 OpenAPI 스키마에 포함되므로 API 문서에도 표시됩니다.
하지만 이러한 추가 응답의 경우, 상태 코드와 콘텐츠를 포함하여 `JSONResponse` 같은 `Response`를 직접 반환하도록 반드시 처리해야 합니다.
## `model`을 사용한 추가 응답 { #additional-response-with-model }
*경로 처리 데코레이터*에 `responses` 파라미터를 전달할 수 있습니다.
이는 `dict`를 받습니다. 키는 각 응답의 상태 코드(예: `200`)이고, 값은 각 응답에 대한 정보를 담은 다른 `dict`입니다.
각 응답 `dict`에는 `response_model`처럼 Pydantic 모델을 담는 `model` 키가 있을 수 있습니다.
**FastAPI**는 그 모델을 사용해 JSON Schema를 생성하고, OpenAPI의 올바른 위치에 포함합니다.
예를 들어, 상태 코드 `404`와 Pydantic 모델 `Message`를 사용하는 다른 응답을 선언하려면 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | 참고
`JSONResponse`를 직접 반환해야 한다는 점을 기억하세요.
///
/// info | 정보
`model` 키는 OpenAPI의 일부가 아닙니다.
**FastAPI**는 여기에서 Pydantic 모델을 가져와 JSON Schema를 생성하고 올바른 위치에 넣습니다.
올바른 위치는 다음과 같습니다:
* 값으로 또 다른 JSON 객체(`dict`)를 가지는 `content` 키 안에:
* 미디어 타입(예: `application/json`)을 키로 가지며, 값으로 또 다른 JSON 객체를 포함하고:
* `schema` 키가 있고, 그 값이 모델에서 생성된 JSON Schema입니다. 이것이 올바른 위치입니다.
* **FastAPI**는 이를 직접 포함하는 대신, OpenAPI의 다른 위치에 있는 전역 JSON Schemas를 참조하도록 여기에서 reference를 추가합니다. 이렇게 하면 다른 애플리케이션과 클라이언트가 그 JSON Schema를 직접 사용할 수 있고, 더 나은 코드 생성 도구 등을 제공할 수 있습니다.
///
이 *경로 처리*에 대해 OpenAPI에 생성되는 응답은 다음과 같습니다:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
스키마는 OpenAPI 스키마 내부의 다른 위치를 참조합니다:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## 주요 응답에 대한 추가 미디어 타입 { #additional-media-types-for-the-main-response }
같은 `responses` 파라미터를 사용해 동일한 주요 응답에 대해 다른 미디어 타입을 추가할 수도 있습니다.
예를 들어, *경로 처리*가 JSON 객체(미디어 타입 `application/json`) 또는 PNG 이미지(미디어 타입 `image/png`)를 반환할 수 있다고 선언하기 위해 `image/png`라는 추가 미디어 타입을 추가할 수 있습니다:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | 참고
이미지는 `FileResponse`를 사용해 직접 반환해야 한다는 점에 유의하세요.
///
/// info | 정보
`responses` 파라미터에서 다른 미디어 타입을 명시적으로 지정하지 않는 한, FastAPI는 응답이 주요 응답 클래스와 동일한 미디어 타입(기본값 `application/json`)을 가진다고 가정합니다.
하지만 커스텀 응답 클래스를 지정하면서 미디어 타입을 `None`으로 설정했다면, FastAPI는 연결된 모델이 있는 모든 추가 응답에 대해 `application/json`을 사용합니다.
///
## 정보 결합하기 { #combining-information }
`response_model`, `status_code`, `responses` 파라미터를 포함해 여러 위치의 응답 정보를 결합할 수도 있습니다.
기본 상태 코드 `200`(또는 필요하다면 커스텀 코드)을 사용하여 `response_model`을 선언하고, 그와 동일한 응답에 대한 추가 정보를 `responses`에서 OpenAPI 스키마에 직접 선언할 수 있습니다.
**FastAPI**는 `responses`의 추가 정보를 유지하고, 모델의 JSON Schema와 결합합니다.
예를 들어, Pydantic 모델을 사용하고 커스텀 `description`을 가진 상태 코드 `404` 응답을 선언할 수 있습니다.
또한 `response_model`을 사용하는 상태 코드 `200` 응답을 선언하되, 커스텀 `example`을 포함할 수도 있습니다:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
이 모든 내용은 OpenAPI에 결합되어 포함되고, API 문서에 표시됩니다:
## 미리 정의된 응답과 커스텀 응답 결합하기 { #combine-predefined-responses-and-custom-ones }
여러 *경로 처리*에 적용되는 미리 정의된 응답이 필요할 수도 있지만, 각 *경로 처리*마다 필요한 커스텀 응답과 결합하고 싶을 수도 있습니다.
그런 경우 Python의 `dict` “unpacking” 기법인 `**dict_to_unpack`을 사용할 수 있습니다:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
여기서 `new_dict`는 `old_dict`의 모든 키-값 쌍에 더해 새 키-값 쌍까지 포함합니다:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
이 기법을 사용해 *경로 처리*에서 일부 미리 정의된 응답을 재사용하고, 추가 커스텀 응답과 결합할 수 있습니다.
예를 들어:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## OpenAPI 응답에 대한 추가 정보 { #more-information-about-openapi-responses }
응답에 정확히 무엇을 포함할 수 있는지 보려면, OpenAPI 사양의 다음 섹션을 확인하세요:
* [OpenAPI Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object): `Response Object`를 포함합니다.
* [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object): `responses` 파라미터 안의 각 응답에 이것의 어떤 항목이든 직접 포함할 수 있습니다. `description`, `headers`, `content`(여기에서 서로 다른 미디어 타입과 JSON Schema를 선언합니다), `links` 등을 포함할 수 있습니다.
================================================
FILE: docs/ko/docs/advanced/additional-status-codes.md
================================================
# 추가 상태 코드 { #additional-status-codes }
기본적으로 **FastAPI**는 응답을 `JSONResponse`를 사용하여 반환하며, *경로 처리*에서 반환한 내용을 해당 `JSONResponse` 안에 넣어 반환합니다.
기본 상태 코드 또는 *경로 처리*에서 설정한 상태 코드를 사용합니다.
## 추가 상태 코드 { #additional-status-codes_1 }
기본 상태 코드와 별도로 추가 상태 코드를 반환하려면 `JSONResponse`와 같이 `Response`를 직접 반환하고 추가 상태 코드를 직접 설정할 수 있습니다.
예를 들어 항목을 업데이트할 수 있는 *경로 처리*가 있고 성공 시 200 “OK”의 HTTP 상태 코드를 반환한다고 가정해 보겠습니다.
하지만 새로운 항목을 허용하기를 원할 것입니다. 그리고 항목이 이전에 존재하지 않았다면 이를 생성하고 HTTP 상태 코드 201 "Created"를 반환합니다.
이를 위해서는 `JSONResponse`를 가져와서 원하는 `status_code`를 설정하여 콘텐츠를 직접 반환합니다:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning
위의 예제처럼 `Response`를 직접 반환하면 바로 반환됩니다.
모델 등과 함께 직렬화되지 않습니다.
원하는 데이터가 있는지, 값이 유효한 JSON인지 확인합니다(`JSONResponse`를 사용하는 경우).
///
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자 여러분을 위한 편의성으로 `fastapi.responses`와 동일한 `starlette.responses`를 제공합니다. 그러나 사용 가능한 응답의 대부분은 Starlette에서 직접 제공됩니다. `status` 또한 마찬가지입니다.
///
## OpenAPI 및 API 문서 { #openapi-and-api-docs }
추가 상태 코드와 응답을 직접 반환하는 경우, FastAPI는 반환할 내용을 미리 알 수 있는 방법이 없기 때문에 OpenAPI 스키마(API 문서)에 포함되지 않습니다.
하지만 다음을 사용하여 코드에 이를 문서화할 수 있습니다: [추가 응답](additional-responses.md).
================================================
FILE: docs/ko/docs/advanced/advanced-dependencies.md
================================================
# 고급 의존성 { #advanced-dependencies }
## 매개변수화된 의존성 { #parameterized-dependencies }
지금까지 본 모든 의존성은 고정된 함수 또는 클래스입니다.
하지만 여러 개의 함수나 클래스를 선언하지 않고도 의존성에 매개변수를 설정해야 하는 경우가 있을 수 있습니다.
예를 들어, `q` 쿼리 매개변수가 특정 고정된 내용을 포함하고 있는지 확인하는 의존성을 원한다고 가정해 봅시다.
이때 해당 고정된 내용을 매개변수화할 수 있길 바랍니다.
## "호출 가능한" 인스턴스 { #a-callable-instance }
Python에는 클래스의 인스턴스를 "호출 가능"하게 만드는 방법이 있습니다.
클래스 자체(이미 호출 가능함)가 아니라 해당 클래스의 인스턴스에 대해 호출 가능하게 하는 것입니다.
이를 위해 `__call__` 메서드를 선언합니다:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
이 경우, **FastAPI**는 추가 매개변수와 하위 의존성을 확인하기 위해 `__call__`을 사용하게 되며,
나중에 *경로 처리 함수*에서 매개변수에 값을 전달할 때 이를 호출하게 됩니다.
## 인스턴스 매개변수화하기 { #parameterize-the-instance }
이제 `__init__`을 사용하여 의존성을 "매개변수화"할 수 있는 인스턴스의 매개변수를 선언할 수 있습니다:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
이 경우, **FastAPI**는 `__init__`에 전혀 관여하지 않으며, 우리는 이 메서드를 코드에서 직접 사용하게 됩니다.
## 인스턴스 생성하기 { #create-an-instance }
다음과 같이 이 클래스의 인스턴스를 생성할 수 있습니다:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
이렇게 하면 `checker.fixed_content` 속성에 `"bar"`라는 값을 담아 의존성을 "매개변수화"할 수 있습니다.
## 인스턴스를 의존성으로 사용하기 { #use-the-instance-as-a-dependency }
그런 다음, 클래스 자체가 아닌 인스턴스 `checker`가 의존성이 되므로, `Depends(FixedContentQueryChecker)` 대신 `Depends(checker)`에서 이 `checker` 인스턴스를 사용할 수 있습니다.
의존성을 해결할 때 **FastAPI**는 이 `checker`를 다음과 같이 호출합니다:
```Python
checker(q="somequery")
```
...그리고 이때 반환되는 값을 *경로 처리 함수*의 의존성 값으로, `fixed_content_included` 매개변수에 전달합니다:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | 팁
이 모든 과정이 복잡하게 느껴질 수 있습니다. 그리고 지금은 이 방법이 얼마나 유용한지 명확하지 않을 수도 있습니다.
이 예시는 의도적으로 간단하게 만들었지만, 전체 구조가 어떻게 작동하는지 보여줍니다.
보안 관련 장에서는 이와 같은 방식으로 구현된 유틸리티 함수들이 있습니다.
이 모든 과정을 이해했다면, 이러한 보안용 유틸리티 도구들이 내부적으로 어떻게 작동하는지 이미 파악한 것입니다.
///
## `yield`, `HTTPException`, `except`, 백그라운드 태스크가 있는 의존성 { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | 경고
대부분의 경우 이러한 기술 세부사항이 필요하지 않을 것입니다.
이 세부사항은 주로 0.121.0 이전의 FastAPI 애플리케이션이 있고 `yield`가 있는 의존성에서 문제가 발생하는 경우에 유용합니다.
///
`yield`가 있는 의존성은 여러 사용 사례를 수용하고 일부 문제를 해결하기 위해 시간이 지나며 발전해 왔습니다. 다음은 변경된 내용의 요약입니다.
### `yield`와 `scope`가 있는 의존성 { #dependencies-with-yield-and-scope }
0.121.0 버전에서 FastAPI는 `Depends(scope="function")` 지원을 추가했습니다.
`Depends(scope="function")`를 사용하면, `yield` 이후의 종료 코드는 *경로 처리 함수*가 끝난 직후(클라이언트에 응답이 반환되기 전)에 실행됩니다.
그리고 `Depends(scope="request")`(기본값)를 사용하면, `yield` 이후의 종료 코드는 응답이 전송된 후에 실행됩니다.
자세한 내용은 [`yield`가 있는 의존성 - 조기 종료와 `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 문서를 참고하세요.
### `yield`가 있는 의존성과 `StreamingResponse`, 기술 세부사항 { #dependencies-with-yield-and-streamingresponse-technical-details }
FastAPI 0.118.0 이전에는 `yield`가 있는 의존성을 사용하면, *경로 처리 함수*가 반환된 뒤 응답을 보내기 직전에 `yield` 이후의 종료 코드가 실행되었습니다.
의도는 응답이 네트워크를 통해 전달되기를 기다리면서 필요한 것보다 더 오래 리소스를 점유하지 않도록 하는 것이었습니다.
이 변경은 `StreamingResponse`를 반환하는 경우에도 `yield`가 있는 의존성의 종료 코드가 이미 실행된다는 의미이기도 했습니다.
예를 들어, `yield`가 있는 의존성에 데이터베이스 세션이 있다면, `StreamingResponse`는 데이터를 스트리밍하는 동안 해당 세션을 사용할 수 없게 됩니다. `yield` 이후의 종료 코드에서 세션이 이미 닫혔기 때문입니다.
이 동작은 0.118.0에서 되돌려져, `yield` 이후의 종료 코드가 응답이 전송된 뒤 실행되도록 변경되었습니다.
/// info | 정보
아래에서 보시겠지만, 이는 0.106.0 버전 이전의 동작과 매우 비슷하지만, 여러 개선 사항과 코너 케이스에 대한 버그 수정이 포함되어 있습니다.
///
#### 종료 코드를 조기에 실행하는 사용 사례 { #use-cases-with-early-exit-code }
특정 조건의 일부 사용 사례에서는 응답을 보내기 전에 `yield`가 있는 의존성의 종료 코드를 실행하던 예전 동작이 도움이 될 수 있습니다.
예를 들어, `yield`가 있는 의존성에서 데이터베이스 세션을 사용해 사용자를 검증만 하고, *경로 처리 함수*에서는 그 데이터베이스 세션을 다시는 사용하지 않으며(의존성에서만 사용), **그리고** 응답을 전송하는 데 오랜 시간이 걸리는 경우를 생각해 봅시다. 예를 들어 데이터를 천천히 보내는 `StreamingResponse`인데, 어떤 이유로든 데이터베이스를 사용하지는 않는 경우입니다.
이 경우 데이터베이스 세션은 응답 전송이 끝날 때까지 유지되지만, 사용하지 않는다면 굳이 유지할 필요가 없습니다.
다음과 같이 보일 수 있습니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
다음에서 `Session`을 자동으로 닫는 종료 코드는:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...응답이 느린 데이터 전송을 마친 뒤에 실행됩니다:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
하지만 `generate_stream()`는 데이터베이스 세션을 사용하지 않으므로, 응답을 전송하는 동안 세션을 열린 채로 유지할 필요는 없습니다.
SQLModel(또는 SQLAlchemy)을 사용하면서 이런 특정 사용 사례가 있다면, 더 이상 필요하지 않을 때 세션을 명시적으로 닫을 수 있습니다:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
그러면 세션이 데이터베이스 연결을 해제하여, 다른 요청들이 이를 사용할 수 있게 됩니다.
`yield`가 있는 의존성에서 조기 종료가 필요한 다른 사용 사례가 있다면, 여러분의 구체적인 사용 사례와 `yield`가 있는 의존성에 대한 조기 종료가 어떤 점에서 이득이 되는지를 포함해 [GitHub Discussions 질문](https://github.com/fastapi/fastapi/discussions/new?category=questions)을 생성해 주세요.
`yield`가 있는 의존성에서 조기 종료에 대한 설득력 있는 사용 사례가 있다면, 조기 종료를 선택적으로 활성화할 수 있는 새로운 방법을 추가하는 것을 고려하겠습니다.
### `yield`가 있는 의존성과 `except`, 기술 세부사항 { #dependencies-with-yield-and-except-technical-details }
FastAPI 0.110.0 이전에는 `yield`가 있는 의존성을 사용한 다음 그 의존성에서 `except`로 예외를 잡고, 예외를 다시 발생시키지 않으면, 예외가 자동으로 어떤 예외 핸들러 또는 내부 서버 오류 핸들러로 raise/forward 되었습니다.
이는 핸들러 없이 전달된 예외(내부 서버 오류)로 인해 처리되지 않은 메모리 사용이 발생하는 문제를 수정하고, 일반적인 Python 코드의 동작과 일관되게 하기 위해 0.110.0 버전에서 변경되었습니다.
### 백그라운드 태스크와 `yield`가 있는 의존성, 기술 세부사항 { #background-tasks-and-dependencies-with-yield-technical-details }
FastAPI 0.106.0 이전에는 `yield` 이후에 예외를 발생시키는 것이 불가능했습니다. `yield`가 있는 의존성의 종료 코드는 응답이 전송된 *후에* 실행되었기 때문에, [예외 핸들러](../tutorial/handling-errors.md#install-custom-exception-handlers)가 이미 실행된 뒤였습니다.
이는 주로 백그라운드 태스크 안에서 의존성이 "yield"한 동일한 객체들을 사용할 수 있게 하기 위한 설계였습니다. 백그라운드 태스크가 끝난 뒤에 종료 코드가 실행되었기 때문입니다.
이는 응답이 네트워크를 통해 전달되기를 기다리는 동안 리소스를 점유하지 않기 위한 의도로 FastAPI 0.106.0에서 변경되었습니다.
/// tip | 팁
추가로, 백그라운드 태스크는 보통 별도의 리소스(예: 자체 데이터베이스 연결)를 가지고 따로 처리되어야 하는 독립적인 로직 집합입니다.
따라서 이 방식이 코드를 더 깔끔하게 만들어줄 가능성이 큽니다.
///
이 동작에 의존하던 경우라면, 이제는 백그라운드 태스크를 위한 리소스를 백그라운드 태스크 내부에서 생성하고, 내부적으로는 `yield`가 있는 의존성의 리소스에 의존하지 않는 데이터만 사용해야 합니다.
예를 들어, 동일한 데이터베이스 세션을 사용하는 대신, 백그라운드 태스크 내부에서 새 데이터베이스 세션을 생성하고, 이 새 세션을 사용해 데이터베이스에서 객체를 가져오면 됩니다. 그리고 데이터베이스에서 가져온 객체를 백그라운드 태스크 함수의 매개변수로 전달하는 대신, 해당 객체의 ID를 전달한 다음 백그라운드 태스크 함수 내부에서 객체를 다시 가져오면 됩니다.
================================================
FILE: docs/ko/docs/advanced/advanced-python-types.md
================================================
# 고급 Python 타입 { #advanced-python-types }
Python 타입을 다룰 때 유용할 수 있는 몇 가지 추가 아이디어를 소개합니다.
## `Union` 또는 `Optional` 사용 { #using-union-or-optional }
어떤 이유로 코드에서 `|`를 사용할 수 없다면, 예를 들어 타입 어노테이션이 아니라 `response_model=` 같은 곳이라면, 파이프 문자(`|`) 대신 `typing`의 `Union`을 사용할 수 있습니다.
예를 들어, 어떤 값이 `str` 또는 `None`이 될 수 있다고 선언할 수 있습니다:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing`에는 `None`이 될 수 있음을 선언하는 축약형으로 `Optional`도 있습니다.
아주 개인적인 관점에서의 팁입니다:
- 🚨 `Optional[SomeType]` 사용은 피하세요
- 대신 ✨ **`Union[SomeType, None]`를 사용하세요** ✨.
둘은 동등하며 내부적으로도 같습니다. 하지만 단어 "optional"은 값이 선택 사항이라는 인상을 주는 반면, 실제 의미는 "값이 `None`이 될 수 있다"는 뜻입니다. 값이 선택 사항이 아니라 여전히 필수인 경우에도 그렇습니다.
`Union[SomeType, None]`가 의미를 더 명확하게 드러낸다고 생각합니다.
이는 단지 단어와 명칭의 문제입니다. 하지만 이런 단어가 여러분과 팀원이 코드를 어떻게 생각하는지에 영향을 줄 수 있습니다.
예를 들어, 다음 함수를 보세요:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
매개변수 `name`은 `Optional[str]`로 정의되어 있지만, 사실 선택적이지 않습니다. 이 매개변수 없이 함수를 호출할 수 없습니다:
```Python
say_hi() # 이런, 에러가 발생합니다! 😱
```
`name` 매개변수는 기본값이 없기 때문에 여전히 필수입니다(선택적이 아님). 대신, `name`에는 `None`을 전달할 수 있습니다:
```Python
say_hi(name=None) # 작동합니다. None은 유효합니다 🎉
```
좋은 소식은, 대부분의 경우 타입의 합집합을 정의할 때 그냥 `|`를 사용할 수 있다는 점입니다:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
그래서 보통은 `Optional`과 `Union` 같은 이름에 대해 걱정하지 않으셔도 됩니다. 😎
================================================
FILE: docs/ko/docs/advanced/async-tests.md
================================================
# 비동기 테스트 { #async-tests }
제공된 `TestClient`를 사용하여 **FastAPI** 애플리케이션을 테스트하는 방법을 이미 살펴보았습니다. 지금까지는 `async` 함수를 사용하지 않고, 동기 테스트를 작성하는 방법만 보았습니다.
테스트에서 비동기 함수를 사용할 수 있으면 유용할 수 있습니다. 예를 들어 데이터베이스를 비동기로 쿼리하는 경우를 생각해 보세요. FastAPI 애플리케이션에 요청을 보낸 다음, async 데이터베이스 라이브러리를 사용하면서 백엔드가 데이터베이스에 올바른 데이터를 성공적으로 기록했는지 검증하고 싶을 수 있습니다.
어떻게 동작하게 만들 수 있는지 살펴보겠습니다.
## pytest.mark.anyio { #pytest-mark-anyio }
테스트에서 비동기 함수를 호출하려면, 테스트 함수도 비동기여야 합니다. AnyIO는 이를 위한 깔끔한 플러그인을 제공하며, 일부 테스트 함수를 비동기로 호출하도록 지정할 수 있습니다.
## HTTPX { #httpx }
**FastAPI** 애플리케이션이 `async def` 대신 일반 `def` 함수를 사용하더라도, 내부적으로는 여전히 `async` 애플리케이션입니다.
`TestClient`는 표준 pytest를 사용하여, 일반 `def` 테스트 함수 안에서 비동기 FastAPI 애플리케이션을 호출하도록 내부에서 마법 같은 처리를 합니다. 하지만 비동기 함수 안에서 이를 사용하면 그 마법은 더 이상 동작하지 않습니다. 테스트를 비동기로 실행하면, 테스트 함수 안에서 `TestClient`를 더 이상 사용할 수 없습니다.
`TestClient`는 [HTTPX](https://www.python-httpx.org)를 기반으로 하며, 다행히 HTTPX를 직접 사용해 API를 테스트할 수 있습니다.
## 예시 { #example }
간단한 예시로, [더 큰 애플리케이션](../tutorial/bigger-applications.md)과 [테스트](../tutorial/testing.md)에서 설명한 것과 비슷한 파일 구조를 살펴보겠습니다:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
`main.py` 파일은 다음과 같습니다:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
`test_main.py` 파일에는 `main.py`에 대한 테스트가 있으며, 이제 다음과 같이 보일 수 있습니다:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## 실행하기 { #run-it }
다음과 같이 평소처럼 테스트를 실행할 수 있습니다:
```console
$ pytest
---> 100%
```
## 자세히 보기 { #in-detail }
`@pytest.mark.anyio` 마커는 pytest에게 이 테스트 함수가 비동기로 호출되어야 한다고 알려줍니다:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | 팁
`TestClient`를 사용할 때처럼 단순히 `def`가 아니라, 이제 테스트 함수가 `async def`라는 점에 주목하세요.
///
그 다음 앱으로 `AsyncClient`를 만들고, `await`를 사용해 비동기 요청을 보낼 수 있습니다.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
이는 다음과 동등합니다:
```Python
response = client.get('/')
```
`TestClient`로 요청을 보내기 위해 사용하던 코드입니다.
/// tip | 팁
새 `AsyncClient`와 함께 async/await를 사용하고 있다는 점에 주목하세요. 요청은 비동기입니다.
///
/// warning | 경고
애플리케이션이 lifespan 이벤트에 의존한다면, `AsyncClient`는 이러한 이벤트를 트리거하지 않습니다. 이벤트가 트리거되도록 하려면 [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage)의 `LifespanManager`를 사용하세요.
///
## 기타 비동기 함수 호출 { #other-asynchronous-function-calls }
테스트 함수가 이제 비동기이므로, 테스트에서 FastAPI 애플리케이션에 요청을 보내는 것 외에도 다른 `async` 함수를 코드의 다른 곳에서 호출하듯이 동일하게 호출하고 (`await`) 사용할 수도 있습니다.
/// tip | 팁
테스트에 비동기 함수 호출을 통합할 때(예: [MongoDB의 MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)를 사용할 때) `RuntimeError: Task attached to a different loop`를 마주친다면, 이벤트 루프가 필요한 객체는 async 함수 안에서만 인스턴스화해야 한다는 점을 기억하세요. 예를 들어 `@app.on_event("startup")` 콜백에서 인스턴스화할 수 있습니다.
///
================================================
FILE: docs/ko/docs/advanced/behind-a-proxy.md
================================================
# 프록시 뒤에서 실행하기 { #behind-a-proxy }
많은 경우 FastAPI 앱 앞단에 Traefik이나 Nginx 같은 **프록시(proxy)**를 두고 사용합니다.
이런 프록시는 HTTPS 인증서 처리 등 여러 작업을 담당할 수 있습니다.
## 프록시 전달 헤더 { #proxy-forwarded-headers }
애플리케이션 앞단의 **프록시**는 보통 **서버**로 요청을 보내기 전에, 해당 요청이 프록시에 의해 **전달(forwarded)**되었다는 것을 서버가 알 수 있도록 몇몇 헤더를 동적으로 설정합니다. 이를 통해 서버는 도메인을 포함한 원래의 (공개) URL, HTTPS 사용 여부 등 정보를 알 수 있습니다.
**서버** 프로그램(예: **FastAPI CLI**를 통해 실행되는 **Uvicorn**)은 이런 헤더를 해석할 수 있고, 그 정보를 애플리케이션으로 전달할 수 있습니다.
하지만 보안상, 서버는 자신이 신뢰할 수 있는 프록시 뒤에 있다는 것을 모르면 해당 헤더를 해석하지 않습니다.
/// note | 기술 세부사항
프록시 헤더는 다음과 같습니다:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### 프록시 전달 헤더 활성화하기 { #enable-proxy-forwarded-headers }
FastAPI CLI를 *CLI 옵션* `--forwarded-allow-ips`로 실행하고, 전달 헤더를 읽을 수 있도록 신뢰할 IP 주소들을 넘길 수 있습니다.
`--forwarded-allow-ips="*"`로 설정하면 들어오는 모든 IP를 신뢰합니다.
**서버**가 신뢰할 수 있는 **프록시** 뒤에 있고 프록시만 서버에 접근한다면, 이는 해당 **프록시**의 IP가 무엇이든 간에 받아들이게 됩니다.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### HTTPS에서 리디렉션 { #redirects-with-https }
예를 들어, *경로 처리* `/items/`를 정의했다고 해봅시다:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
클라이언트가 `/items`로 접근하면, 기본적으로 `/items/`로 리디렉션됩니다.
하지만 *CLI 옵션* `--forwarded-allow-ips`를 설정하기 전에는 `http://localhost:8000/items/`로 리디렉션될 수 있습니다.
그런데 애플리케이션이 `https://mysuperapp.com`에 호스팅되어 있고, 리디렉션도 `https://mysuperapp.com/items/`로 되어야 할 수 있습니다.
이때 `--proxy-headers`를 설정하면 FastAPI가 올바른 위치로 리디렉션할 수 있습니다. 😎
```
https://mysuperapp.com/items/
```
/// tip | 팁
HTTPS에 대해 더 알아보려면 가이드 [HTTPS에 대하여](../deployment/https.md)를 확인하세요.
///
### 프록시 전달 헤더가 동작하는 방식 { #how-proxy-forwarded-headers-work }
다음은 **프록시**가 클라이언트와 **애플리케이션 서버** 사이에서 전달 헤더를 추가하는 과정을 시각적으로 나타낸 것입니다:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
**프록시**는 원래의 클라이언트 요청을 가로채고, **애플리케이션 서버**로 요청을 전달하기 전에 특수한 *forwarded* 헤더(`X-Forwarded-*`)를 추가합니다.
이 헤더들은 그렇지 않으면 사라질 수 있는 원래 요청의 정보를 보존합니다:
* **X-Forwarded-For**: 원래 클라이언트의 IP 주소
* **X-Forwarded-Proto**: 원래 프로토콜(`https`)
* **X-Forwarded-Host**: 원래 호스트(`mysuperapp.com`)
**FastAPI CLI**를 `--forwarded-allow-ips`로 설정하면, 이 헤더를 신뢰하고 사용합니다. 예를 들어 리디렉션에서 올바른 URL을 생성하는 데 사용됩니다.
## 제거된 경로 접두사를 가진 프록시 { #proxy-with-a-stripped-path-prefix }
애플리케이션에 경로 접두사(prefix)를 추가하는 프록시를 둘 수도 있습니다.
이런 경우 `root_path`를 사용해 애플리케이션을 구성할 수 있습니다.
`root_path`는 (FastAPI가 Starlette를 통해 기반으로 하는) ASGI 사양에서 제공하는 메커니즘입니다.
`root_path`는 이러한 특정 사례를 처리하는 데 사용됩니다.
또한 서브 애플리케이션을 마운트할 때 내부적으로도 사용됩니다.
경로 접두사가 제거(stripped)되는 프록시가 있다는 것은, 코드에서는 `/app`에 경로를 선언하지만, 위에 한 겹(프록시)을 추가해 **FastAPI** 애플리케이션을 `/api/v1` 같은 경로 아래에 두는 것을 의미합니다.
이 경우 원래 경로 `/app`은 실제로 `/api/v1/app`에서 서비스됩니다.
코드는 모두 `/app`만 있다고 가정하고 작성되어 있는데도 말입니다.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
그리고 프록시는 요청을 앱 서버(아마 FastAPI CLI를 통해 실행되는 Uvicorn)로 전달하기 전에, 동적으로 **경로 접두사**를 **"제거"**합니다. 그래서 애플리케이션은 여전히 `/app`에서 서비스된다고 믿게 되고, 코드 전체를 `/api/v1` 접두사를 포함하도록 수정할 필요가 없어집니다.
여기까지는 보통 정상적으로 동작합니다.
하지만 통합 문서 UI(프론트엔드)를 열면, OpenAPI 스키마를 `/api/v1/openapi.json`이 아니라 `/openapi.json`에서 가져오려고 합니다.
그래서 브라우저에서 실행되는 프론트엔드는 `/openapi.json`에 접근하려고 시도하지만 OpenAPI 스키마를 얻지 못합니다.
앱에 대해 `/api/v1` 경로 접두사를 가진 프록시가 있으므로, 프론트엔드는 `/api/v1/openapi.json`에서 OpenAPI 스키마를 가져와야 합니다.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | 팁
IP `0.0.0.0`은 보통 해당 머신/서버에서 사용 가능한 모든 IP에서 프로그램이 리슨한다는 의미로 사용됩니다.
///
문서 UI는 또한 OpenAPI 스키마에서 이 API `server`가 `/api/v1`(프록시 뒤) 위치에 있다고 선언해야 합니다. 예:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// 여기에 다른 내용이 더 있습니다
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// 여기에 다른 내용이 더 있습니다
}
}
```
이 예시에서 "Proxy"는 **Traefik** 같은 것이고, 서버는 **Uvicorn**으로 실행되는 FastAPI CLI처럼, FastAPI 애플리케이션을 실행하는 구성일 수 있습니다.
### `root_path` 제공하기 { #providing-the-root-path }
이를 달성하려면 다음처럼 커맨드 라인 옵션 `--root-path`를 사용할 수 있습니다:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Hypercorn을 사용한다면, Hypercorn에도 `--root-path` 옵션이 있습니다.
/// note | 기술 세부사항
ASGI 사양은 이 사용 사례를 위해 `root_path`를 정의합니다.
그리고 커맨드 라인 옵션 `--root-path`가 그 `root_path`를 제공합니다.
///
### 현재 `root_path` 확인하기 { #checking-the-current-root-path }
요청마다 애플리케이션에서 사용 중인 현재 `root_path`를 얻을 수 있는데, 이는 `scope` 딕셔너리(ASGI 사양의 일부)에 포함되어 있습니다.
여기서는 데모 목적을 위해 메시지에 포함하고 있습니다.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
그 다음 Uvicorn을 다음과 같이 시작하면:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
응답은 다음과 비슷할 것입니다:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### FastAPI 앱에서 `root_path` 설정하기 { #setting-the-root-path-in-the-fastapi-app }
또는 `--root-path` 같은 커맨드 라인 옵션(또는 동등한 방법)을 제공할 수 없는 경우, FastAPI 앱을 생성할 때 `root_path` 파라미터를 설정할 수 있습니다:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
`FastAPI`에 `root_path`를 전달하는 것은 Uvicorn이나 Hypercorn에 커맨드 라인 옵션 `--root-path`를 전달하는 것과 동일합니다.
### `root_path`에 대하여 { #about-root-path }
서버(Uvicorn)는 그 `root_path`를 앱에 전달하는 것 외에는 다른 용도로 사용하지 않는다는 점을 기억하세요.
하지만 브라우저로 [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app)에 접속하면 정상 응답을 볼 수 있습니다:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
따라서 `http://127.0.0.1:8000/api/v1/app`로 접근될 것이라고 기대하지는 않습니다.
Uvicorn은 프록시가 `http://127.0.0.1:8000/app`에서 Uvicorn에 접근할 것을 기대하고, 그 위에 `/api/v1` 접두사를 추가하는 것은 프록시의 책임입니다.
## 제거된 경로 접두사를 가진 프록시에 대하여 { #about-proxies-with-a-stripped-path-prefix }
경로 접두사가 제거되는 프록시는 구성 방법 중 하나일 뿐이라는 점을 기억하세요.
많은 경우 기본값은 프록시가 경로 접두사를 제거하지 않는 방식일 것입니다.
그런 경우(경로 접두사를 제거하지 않는 경우) 프록시는 `https://myawesomeapp.com` 같은 곳에서 리슨하고, 브라우저가 `https://myawesomeapp.com/api/v1/app`로 접근하면, 서버(예: Uvicorn)가 `http://127.0.0.1:8000`에서 리슨하고 있을 때 프록시(경로 접두사를 제거하지 않는)는 동일한 경로로 Uvicorn에 접근합니다: `http://127.0.0.1:8000/api/v1/app`.
## Traefik으로 로컬 테스트하기 { #testing-locally-with-traefik }
[Traefik](https://docs.traefik.io/)을 사용하면, 경로 접두사가 제거되는 구성을 로컬에서 쉽게 실험할 수 있습니다.
[Traefik 다운로드](https://github.com/containous/traefik/releases)는 단일 바이너리이며, 압축 파일을 풀고 터미널에서 바로 실행할 수 있습니다.
그 다음 다음 내용을 가진 `traefik.toml` 파일을 생성하세요:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
이는 Traefik이 9999 포트에서 리슨하고, 다른 파일 `routes.toml`을 사용하도록 지시합니다.
/// tip | 팁
표준 HTTP 포트 80 대신 9999 포트를 사용해서, 관리자(`sudo`) 권한으로 실행하지 않아도 되게 했습니다.
///
이제 다른 파일 `routes.toml`을 생성하세요:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
이 파일은 Traefik이 경로 접두사 `/api/v1`을 사용하도록 설정합니다.
그리고 Traefik은 요청을 `http://127.0.0.1:8000`에서 실행 중인 Uvicorn으로 전달합니다.
이제 Traefik을 시작하세요:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### 응답 확인하기 { #check-the-responses }
이제 Uvicorn의 포트로 된 URL인 [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app)로 접속하면 정상 응답을 볼 수 있습니다:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | 팁
`http://127.0.0.1:8000/app`로 접근했는데도 `/api/v1`의 `root_path`가 표시되는 것에 주의하세요. 이는 옵션 `--root-path`에서 가져온 값입니다.
///
이제 Traefik의 포트가 포함되고 경로 접두사가 포함된 URL [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app)을 여세요.
동일한 응답을 얻습니다:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
하지만 이번에는 프록시가 제공한 접두사 경로 `/api/v1`이 포함된 URL에서의 응답입니다.
물론 여기서의 아이디어는 모두가 프록시를 통해 앱에 접근한다는 것이므로, `/api/v1` 경로 접두사가 있는 버전이 "올바른" 접근입니다.
그리고 경로 접두사가 없는 버전(`http://127.0.0.1:8000/app`)은 Uvicorn이 직접 제공하는 것이며, 오직 _프록시_(Traefik)가 접근하기 위한 용도입니다.
이는 프록시(Traefik)가 경로 접두사를 어떻게 사용하는지, 그리고 서버(Uvicorn)가 옵션 `--root-path`로부터의 `root_path`를 어떻게 사용하는지를 보여줍니다.
### 문서 UI 확인하기 { #check-the-docs-ui }
하지만 재미있는 부분은 여기입니다. ✨
앱에 접근하는 "공식" 방법은 우리가 정의한 경로 접두사를 가진 프록시를 통해서입니다. 따라서 기대하는 대로, URL에 경로 접두사가 없는 상태에서 Uvicorn이 직접 제공하는 docs UI를 시도하면, 프록시를 통해 접근된다고 가정하고 있기 때문에 동작하지 않습니다.
[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)에서 확인할 수 있습니다:
하지만 프록시(포트 `9999`)를 사용해 "공식" URL인 `/api/v1/docs`에서 docs UI에 접근하면, 올바르게 동작합니다! 🎉
[http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs)에서 확인할 수 있습니다:
원하던 그대로입니다. ✔️
이는 FastAPI가 이 `root_path`를 사용해, OpenAPI에서 기본 `server`를 `root_path`가 제공한 URL로 생성하기 때문입니다.
## 추가 서버 { #additional-servers }
/// warning | 경고
이는 더 고급 사용 사례입니다. 건너뛰어도 괜찮습니다.
///
기본적으로 **FastAPI**는 OpenAPI 스키마에서 `root_path`의 URL로 `server`를 생성합니다.
하지만 예를 들어 동일한 docs UI가 스테이징과 프로덕션 환경 모두와 상호작용하도록 하려면, 다른 대안 `servers`를 제공할 수도 있습니다.
사용자 정의 `servers` 리스트를 전달했고 `root_path`(API가 프록시 뒤에 있기 때문)가 있다면, **FastAPI**는 리스트의 맨 앞에 이 `root_path`를 가진 "server"를 삽입합니다.
예:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
다음과 같은 OpenAPI 스키마를 생성합니다:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// 여기에 다른 내용이 더 있습니다
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// 여기에 다른 내용이 더 있습니다
}
}
```
/// tip | 팁
`root_path`에서 가져온 값인 `/api/v1`의 `url` 값을 가진, 자동 생성된 server에 주목하세요.
///
[http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs)의 docs UI에서는 다음처럼 보입니다:
/// tip | 팁
docs UI는 선택한 server와 상호작용합니다.
///
/// note | 기술 세부사항
OpenAPI 사양에서 `servers` 속성은 선택 사항입니다.
`servers` 파라미터를 지정하지 않고 `root_path`가 `/`와 같다면, 생성된 OpenAPI 스키마의 `servers` 속성은 기본적으로 완전히 생략되며, 이는 `url` 값이 `/`인 단일 server와 동등합니다.
///
### `root_path`에서 자동 server 비활성화하기 { #disable-automatic-server-from-root-path }
**FastAPI**가 `root_path`를 사용한 자동 server를 포함하지 않게 하려면, `root_path_in_servers=False` 파라미터를 사용할 수 있습니다:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
그러면 OpenAPI 스키마에 포함되지 않습니다.
## 서브 애플리케이션 마운트하기 { #mounting-a-sub-application }
프록시에서 `root_path`를 사용하면서도, [서브 애플리케이션 - 마운트](sub-applications.md)에 설명된 것처럼 서브 애플리케이션을 마운트해야 한다면, 기대하는 대로 일반적으로 수행할 수 있습니다.
FastAPI가 내부적으로 `root_path`를 똑똑하게 사용하므로, 그냥 동작합니다. ✨
================================================
FILE: docs/ko/docs/advanced/custom-response.md
================================================
# 사용자 정의 응답 - HTML, Stream, 파일, 기타 { #custom-response-html-stream-file-others }
기본적으로 **FastAPI**는 JSON 응답을 반환합니다.
[응답을 직접 반환하기](response-directly.md)에서 본 것처럼 `Response`를 직접 반환하여 이를 재정의할 수 있습니다.
그러나 `Response`를 직접 반환하면(또는 `JSONResponse`와 같은 하위 클래스를 반환하면) 데이터가 자동으로 변환되지 않으며(비록 `response_model`을 선언했다 하더라도), 문서도 자동으로 생성되지 않습니다(예를 들어, 생성된 OpenAPI의 일부로 HTTP 헤더 `Content-Type`에 특정 "미디어 타입"을 포함하는 것 등).
하지만 *경로 처리 데코레이터*에서 `response_class` 매개변수를 사용하여 사용할 `Response`(예: 어떤 `Response` 하위 클래스든)를 선언할 수도 있습니다.
*경로 처리 함수*에서 반환하는 내용은 해당 `Response` 안에 담깁니다.
/// note | 참고
미디어 타입이 없는 응답 클래스를 사용하는 경우, FastAPI는 응답에 내용이 없을 것으로 예상하므로 생성된 OpenAPI 문서에서 응답 형식을 문서화하지 않습니다.
///
## JSON 응답 { #json-responses }
기본적으로 FastAPI는 JSON 응답을 반환합니다.
[응답 모델](../tutorial/response-model.md)을 선언하면 FastAPI는 Pydantic을 사용해 데이터를 JSON으로 직렬화합니다.
응답 모델을 선언하지 않으면 FastAPI는 [JSON 호환 가능 인코더](../tutorial/encoder.md)에서 설명한 `jsonable_encoder`를 사용해 데이터를 변환한 뒤 `JSONResponse`에 넣습니다.
`JSONResponse`처럼 JSON 미디어 타입(`application/json`)을 가진 `response_class`를 선언하면, 여러분이 *경로 처리 데코레이터*에서 선언한 Pydantic의 `response_model`로 데이터가 자동 변환(및 필터링)됩니다. 하지만 데이터의 JSON 바이트 직렬화 자체는 Pydantic이 수행하지 않고, `jsonable_encoder`로 변환한 후 `JSONResponse` 클래스에 전달하며, 그 클래스가 Python 표준 JSON 라이브러리를 사용해 바이트로 직렬화합니다.
### JSON 성능 { #json-performance }
요약하면, 최대 성능이 필요하다면 [응답 모델](../tutorial/response-model.md)을 사용하고, *경로 처리 데코레이터*에 `response_class`를 선언하지 마세요.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML 응답 { #html-response }
**FastAPI**에서 HTML 응답을 직접 반환하려면 `HTMLResponse`를 사용하세요.
* `HTMLResponse`를 임포트합니다.
* *경로 처리 데코레이터*의 `response_class` 매개변수로 `HTMLResponse`를 전달합니다.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | 정보
`response_class` 매개변수는 응답의 "미디어 타입"을 정의하는 데에도 사용됩니다.
이 경우, HTTP 헤더 `Content-Type`은 `text/html`로 설정됩니다.
그리고 이는 OpenAPI에 그대로 문서화됩니다.
///
### `Response` 반환하기 { #return-a-response }
[응답을 직접 반환하기](response-directly.md)에서 본 것처럼, *경로 처리*에서 응답을 직접 반환하여 재정의할 수도 있습니다.
위의 예제와 동일하게 `HTMLResponse`를 반환하는 코드는 다음과 같을 수 있습니다:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | 경고
*경로 처리 함수*에서 직접 반환된 `Response`는 OpenAPI에 문서화되지 않습니다(예를 들어, `Content-Type`이 문서화되지 않음) 그리고 자동 대화형 문서에도 표시되지 않습니다.
///
/// info | 정보
물론 실제 `Content-Type` 헤더, 상태 코드 등은 반환된 `Response` 객체에서 가져옵니다.
///
### OpenAPI에 문서화하고 `Response` 재정의하기 { #document-in-openapi-and-override-response }
함수 내부에서 응답을 재정의하면서 동시에 OpenAPI에서 "미디어 타입"을 문서화하고 싶다면, `response_class` 매개변수를 사용하면서 `Response` 객체를 반환할 수 있습니다.
이 경우 `response_class`는 OpenAPI *경로 처리*를 문서화하는 데만 사용되고, 실제로는 여러분이 반환한 `Response`가 그대로 사용됩니다.
#### `HTMLResponse` 직접 반환하기 { #return-an-htmlresponse-directly }
예를 들어, 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
이 예제에서 `generate_html_response()` 함수는 HTML을 `str`로 반환하는 대신 이미 `Response`를 생성하고 반환합니다.
`generate_html_response()`를 호출한 결과를 반환함으로써, 기본적인 **FastAPI** 동작을 재정의하는 `Response`를 이미 반환하고 있습니다.
하지만 `response_class`에 `HTMLResponse`를 함께 전달했기 때문에, **FastAPI**는 이를 OpenAPI 및 대화형 문서에서 `text/html`의 HTML로 문서화하는 방법을 알 수 있습니다:
## 사용 가능한 응답들 { #available-responses }
다음은 사용할 수 있는 몇 가지 응답들입니다.
`Response`를 사용하여 다른 어떤 것도 반환할 수 있으며, 직접 하위 클래스를 만들 수도 있습니다.
/// note | 기술 세부사항
`from starlette.responses import HTMLResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 동일하게 제공하지만, 대부분의 사용 가능한 응답은 Starlette에서 직접 가져옵니다.
///
### `Response` { #response }
기본 `Response` 클래스이며, 다른 모든 응답 클래스가 이를 상속합니다.
이 클래스를 직접 반환할 수 있습니다.
다음 매개변수를 받을 수 있습니다:
* `content` - `str` 또는 `bytes`.
* `status_code` - HTTP 상태 코드를 나타내는 `int`.
* `headers` - 문자열로 이루어진 `dict`.
* `media_type` - 미디어 타입을 나타내는 `str` 예: `"text/html"`.
FastAPI(정확히는 Starlette)가 자동으로 Content-Length 헤더를 포함시킵니다. 또한 `media_type`에 기반하여 Content-Type 헤더를 포함하며, 텍스트 타입의 경우 문자 집합을 추가합니다.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
텍스트 또는 바이트를 받아 HTML 응답을 반환합니다. 위에서 설명한 내용과 같습니다.
### `PlainTextResponse` { #plaintextresponse }
텍스트 또는 바이트를 받아 일반 텍스트 응답을 반환합니다.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
데이터를 받아 `application/json`으로 인코딩된 응답을 반환합니다.
이는 위에서 설명했듯이 **FastAPI**에서 기본적으로 사용되는 응답 형식입니다.
/// note | 기술 세부사항
하지만 응답 모델 또는 반환 타입을 선언한 경우, 해당 모델이 데이터를 JSON으로 직렬화하는 데 직접 사용되며, 올바른 JSON 미디어 타입의 응답이 `JSONResponse` 클래스를 사용하지 않고 바로 반환됩니다.
이 방식이 최상의 성능을 얻는 이상적인 방법입니다.
///
### `RedirectResponse` { #redirectresponse }
HTTP 리디렉션 응답을 반환합니다. 기본적으로 상태 코드는 307(임시 리디렉션)으로 설정됩니다.
`RedirectResponse`를 직접 반환할 수 있습니다:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
또는 `response_class` 매개변수에서 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
이 경우, *경로 처리* 함수에서 URL을 직접 반환할 수 있습니다.
이 경우, 사용되는 `status_code`는 `RedirectResponse`의 기본값인 `307`입니다.
---
`status_code` 매개변수를 `response_class` 매개변수와 함께 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
비동기 제너레이터 또는 일반 제너레이터/이터레이터(`yield`가 있는 함수)를 받아 응답 본문을 스트리밍합니다.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | 기술 세부사항
`async` 작업은 `await`에 도달했을 때만 취소될 수 있습니다. `await`가 없으면 제너레이터(`yield`가 있는 함수)는 제대로 취소될 수 없고, 취소가 요청된 후에도 계속 실행될 수 있습니다.
이 작은 예제는 어떤 `await`도 필요하지 않으므로, 이벤트 루프가 취소를 처리할 기회를 주기 위해 `await anyio.sleep(0)`를 추가합니다.
대규모 또는 무한 스트림에서는 더욱 중요합니다.
///
/// tip | 팁
`StreamingResponse`를 직접 반환하는 대신, [데이터 스트리밍](./stream-data.md)에서의 스타일을 따르는 것이 더 편리하며 백그라운드에서 취소 처리를 해줍니다.
JSON Lines를 스트리밍한다면, [JSON Lines 스트리밍](../tutorial/stream-json-lines.md) 튜토리얼을 확인하세요.
///
### `FileResponse` { #fileresponse }
파일을 비동기로 스트리밍하여 응답합니다.
다른 응답 유형과는 다른 인수를 사용하여 객체를 생성합니다:
* `path` - 스트리밍할 파일의 경로.
* `headers` - 딕셔너리 형식의 사용자 정의 헤더.
* `media_type` - 미디어 타입을 나타내는 문자열. 설정되지 않은 경우 파일 이름이나 경로를 사용하여 추론합니다.
* `filename` - 설정된 경우 응답의 `Content-Disposition`에 포함됩니다.
파일 응답에는 적절한 `Content-Length`, `Last-Modified`, 및 `ETag` 헤더가 포함됩니다.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
또한 `response_class` 매개변수를 사용할 수도 있습니다:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
이 경우, 경로 처리 함수에서 파일 경로를 직접 반환할 수 있습니다.
## 사용자 정의 응답 클래스 { #custom-response-class }
`Response`를 상속받아 사용자 정의 응답 클래스를 생성하고 사용할 수 있습니다.
예를 들어, [`orjson`](https://github.com/ijl/orjson)을 일부 설정과 함께 사용하고 싶다고 가정해봅시다.
들여쓰기 및 포맷된 JSON을 반환하고 싶다면, orjson 옵션 `orjson.OPT_INDENT_2`를 사용할 수 있습니다.
`CustomORJSONResponse`를 생성할 수 있습니다. 여기서 핵심은 `Response.render(content)` 메서드를 생성하여 내용을 `bytes`로 반환하는 것입니다:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
이제 다음 대신:
```json
{"message": "Hello World"}
```
...이 응답은 이렇게 반환됩니다:
```json
{
"message": "Hello World"
}
```
물론 JSON 포맷팅보다 더 유용하게 활용할 방법을 찾을 수 있을 것입니다. 😉
### `orjson` 또는 응답 모델 { #orjson-or-response-model }
성능이 목적이라면, `orjson` 응답을 사용하는 것보다 [응답 모델](../tutorial/response-model.md)을 사용하는 편이 더 나을 가능성이 큽니다.
응답 모델을 사용하면 FastAPI는 중간 단계(예: `jsonable_encoder`로의 변환) 없이 Pydantic을 사용해 데이터를 JSON으로 직렬화합니다. 이런 중간 단계는 다른 경우에 발생합니다.
그리고 내부적으로 Pydantic은 JSON 직렬화를 위해 `orjson`과 동일한 Rust 기반 메커니즘을 사용하므로, 응답 모델만으로도 이미 최상의 성능을 얻게 됩니다.
## 기본 응답 클래스 { #default-response-class }
**FastAPI** 클래스 인스턴스 또는 `APIRouter`를 생성할 때 기본으로 사용할 응답 클래스를 지정할 수 있습니다.
이를 정의하는 매개변수는 `default_response_class`입니다.
아래 예제에서 **FastAPI**는 모든 *경로 처리*에서 JSON 대신 기본적으로 `HTMLResponse`를 사용합니다.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | 팁
여전히 이전처럼 *경로 처리*에서 `response_class`를 재정의할 수 있습니다.
///
## 추가 문서화 { #additional-documentation }
OpenAPI에서 `responses`를 사용하여 미디어 타입 및 기타 세부 정보를 선언할 수도 있습니다: [OpenAPI에서 추가 응답](additional-responses.md).
================================================
FILE: docs/ko/docs/advanced/dataclasses.md
================================================
# Dataclasses 사용하기 { #using-dataclasses }
FastAPI는 **Pydantic** 위에 구축되어 있으며, 지금까지는 Pydantic 모델을 사용해 요청과 응답을 선언하는 방법을 보여드렸습니다.
하지만 FastAPI는 [`dataclasses`](https://docs.python.org/3/library/dataclasses.html)도 같은 방식으로 사용하는 것을 지원합니다:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
이는 **Pydantic** 덕분에 여전히 지원되는데, Pydantic이 [`dataclasses`에 대한 내부 지원](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel)을 제공하기 때문입니다.
따라서 위 코드처럼 Pydantic을 명시적으로 사용하지 않더라도, FastAPI는 Pydantic을 사용해 표준 dataclasses를 Pydantic의 dataclasses 변형으로 변환합니다.
그리고 물론 다음과 같은 기능도 동일하게 지원합니다:
* 데이터 검증
* 데이터 직렬화
* 데이터 문서화 등
이는 Pydantic 모델을 사용할 때와 같은 방식으로 동작합니다. 그리고 실제로도 내부적으로는 Pydantic을 사용해 같은 방식으로 구현됩니다.
/// info
dataclasses는 Pydantic 모델이 할 수 있는 모든 것을 할 수는 없다는 점을 기억하세요.
그래서 여전히 Pydantic 모델을 사용해야 할 수도 있습니다.
하지만 이미 여러 dataclasses를 가지고 있다면, 이것은 FastAPI로 웹 API를 구동하는 데 그것들을 활용할 수 있는 좋은 방법입니다. 🤓
///
## `response_model`에서 Dataclasses 사용하기 { #dataclasses-in-response-model }
`response_model` 매개변수에서도 `dataclasses`를 사용할 수 있습니다:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
dataclass는 자동으로 Pydantic dataclass로 변환됩니다.
이렇게 하면 해당 스키마가 API docs 사용자 인터페이스에 표시됩니다:
## 중첩 데이터 구조에서 Dataclasses 사용하기 { #dataclasses-in-nested-data-structures }
`dataclasses`를 다른 타입 애너테이션과 조합해 중첩 데이터 구조를 만들 수도 있습니다.
일부 경우에는 Pydantic 버전의 `dataclasses`를 사용해야 할 수도 있습니다. 예를 들어 자동 생성된 API 문서에서 오류가 발생하는 경우입니다.
그런 경우 표준 `dataclasses`를 드롭인 대체재인 `pydantic.dataclasses`로 간단히 바꾸면 됩니다:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. 표준 `dataclasses`에서 `field`를 계속 임포트합니다.
2. `pydantic.dataclasses`는 `dataclasses`의 드롭인 대체재입니다.
3. `Author` dataclass에는 `Item` dataclasses의 리스트가 포함됩니다.
4. `Author` dataclass가 `response_model` 매개변수로 사용됩니다.
5. 요청 본문으로 dataclasses와 함께 다른 표준 타입 애너테이션을 사용할 수 있습니다.
이 경우에는 `Item` dataclasses의 리스트입니다.
6. 여기서는 dataclasses 리스트인 `items`를 포함하는 딕셔너리를 반환합니다.
FastAPI는 여전히 데이터를 JSON으로 직렬화할 수 있습니다.
7. 여기서 `response_model`은 `Author` dataclasses 리스트에 대한 타입 애너테이션을 사용합니다.
다시 말해, `dataclasses`를 표준 타입 애너테이션과 조합할 수 있습니다.
8. 이 *경로 처리 함수*는 `async def` 대신 일반 `def`를 사용하고 있다는 점에 주목하세요.
언제나처럼 FastAPI에서는 필요에 따라 `def`와 `async def`를 조합해 사용할 수 있습니다.
어떤 것을 언제 사용해야 하는지 다시 확인하고 싶다면, [`async`와 `await`](../async.md#in-a-hurry) 문서의 _"급하신가요?"_ 섹션을 확인하세요.
9. 이 *경로 처리 함수*는 dataclasses를(물론 반환할 수도 있지만) 반환하지 않고, 내부 데이터를 담은 딕셔너리들의 리스트를 반환합니다.
FastAPI는 `response_model` 매개변수(dataclasses 포함)를 사용해 응답을 변환합니다.
`dataclasses`는 다른 타입 애너테이션과 매우 다양한 조합으로 결합해 복잡한 데이터 구조를 구성할 수 있습니다.
더 구체적인 내용은 위 코드 내 애너테이션 팁을 확인하세요.
## 더 알아보기 { #learn-more }
`dataclasses`를 다른 Pydantic 모델과 조합하거나, 이를 상속하거나, 여러분의 모델에 포함하는 등의 작업도 할 수 있습니다.
자세한 내용은 [dataclasses에 관한 Pydantic 문서](https://docs.pydantic.dev/latest/concepts/dataclasses/)를 참고하세요.
## 버전 { #version }
이 기능은 FastAPI `0.67.0` 버전부터 사용할 수 있습니다. 🔖
================================================
FILE: docs/ko/docs/advanced/events.md
================================================
# Lifespan 이벤트 { #lifespan-events }
애플리케이션이 **시작**하기 전에 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이는 이 코드가 **한 번**만 실행되며, 애플리케이션이 **요청을 받기 시작하기 전**에 실행된다는 의미입니다.
마찬가지로, 애플리케이션이 **종료**될 때 실행되어야 하는 로직(코드)을 정의할 수 있습니다. 이 경우, 이 코드는 **한 번**만 실행되며, **여러 요청을 처리한 후**에 실행됩니다.
이 코드는 애플리케이션이 요청을 받기 **시작**하기 전에 실행되고, 요청 처리를 **끝낸 직후**에 실행되기 때문에 전체 애플리케이션의 **수명(lifespan)**을 다룹니다(잠시 후 "lifespan"이라는 단어가 중요해집니다 😉).
이는 전체 앱에서 사용해야 하는 **자원**을 설정하고, 요청 간에 **공유되는** 자원을 설정하고, 그리고/또는 이후에 **정리**하는 데 매우 유용할 수 있습니다. 예를 들어, 데이터베이스 연결 풀 또는 공유 머신러닝 모델을 로드하는 경우입니다.
## 사용 사례 { #use-case }
먼저 **사용 사례** 예시로 시작한 다음, 이를 어떻게 해결할지 살펴보겠습니다.
요청을 처리하는 데 사용하고 싶은 **머신러닝 모델**이 있다고 상상해 봅시다. 🤖
동일한 모델이 요청 간에 공유되므로, 요청마다 모델이 하나씩 있거나 사용자마다 하나씩 있는 등의 방식이 아닙니다.
모델을 로드하는 데 **상당한 시간이 걸린다고 상상해 봅시다**, 왜냐하면 모델이 **디스크에서 많은 데이터를 읽어야** 하기 때문입니다. 그래서 모든 요청마다 이를 수행하고 싶지는 않습니다.
모듈/파일의 최상위에서 로드할 수도 있지만, 그러면 단순한 자동화된 테스트를 실행하는 경우에도 **모델을 로드**하게 되고, 테스트가 코드의 독립적인 부분을 실행하기 전에 모델이 로드될 때까지 기다려야 하므로 **느려집니다**.
이것이 우리가 해결할 문제입니다. 요청을 처리하기 전에 모델을 로드하되, 코드가 로드되는 동안이 아니라 애플리케이션이 요청을 받기 시작하기 직전에만 로드하겠습니다.
## Lifespan { #lifespan }
`FastAPI` 앱의 `lifespan` 매개변수와 "컨텍스트 매니저"를 사용하여 *시작*과 *종료* 로직을 정의할 수 있습니다(컨텍스트 매니저가 무엇인지 잠시 후에 보여드리겠습니다).
예제로 시작한 다음 자세히 살펴보겠습니다.
`yield`를 사용해 비동기 함수 `lifespan()`을 다음과 같이 생성합니다:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
여기서는 `yield` 이전에 (가짜) 모델 함수를 머신러닝 모델이 들어 있는 딕셔너리에 넣어 모델을 로드하는 비용이 큰 *시작* 작업을 시뮬레이션합니다. 이 코드는 애플리케이션이 **요청을 받기 시작하기 전**, *시작* 동안에 실행됩니다.
그리고 `yield` 직후에는 모델을 언로드합니다. 이 코드는 애플리케이션이 **요청 처리를 마친 후**, *종료* 직전에 실행됩니다. 예를 들어 메모리나 GPU 같은 자원을 해제할 수 있습니다.
/// tip | 팁
`shutdown`은 애플리케이션을 **중지**할 때 발생합니다.
새 버전을 시작해야 할 수도 있고, 그냥 실행하는 게 지겨워졌을 수도 있습니다. 🤷
///
### Lifespan 함수 { #lifespan-function }
먼저 주목할 점은 `yield`를 사용하여 비동기 함수를 정의하고 있다는 것입니다. 이는 `yield`를 사용하는 의존성과 매우 유사합니다.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
함수의 첫 번째 부분, 즉 `yield` 이전의 코드는 애플리케이션이 시작되기 **전에** 실행됩니다.
그리고 `yield` 이후의 부분은 애플리케이션이 종료된 **후에** 실행됩니다.
### 비동기 컨텍스트 매니저 { #async-context-manager }
확인해 보면, 함수는 `@asynccontextmanager`로 데코레이션되어 있습니다.
이는 함수를 "**비동기 컨텍스트 매니저**"라고 불리는 것으로 변환합니다.
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
파이썬에서 **컨텍스트 매니저**는 `with` 문에서 사용할 수 있는 것입니다. 예를 들어, `open()`은 컨텍스트 매니저로 사용할 수 있습니다:
```Python
with open("file.txt") as file:
file.read()
```
최근 버전의 파이썬에는 **비동기 컨텍스트 매니저**도 있습니다. 이를 `async with`와 함께 사용합니다:
```Python
async with lifespan(app):
await do_stuff()
```
위와 같은 컨텍스트 매니저 또는 비동기 컨텍스트 매니저를 만들면, `with` 블록에 들어가기 전에 `yield` 이전의 코드를 실행하고, `with` 블록을 벗어난 후에는 `yield` 이후의 코드를 실행합니다.
위의 코드 예제에서는 직접 사용하지 않고, FastAPI에 전달하여 FastAPI가 이를 사용하도록 합니다.
`FastAPI` 앱의 `lifespan` 매개변수는 **비동기 컨텍스트 매니저**를 받으므로, 새 `lifespan` 비동기 컨텍스트 매니저를 전달할 수 있습니다.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## 대체 이벤트(사용 중단) { #alternative-events-deprecated }
/// warning | 경고
*시작*과 *종료*를 처리하는 권장 방법은 위에서 설명한 대로 `FastAPI` 앱의 `lifespan` 매개변수를 사용하는 것입니다. `lifespan` 매개변수를 제공하면 `startup`과 `shutdown` 이벤트 핸들러는 더 이상 호출되지 않습니다. `lifespan`만 쓰거나 이벤트만 쓰거나 둘 중 하나이지, 둘 다는 아닙니다.
이 부분은 아마 건너뛰셔도 됩니다.
///
*시작*과 *종료* 동안 실행될 이 로직을 정의하는 대체 방법이 있습니다.
애플리케이션이 시작되기 전에 또는 애플리케이션이 종료될 때 실행되어야 하는 이벤트 핸들러(함수)를 정의할 수 있습니다.
이 함수들은 `async def` 또는 일반 `def`로 선언할 수 있습니다.
### `startup` 이벤트 { #startup-event }
애플리케이션이 시작되기 전에 실행되어야 하는 함수를 추가하려면, `"startup"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
이 경우, `startup` 이벤트 핸들러 함수는 "database"(그냥 `dict`) 항목을 일부 값으로 초기화합니다.
여러 개의 이벤트 핸들러 함수를 추가할 수 있습니다.
그리고 모든 `startup` 이벤트 핸들러가 완료될 때까지 애플리케이션은 요청을 받기 시작하지 않습니다.
### `shutdown` 이벤트 { #shutdown-event }
애플리케이션이 종료될 때 실행되어야 하는 함수를 추가하려면, `"shutdown"` 이벤트로 선언합니다:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
여기서 `shutdown` 이벤트 핸들러 함수는 텍스트 한 줄 `"Application shutdown"`을 `log.txt` 파일에 기록합니다.
/// info | 정보
`open()` 함수에서 `mode="a"`는 "append"(추가)를 의미하므로, 기존 내용을 덮어쓰지 않고 파일에 있던 내용 뒤에 줄이 추가됩니다.
///
/// tip | 팁
이 경우에는 파일과 상호작용하는 표준 파이썬 `open()` 함수를 사용하고 있습니다.
따라서 I/O(input/output)가 포함되어 있어 디스크에 기록되는 것을 "기다리는" 과정이 필요합니다.
하지만 `open()`은 `async`와 `await`를 사용하지 않습니다.
그래서 이벤트 핸들러 함수는 `async def` 대신 표준 `def`로 선언합니다.
///
### `startup`과 `shutdown`을 함께 { #startup-and-shutdown-together }
*시작*과 *종료* 로직은 연결되어 있을 가능성이 높습니다. 무언가를 시작했다가 끝내거나, 자원을 획득했다가 해제하는 등의 작업이 필요할 수 있습니다.
로직이나 변수를 함께 공유하지 않는 분리된 함수에서 이를 처리하면, 전역 변수에 값을 저장하거나 비슷한 트릭이 필요해져 더 어렵습니다.
그 때문에, 이제는 위에서 설명한 대로 `lifespan`을 사용하는 것이 권장됩니다.
## 기술적 세부사항 { #technical-details }
호기심 많은 분들을 위한 기술적인 세부사항입니다. 🤓
내부적으로 ASGI 기술 사양에서는 이것이 [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)의 일부이며, `startup`과 `shutdown`이라는 이벤트를 정의합니다.
/// info | 정보
Starlette `lifespan` 핸들러에 대해서는 [Starlette의 Lifespan 문서](https://www.starlette.dev/lifespan/)에서 더 읽어볼 수 있습니다.
또한 코드의 다른 영역에서 사용할 수 있는 lifespan 상태를 처리하는 방법도 포함되어 있습니다.
///
## 서브 애플리케이션 { #sub-applications }
🚨 이 lifespan 이벤트(startup 및 shutdown)는 메인 애플리케이션에 대해서만 실행되며, [서브 애플리케이션 - Mounts](sub-applications.md)에는 실행되지 않음을 유의하세요.
================================================
FILE: docs/ko/docs/advanced/generate-clients.md
================================================
# SDK 생성하기 { #generating-sdks }
**FastAPI**는 **OpenAPI** 사양을 기반으로 하므로, FastAPI의 API는 많은 도구가 이해할 수 있는 표준 형식으로 설명할 수 있습니다.
덕분에 여러 언어용 클라이언트 라이브러리(**SDKs**), 최신 **문서**, 그리고 코드와 동기화된 **테스트** 또는 **자동화 워크플로**를 쉽게 생성할 수 있습니다.
이 가이드에서는 FastAPI 백엔드용 **TypeScript SDK**를 생성하는 방법을 배웁니다.
## 오픈 소스 SDK 생성기 { #open-source-sdk-generators }
다양하게 활용할 수 있는 옵션으로 [OpenAPI Generator](https://openapi-generator.tech/)가 있으며, **다양한 프로그래밍 언어**를 지원하고 OpenAPI 사양으로부터 SDK를 생성할 수 있습니다.
**TypeScript 클라이언트**의 경우 [Hey API](https://heyapi.dev/)는 TypeScript 생태계에 최적화된 경험을 제공하는 목적에 맞게 설계된 솔루션입니다.
더 많은 SDK 생성기는 [OpenAPI.Tools](https://openapi.tools/#sdk)에서 확인할 수 있습니다.
/// tip | 팁
FastAPI는 **OpenAPI 3.1** 사양을 자동으로 생성하므로, 사용하는 도구는 이 버전을 지원해야 합니다.
///
## FastAPI 스폰서의 SDK 생성기 { #sdk-generators-from-fastapi-sponsors }
이 섹션에서는 FastAPI를 후원하는 회사들이 제공하는 **벤처 투자 기반** 및 **기업 지원** 솔루션을 소개합니다. 이 제품들은 고품질로 생성된 SDK에 더해 **추가 기능**과 **통합**을 제공합니다.
✨ [**FastAPI 후원하기**](../help-fastapi.md#sponsor-the-author) ✨를 통해, 이 회사들은 프레임워크와 그 **생태계**가 건강하고 **지속 가능**하게 유지되도록 돕습니다.
또한 이들의 후원은 FastAPI **커뮤니티**(여러분)에 대한 강한 헌신을 보여주며, **좋은 서비스**를 제공하는 것뿐 아니라, 견고하고 활발한 프레임워크인 FastAPI를 지원하는 데에도 관심이 있음을 나타냅니다. 🙇
예를 들어 다음을 사용해 볼 수 있습니다:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
이 중 일부는 오픈 소스이거나 무료 티어를 제공하므로, 비용 부담 없이 사용해 볼 수 있습니다. 다른 상용 SDK 생성기도 있으며 온라인에서 찾을 수 있습니다. 🤓
## TypeScript SDK 만들기 { #create-a-typescript-sdk }
간단한 FastAPI 애플리케이션으로 시작해 보겠습니다:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
*경로 처리*에서 요청 페이로드와 응답 페이로드에 사용하는 모델을 `Item`, `ResponseMessage` 모델로 정의하고 있다는 점에 주목하세요.
### API 문서 { #api-docs }
`/docs`로 이동하면, 요청으로 보낼 데이터와 응답으로 받을 데이터에 대한 **스키마(schemas)**가 있는 것을 볼 수 있습니다:
이 스키마는 앱에서 모델로 선언되었기 때문에 볼 수 있습니다.
그 정보는 앱의 **OpenAPI 스키마**에서 사용할 수 있고, 이후 API 문서에 표시됩니다.
OpenAPI에 포함된 모델의 동일한 정보가 **클라이언트 코드 생성**에 사용될 수 있습니다.
### Hey API { #hey-api }
모델이 포함된 FastAPI 앱이 준비되면, Hey API를 사용해 TypeScript 클라이언트를 생성할 수 있습니다. 가장 빠른 방법은 npx를 사용하는 것입니다.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
이 명령은 `./src/client`에 TypeScript SDK를 생성합니다.
[`@hey-api/openapi-ts` 설치 방법](https://heyapi.dev/openapi-ts/get-started)과 [생성된 결과물](https://heyapi.dev/openapi-ts/output)은 해당 웹사이트에서 확인할 수 있습니다.
### SDK 사용하기 { #using-the-sdk }
이제 클라이언트 코드를 import해서 사용할 수 있습니다. 아래처럼 사용할 수 있으며, 메서드에 대한 자동 완성이 제공되는 것을 확인할 수 있습니다:
보낼 페이로드에 대해서도 자동 완성이 제공됩니다:
/// tip | 팁
`name`과 `price`에 대한 자동 완성은 FastAPI 애플리케이션에서 `Item` 모델에 정의된 내용입니다.
///
전송하는 데이터에 대해 인라인 오류도 표시됩니다:
응답 객체도 자동 완성을 제공합니다:
## 태그가 있는 FastAPI 앱 { #fastapi-app-with-tags }
대부분의 경우 FastAPI 앱은 더 커지고, 서로 다른 *경로 처리* 그룹을 분리하기 위해 태그를 사용하게 될 가능성이 큽니다.
예를 들어 **items** 섹션과 **users** 섹션이 있고, 이를 태그로 분리할 수 있습니다:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### 태그로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-tags }
태그를 사용하는 FastAPI 앱에 대해 클라이언트를 생성하면, 일반적으로 생성된 클라이언트 코드도 태그를 기준으로 분리됩니다.
이렇게 하면 클라이언트 코드에서 항목들이 올바르게 정렬되고 그룹화됩니다:
이 경우 다음이 있습니다:
* `ItemsService`
* `UsersService`
### 클라이언트 메서드 이름 { #client-method-names }
현재 `createItemItemsPost` 같은 생성된 메서드 이름은 그다지 깔끔하지 않습니다:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...이는 클라이언트 생성기가 각 *경로 처리*에 대해 OpenAPI 내부의 **operation ID**를 사용하기 때문입니다.
OpenAPI는 모든 *경로 처리* 전체에서 operation ID가 각각 유일해야 한다고 요구합니다. 그래서 FastAPI는 operation ID가 유일하도록 **함수 이름**, **경로**, **HTTP method/operation**을 조합해 operation ID를 생성합니다.
하지만 다음에서 이를 개선하는 방법을 보여드리겠습니다. 🤓
## 커스텀 Operation ID와 더 나은 메서드 이름 { #custom-operation-ids-and-better-method-names }
클라이언트에서 **더 단순한 메서드 이름**을 갖도록, operation ID가 **생성되는 방식**을 **수정**할 수 있습니다.
이 경우 operation ID가 다른 방식으로도 **유일**하도록 보장해야 합니다.
예를 들어 각 *경로 처리*이 태그를 갖도록 한 다음, **태그**와 *경로 처리* **이름**(함수 이름)을 기반으로 operation ID를 생성할 수 있습니다.
### 유일 ID 생성 함수 커스터마이징 { #custom-generate-unique-id-function }
FastAPI는 각 *경로 처리*에 대해 **유일 ID**를 사용하며, 이는 **operation ID** 및 요청/응답에 필요한 커스텀 모델 이름에도 사용됩니다.
이 함수를 커스터마이징할 수 있습니다. 이 함수는 `APIRoute`를 받아 문자열을 반환합니다.
예를 들어 아래에서는 첫 번째 태그(대부분 태그는 하나만 있을 것입니다)와 *경로 처리* 이름(함수 이름)을 사용합니다.
그 다음 이 커스텀 함수를 `generate_unique_id_function` 매개변수로 **FastAPI**에 전달할 수 있습니다:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### 커스텀 Operation ID로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-custom-operation-ids }
이제 클라이언트를 다시 생성하면, 개선된 메서드 이름을 확인할 수 있습니다:
보시다시피, 이제 메서드 이름은 태그 다음에 함수 이름이 오며, URL 경로와 HTTP operation의 정보는 포함하지 않습니다.
### 클라이언트 생성기를 위한 OpenAPI 사양 전처리 { #preprocess-the-openapi-specification-for-the-client-generator }
생성된 코드에는 여전히 일부 **중복 정보**가 있습니다.
`ItemsService`(태그에서 가져옴)에 이미 **items**가 포함되어 있어 이 메서드가 items와 관련되어 있음을 알 수 있지만, 메서드 이름에도 태그 이름이 접두사로 붙어 있습니다. 😕
OpenAPI 전반에서는 operation ID가 **유일**하다는 것을 보장하기 위해 이 방식을 유지하고 싶을 수 있습니다.
하지만 생성된 클라이언트에서는, 클라이언트를 생성하기 직전에 OpenAPI operation ID를 **수정**해서 메서드 이름을 더 보기 좋고 **깔끔하게** 만들 수 있습니다.
OpenAPI JSON을 `openapi.json` 파일로 다운로드한 뒤, 아래와 같은 스크립트로 **접두사 태그를 제거**할 수 있습니다:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
이렇게 하면 operation ID가 `items-get_items` 같은 형태에서 `get_items`로 변경되어, 클라이언트 생성기가 더 단순한 메서드 이름을 생성할 수 있습니다.
### 전처리된 OpenAPI로 TypeScript 클라이언트 생성하기 { #generate-a-typescript-client-with-the-preprocessed-openapi }
이제 최종 결과가 `openapi.json` 파일에 있으므로, 입력 위치를 업데이트해야 합니다:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
새 클라이언트를 생성한 후에는 **깔끔한 메서드 이름**을 가지면서도, **자동 완성**, **인라인 오류** 등은 그대로 제공됩니다:
## 장점 { #benefits }
자동으로 생성된 클라이언트를 사용하면 다음에 대해 **자동 완성**을 받을 수 있습니다:
* 메서드
* 본문(body)의 요청 페이로드, 쿼리 파라미터 등
* 응답 페이로드
또한 모든 것에 대해 **인라인 오류**도 확인할 수 있습니다.
그리고 백엔드 코드를 업데이트한 뒤 프론트엔드를 **재생성(regenerate)**하면, 새 *경로 처리*가 메서드로 추가되고 기존 것은 제거되며, 그 밖의 변경 사항도 생성된 코드에 반영됩니다. 🤓
이는 무언가 변경되면 그 변경이 클라이언트 코드에도 자동으로 **반영**된다는 뜻입니다. 또한 클라이언트를 **빌드(build)**하면 사용된 데이터가 **불일치(mismatch)**할 경우 오류가 발생합니다.
따라서 운영 환경에서 최종 사용자에게 오류가 노출된 뒤 문제를 추적하는 대신, 개발 사이클 초기에 **많은 오류를 매우 빨리 감지**할 수 있습니다. ✨
================================================
FILE: docs/ko/docs/advanced/index.md
================================================
# 심화 사용자 안내서 { #advanced-user-guide }
## 추가 기능 { #additional-features }
메인 [자습서 - 사용자 안내서](../tutorial/index.md)는 여러분이 **FastAPI**의 모든 주요 기능을 둘러보시기에 충분할 것입니다.
이어지는 장에서는 여러분이 다른 옵션, 구성 및 추가 기능을 보실 수 있습니다.
/// tip | 팁
다음 장들이 **반드시 "심화"**인 것은 아닙니다.
그리고 여러분의 사용 사례에 대한 해결책이 그중 하나에 있을 수 있습니다.
///
## 자습서를 먼저 읽으십시오 { #read-the-tutorial-first }
여러분은 메인 [자습서 - 사용자 안내서](../tutorial/index.md)의 지식으로 **FastAPI**의 대부분의 기능을 사용하실 수 있습니다.
이어지는 장들은 여러분이 메인 자습서 - 사용자 안내서를 이미 읽으셨으며 주요 아이디어를 알고 계신다고 가정합니다.
================================================
FILE: docs/ko/docs/advanced/json-base64-bytes.md
================================================
# 바이트를 Base64로 포함하는 JSON { #json-with-bytes-as-base64 }
애플리케이션에서 JSON 데이터를 주고받아야 하지만 그 안에 바이너리 데이터를 포함해야 한다면, base64로 인코딩해서 포함할 수 있습니다.
## Base64와 파일 { #base64-vs-files }
바이너리 데이터 업로드에는 [요청 파일](../tutorial/request-files.md)을, 바이너리 데이터 전송에는 [커스텀 응답 - FileResponse](./custom-response.md#fileresponse--fileresponse-)를 사용할 수 있는지 먼저 고려하세요. JSON으로 인코딩하는 대신 말입니다.
JSON은 UTF-8로 인코딩된 문자열만 포함할 수 있으므로, 원시 바이트를 그대로 담을 수 없습니다.
Base64는 바이너리 데이터를 문자열로 인코딩할 수 있지만, 이를 위해 원래의 바이너리 데이터보다 더 많은 문자 수를 사용해야 하므로 일반적인 파일 전송보다 비효율적일 수 있습니다.
반드시 JSON 안에 바이너리 데이터를 포함해야 하고, 파일을 사용할 수 없을 때만 base64를 사용하세요.
## Pydantic `bytes` { #pydantic-bytes }
`bytes` 필드를 가진 Pydantic 모델을 선언하고, 모델 설정에서 `val_json_bytes`를 사용하도록 지정하면 입력 JSON 데이터를 base64로 “검증”하도록 할 수 있습니다. 이 검증 과정의 일부로 base64 문자열을 바이트로 디코딩합니다.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
`/docs`를 확인하면 `data` 필드가 base64로 인코딩된 bytes를 기대한다고 표시됩니다:
아래와 같은 요청을 보낼 수 있습니다:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | 팁
`aGVsbG8=`는 `hello`의 base64 인코딩입니다.
///
그러면 Pydantic이 base64 문자열을 디코딩하여 모델의 `data` 필드에 원래 바이트를 제공합니다.
다음과 같은 응답을 받게 됩니다:
```json
{
"description": "Some data",
"content": "hello"
}
```
## 출력 데이터용 Pydantic `bytes` { #pydantic-bytes-for-output-data }
출력 데이터에도 모델 설정에서 `ser_json_bytes`와 함께 `bytes` 필드를 사용할 수 있습니다. 그러면 Pydantic이 JSON 응답을 생성할 때 바이트를 base64로 “직렬화”합니다.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## 입력과 출력 데이터용 Pydantic `bytes` { #pydantic-bytes-for-input-and-output-data }
물론, 동일한 모델을 사용해 JSON 데이터를 받을 때는 `val_json_bytes`로 입력을 “검증”하고, JSON 데이터를 보낼 때는 `ser_json_bytes`로 출력을 “직렬화”하도록 base64를 모두 처리하게 구성할 수 있습니다.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/ko/docs/advanced/middleware.md
================================================
# 고급 Middleware { #advanced-middleware }
메인 튜토리얼에서 애플리케이션에 [커스텀 Middleware](../tutorial/middleware.md)를 추가하는 방법을 읽었습니다.
그리고 [`CORSMiddleware`로 CORS 처리하기](../tutorial/cors.md)도 읽었습니다.
이 섹션에서는 다른 middleware들을 사용하는 방법을 살펴보겠습니다.
## ASGI middleware 추가하기 { #adding-asgi-middlewares }
**FastAPI**는 Starlette를 기반으로 하고 ASGI 사양을 구현하므로, 어떤 ASGI middleware든 사용할 수 있습니다.
ASGI 사양을 따르기만 하면, FastAPI나 Starlette를 위해 만들어진 middleware가 아니어도 동작합니다.
일반적으로 ASGI middleware는 첫 번째 인자로 ASGI 앱을 받도록 기대하는 클래스입니다.
그래서 서드파티 ASGI middleware 문서에서는 아마 다음과 같이 하라고 안내할 것입니다:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
하지만 FastAPI(정확히는 Starlette)는 더 간단한 방법을 제공하며, 이를 통해 내부 middleware가 서버 오류를 처리하고 커스텀 예외 핸들러가 올바르게 동작하도록 보장합니다.
이를 위해(그리고 CORS 예제에서처럼) `app.add_middleware()`를 사용합니다.
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()`는 첫 번째 인자로 middleware 클래스를 받고, 그 뒤에는 middleware에 전달할 추가 인자들을 받습니다.
## 통합 middleware { #integrated-middlewares }
**FastAPI**에는 일반적인 사용 사례를 위한 여러 middleware가 포함되어 있습니다. 다음에서 이를 사용하는 방법을 살펴보겠습니다.
/// note | 기술 세부사항
다음 예제에서는 `from starlette.middleware.something import SomethingMiddleware`를 사용해도 됩니다.
**FastAPI**는 개발자 편의를 위해 `fastapi.middleware`에 여러 middleware를 제공하지만, 사용 가능한 대부분의 middleware는 Starlette에서 직접 제공됩니다.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
들어오는 모든 요청이 `https` 또는 `wss`여야 하도록 강제합니다.
`http` 또는 `ws`로 들어오는 모든 요청은 대신 보안 스킴으로 리디렉션됩니다.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
HTTP Host Header 공격을 방어하기 위해, 들어오는 모든 요청에 올바르게 설정된 `Host` 헤더가 있어야 하도록 강제합니다.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
다음 인자들을 지원합니다:
* `allowed_hosts` - 호스트명으로 허용할 도메인 이름 목록입니다. `*.example.com` 같은 와일드카드 도메인으로 서브도메인을 매칭하는 것도 지원합니다. 어떤 호스트명이든 허용하려면 `allowed_hosts=["*"]`를 사용하거나 middleware를 생략하세요.
* `www_redirect` - True로 설정하면, 허용된 호스트의 non-www 버전으로 들어오는 요청을 www 버전으로 리디렉션합니다. 기본값은 `True`입니다.
들어오는 요청이 올바르게 검증되지 않으면 `400` 응답이 전송됩니다.
## `GZipMiddleware` { #gzipmiddleware }
`Accept-Encoding` 헤더에 `"gzip"`이 포함된 어떤 요청이든 GZip 응답을 처리합니다.
이 middleware는 일반 응답과 스트리밍 응답을 모두 처리합니다.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
다음 인자들을 지원합니다:
* `minimum_size` - 바이트 단위로 지정한 최소 크기보다 작은 응답은 GZip으로 압축하지 않습니다. 기본값은 `500`입니다.
* `compresslevel` - GZip 압축 중에 사용됩니다. 1부터 9까지의 정수입니다. 기본값은 `9`입니다. 값이 낮을수록 압축은 더 빠르지만 파일 크기는 더 커지고, 값이 높을수록 압축은 더 느리지만 파일 크기는 더 작아집니다.
## 다른 middleware { #other-middlewares }
다른 ASGI middleware도 많이 있습니다.
예를 들어:
* [Uvicorn의 `ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
사용 가능한 다른 middleware를 보려면 [Starlette의 Middleware 문서](https://www.starlette.dev/middleware/)와 [ASGI Awesome List](https://github.com/florimondmanca/awesome-asgi)를 확인하세요.
================================================
FILE: docs/ko/docs/advanced/openapi-callbacks.md
================================================
# OpenAPI 콜백 { #openapi-callbacks }
다른 사람이 만든 *external API*(아마도 여러분의 API를 *사용*할 동일한 개발자)가 요청을 트리거하도록 만드는 *경로 처리*를 가진 API를 만들 수 있습니다.
여러분의 API 앱이 *external API*를 호출할 때 일어나는 과정을 "callback"이라고 합니다. 외부 개발자가 작성한 소프트웨어가 여러분의 API로 요청을 보낸 다음, 여러분의 API가 다시 *external API*로 요청을 보내 *되돌려 호출*하기 때문입니다(아마도 같은 개발자가 만든 API일 것입니다).
이 경우, 그 *external API*가 어떤 형태여야 하는지 문서화하고 싶을 수 있습니다. 어떤 *경로 처리*를 가져야 하는지, 어떤 body를 기대하는지, 어떤 응답을 반환해야 하는지 등입니다.
## 콜백이 있는 앱 { #an-app-with-callbacks }
예시로 확인해 보겠습니다.
청구서를 생성할 수 있는 앱을 개발한다고 가정해 보세요.
이 청구서는 `id`, `title`(선택 사항), `customer`, `total`을 갖습니다.
여러분의 API 사용자(외부 개발자)는 POST 요청으로 여러분의 API에서 청구서를 생성합니다.
그 다음 여러분의 API는(가정해 보면):
* 청구서를 외부 개발자의 고객에게 전송합니다.
* 돈을 수금합니다.
* API 사용자(외부 개발자)의 API로 다시 알림을 보냅니다.
* 이는 (여러분의 API에서) 그 외부 개발자가 제공하는 어떤 *external API*로 POST 요청을 보내는 방식으로 수행됩니다(이것이 "callback"입니다).
## 일반적인 **FastAPI** 앱 { #the-normal-fastapi-app }
먼저 콜백을 추가하기 전, 일반적인 API 앱이 어떻게 생겼는지 보겠습니다.
`Invoice` body를 받는 *경로 처리*와, 콜백을 위한 URL을 담는 쿼리 파라미터 `callback_url`이 있을 것입니다.
이 부분은 꽤 일반적이며, 대부분의 코드는 이미 익숙할 것입니다:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | 팁
`callback_url` 쿼리 파라미터는 Pydantic의 [Url](https://docs.pydantic.dev/latest/api/networks/) 타입을 사용합니다.
///
유일하게 새로운 것은 *경로 처리 데코레이터*의 인자로 `callbacks=invoices_callback_router.routes`가 들어간다는 점입니다. 이것이 무엇인지 다음에서 보겠습니다.
## 콜백 문서화하기 { #documenting-the-callback }
실제 콜백 코드는 여러분의 API 앱에 크게 의존합니다.
그리고 앱마다 많이 달라질 수 있습니다.
다음처럼 한두 줄의 코드일 수도 있습니다:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
하지만 콜백에서 가장 중요한 부분은, 여러분의 API 사용자(외부 개발자)가 콜백 요청 body로 *여러분의 API*가 보낼 데이터 등에 맞춰 *external API*를 올바르게 구현하도록 보장하는 것입니다.
그래서 다음으로 할 일은, *여러분의 API*에서 보내는 콜백을 받기 위해 그 *external API*가 어떤 형태여야 하는지 문서화하는 코드를 추가하는 것입니다.
그 문서는 여러분의 API에서 `/docs`의 Swagger UI에 표시되며, 외부 개발자들이 *external API*를 어떻게 만들어야 하는지 알 수 있게 해줍니다.
이 예시는 콜백 자체(한 줄 코드로도 될 수 있음)를 구현하지 않고, 문서화 부분만 구현합니다.
/// tip | 팁
실제 콜백은 단지 HTTP 요청입니다.
콜백을 직접 구현할 때는 [HTTPX](https://www.python-httpx.org)나 [Requests](https://requests.readthedocs.io/) 같은 것을 사용할 수 있습니다.
///
## 콜백 문서화 코드 작성하기 { #write-the-callback-documentation-code }
이 코드는 앱에서 실행되지 않습니다. 그 *external API*가 어떤 형태여야 하는지 *문서화*하는 데만 필요합니다.
하지만 **FastAPI**로 API의 자동 문서를 쉽게 생성하는 방법은 이미 알고 있습니다.
따라서 그와 같은 지식을 사용해 *external API*가 어떻게 생겨야 하는지 문서화할 것입니다... 즉 외부 API가 구현해야 하는 *경로 처리(들)*(여러분의 API가 호출할 것들)을 만들어서 말입니다.
/// tip | 팁
콜백을 문서화하는 코드를 작성할 때는, 자신이 그 *외부 개발자*라고 상상하는 것이 유용할 수 있습니다. 그리고 지금은 *여러분의 API*가 아니라 *external API*를 구현하고 있다고 생각해 보세요.
이 관점(외부 개발자의 관점)을 잠시 채택하면, 그 *external API*를 위해 파라미터, body용 Pydantic 모델, 응답 등을 어디에 두어야 하는지가 더 명확하게 느껴질 수 있습니다.
///
### 콜백 `APIRouter` 생성하기 { #create-a-callback-apirouter }
먼저 하나 이상의 콜백을 담을 새 `APIRouter`를 만듭니다.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### 콜백 *경로 처리* 생성하기 { #create-the-callback-path-operation }
콜백 *경로 처리*를 만들려면 위에서 만든 동일한 `APIRouter`를 사용합니다.
일반적인 FastAPI *경로 처리*처럼 보일 것입니다:
* 아마도 받아야 할 body 선언이 있을 것입니다(예: `body: InvoiceEvent`).
* 그리고 반환해야 할 응답 선언도 있을 수 있습니다(예: `response_model=InvoiceEventReceived`).
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
일반적인 *경로 처리*와의 주요 차이점은 2가지입니다:
* 실제 코드를 가질 필요가 없습니다. 여러분의 앱은 이 코드를 절대 호출하지 않기 때문입니다. 이는 *external API*를 문서화하는 데만 사용됩니다. 따라서 함수는 그냥 `pass`만 있어도 됩니다.
* *path*에는 [OpenAPI 3 표현식](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression)(자세한 내용은 아래 참고)이 포함될 수 있으며, 이를 통해 *여러분의 API*로 보내진 원래 요청의 파라미터와 일부 값을 변수로 사용할 수 있습니다.
### 콜백 경로 표현식 { #the-callback-path-expression }
콜백 *path*는 *여러분의 API*로 보내진 원래 요청의 일부를 포함할 수 있는 [OpenAPI 3 표현식](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression)을 가질 수 있습니다.
이 경우, 다음 `str`입니다:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
따라서 여러분의 API 사용자(외부 개발자)가 *여러분의 API*로 다음 요청을 보내고:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
JSON body가 다음과 같다면:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
그러면 *여러분의 API*는 청구서를 처리하고, 나중에 어느 시점에서 `callback_url`(즉 *external API*)로 콜백 요청을 보냅니다:
```
https://www.external.org/events/invoices/2expen51ve
```
그리고 다음과 같은 JSON body를 포함할 것입니다:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
또한 그 *external API*로부터 다음과 같은 JSON body 응답을 기대합니다:
```JSON
{
"ok": true
}
```
/// tip | 팁
콜백 URL에는 `callback_url` 쿼리 파라미터로 받은 URL(`https://www.external.org/events`)뿐 아니라, JSON body 안의 청구서 `id`(`2expen51ve`)도 함께 사용된다는 점에 주목하세요.
///
### 콜백 라우터 추가하기 { #add-the-callback-router }
이 시점에서, 위에서 만든 콜백 라우터 안에 *콜백 경로 처리(들)*(즉 *external developer*가 *external API*에 구현해야 하는 것들)을 준비했습니다.
이제 *여러분의 API 경로 처리 데코레이터*에서 `callbacks` 파라미터를 사용해, 그 콜백 라우터의 `.routes` 속성(실제로는 routes/*경로 처리*의 `list`)을 전달합니다:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | 팁
`callback=`에 라우터 자체(`invoices_callback_router`)를 넘기는 것이 아니라, `invoices_callback_router.routes`처럼 `.routes` 속성을 넘긴다는 점에 주목하세요.
///
### 문서 확인하기 { #check-the-docs }
이제 앱을 실행하고 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)로 이동하세요.
*경로 처리*에 대해 "Callbacks" 섹션을 포함한 문서가 표시되며, *external API*가 어떤 형태여야 하는지 확인할 수 있습니다:
================================================
FILE: docs/ko/docs/advanced/openapi-webhooks.md
================================================
# OpenAPI Webhooks { #openapi-webhooks }
앱이 어떤 데이터와 함께 (요청을 보내서) *사용자의* 앱을 호출할 수 있고, 보통 어떤 **이벤트**를 **알리기** 위해 그렇게 할 수 있다는 것을 API **사용자**에게 알려야 하는 경우가 있습니다.
이는 사용자가 여러분의 API로 요청을 보내는 일반적인 과정 대신, **여러분의 API**(또는 앱)가 **사용자의 시스템**(사용자의 API, 사용자의 앱)으로 **요청을 보낼 수 있다**는 의미입니다.
이를 보통 **webhook**이라고 합니다.
## Webhooks 단계 { #webhooks-steps }
일반적인 과정은, 여러분이 코드에서 보낼 메시지, 즉 **요청 본문(body)**이 무엇인지 **정의**하는 것입니다.
또한 여러분의 앱이 어떤 **시점**에 그 요청(또는 이벤트)을 보낼지도 어떤 방식으로든 정의합니다.
그리고 **사용자**는 (예: 어딘가의 웹 대시보드에서) 여러분의 앱이 그 요청을 보내야 할 **URL**을 어떤 방식으로든 정의합니다.
webhook의 URL을 등록하는 방법과 실제로 그 요청을 보내는 코드에 대한 모든 **로직**은 여러분에게 달려 있습니다. **여러분의 코드**에서 원하는 방식으로 작성하면 됩니다.
## **FastAPI**와 OpenAPI로 webhooks 문서화하기 { #documenting-webhooks-with-fastapi-and-openapi }
**FastAPI**에서는 OpenAPI를 사용해, 이러한 webhook의 이름, 여러분의 앱이 보낼 수 있는 HTTP 작업 타입(예: `POST`, `PUT` 등), 그리고 여러분의 앱이 보낼 요청 **본문(body)**을 정의할 수 있습니다.
이렇게 하면 사용자가 여러분의 **webhook** 요청을 받기 위해 **자신들의 API를 구현**하기가 훨씬 쉬워지고, 경우에 따라서는 자신의 API 코드 일부를 자동 생성할 수도 있습니다.
/// info | 정보
Webhooks는 OpenAPI 3.1.0 이상에서 사용할 수 있으며, FastAPI `0.99.0` 이상에서 지원됩니다.
///
## webhooks가 있는 앱 { #an-app-with-webhooks }
**FastAPI** 애플리케이션을 만들면, *경로 처리*를 정의하는 것과 같은 방식으로(예: `@app.webhooks.post()`), *webhooks*를 정의하는 데 사용할 수 있는 `webhooks` 속성이 있습니다.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
여러분이 정의한 webhook은 **OpenAPI** 스키마와 자동 **docs UI**에 포함됩니다.
/// info | 정보
`app.webhooks` 객체는 실제로 `APIRouter`일 뿐이며, 여러 파일로 앱을 구조화할 때 사용하는 것과 동일한 타입입니다.
///
webhook에서는 실제로(`/items/` 같은) *경로(path)*를 선언하지 않는다는 점에 유의하세요. 그곳에 전달하는 텍스트는 webhook의 **식별자**(이벤트 이름)일 뿐입니다. 예를 들어 `@app.webhooks.post("new-subscription")`에서 webhook 이름은 `new-subscription`입니다.
이는 **사용자**가 webhook 요청을 받고 싶은 실제 **URL 경로**를 다른 방식(예: 웹 대시보드)으로 정의할 것이라고 기대하기 때문입니다.
### 문서 확인하기 { #check-the-docs }
이제 앱을 실행하고 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)로 이동하세요.
문서에 일반적인 *경로 처리*가 보이고, 이제는 일부 **webhooks**도 함께 보일 것입니다:
================================================
FILE: docs/ko/docs/advanced/path-operation-advanced-configuration.md
================================================
# 경로 처리 고급 구성 { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | 경고
OpenAPI “전문가”가 아니라면, 아마 이 내용은 필요하지 않을 것입니다.
///
매개변수 `operation_id`를 사용해 *경로 처리*에 사용할 OpenAPI `operationId`를 설정할 수 있습니다.
각 작업마다 고유하도록 보장해야 합니다.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### *경로 처리 함수* 이름을 operationId로 사용하기 { #using-the-path-operation-function-name-as-the-operationid }
API의 함수 이름을 `operationId`로 사용하고 싶다면, 모든 API를 순회하면서 `APIRoute.name`을 사용해 각 *경로 처리*의 `operation_id`를 덮어쓸 수 있습니다.
모든 *경로 처리*를 추가한 뒤에 수행해야 합니다.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | 팁
`app.openapi()`를 수동으로 호출한다면, 그 전에 `operationId`들을 업데이트해야 합니다.
///
/// warning | 경고
이렇게 할 경우, 각 *경로 처리 함수*의 이름이 고유하도록 보장해야 합니다.
서로 다른 모듈(파이썬 파일)에 있어도 마찬가지입니다.
///
## OpenAPI에서 제외하기 { #exclude-from-openapi }
생성된 OpenAPI 스키마(따라서 자동 문서화 시스템)에서 특정 *경로 처리*를 제외하려면, `include_in_schema` 매개변수를 `False`로 설정하세요:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## docstring에서 고급 설명 가져오기 { #advanced-description-from-docstring }
OpenAPI에 사용할 *경로 처리 함수*의 docstring 줄 수를 제한할 수 있습니다.
`\f`(이스케이프된 "form feed" 문자)를 추가하면 **FastAPI**는 이 지점에서 OpenAPI에 사용할 출력 내용을 잘라냅니다.
문서에는 표시되지 않지만, Sphinx 같은 다른 도구는 나머지 부분을 사용할 수 있습니다.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## 추가 응답 { #additional-responses }
*경로 처리*에 대해 `response_model`과 `status_code`를 선언하는 방법을 이미 보셨을 것입니다.
이는 *경로 처리*의 기본 응답에 대한 메타데이터를 정의합니다.
모델, 상태 코드 등과 함께 추가 응답도 선언할 수 있습니다.
이에 대한 문서의 전체 장이 있으니, [OpenAPI의 추가 응답](additional-responses.md)에서 읽어보세요.
## OpenAPI Extra { #openapi-extra }
애플리케이션에서 *경로 처리*를 선언하면, **FastAPI**는 OpenAPI 스키마에 포함될 해당 *경로 처리*의 관련 메타데이터를 자동으로 생성합니다.
/// note | 기술 세부사항
OpenAPI 명세에서는 이를 [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object)라고 부릅니다.
///
여기에는 *경로 처리*에 대한 모든 정보가 있으며, 자동 문서를 생성하는 데 사용됩니다.
`tags`, `parameters`, `requestBody`, `responses` 등이 포함됩니다.
이 *경로 처리* 전용 OpenAPI 스키마는 보통 **FastAPI**가 자동으로 생성하지만, 확장할 수도 있습니다.
/// tip | 팁
이는 저수준 확장 지점입니다.
추가 응답만 선언하면 된다면, 더 편리한 방법은 [OpenAPI의 추가 응답](additional-responses.md)을 사용하는 것입니다.
///
`openapi_extra` 매개변수를 사용해 *경로 처리*의 OpenAPI 스키마를 확장할 수 있습니다.
### OpenAPI 확장 { #openapi-extensions }
예를 들어 `openapi_extra`는 [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions)를 선언하는 데 도움이 될 수 있습니다:
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
자동 API 문서를 열면, 해당 특정 *경로 처리*의 하단에 확장이 표시됩니다.
또한 API의 `/openapi.json`에서 결과 OpenAPI를 보면, 특정 *경로 처리*의 일부로 확장이 포함된 것도 확인할 수 있습니다:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### 사용자 정의 OpenAPI *경로 처리* 스키마 { #custom-openapi-path-operation-schema }
`openapi_extra`의 딕셔너리는 *경로 처리*에 대해 자동으로 생성된 OpenAPI 스키마와 깊게 병합됩니다.
따라서 자동 생성된 스키마에 추가 데이터를 더할 수 있습니다.
예를 들어 Pydantic과 함께 FastAPI의 자동 기능을 사용하지 않고, 자체 코드로 요청을 읽고 검증하기로 결정할 수도 있지만, OpenAPI 스키마에는 여전히 그 요청을 정의하고 싶을 수 있습니다.
그럴 때 `openapi_extra`를 사용할 수 있습니다:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
이 예시에서는 어떤 Pydantic 모델도 선언하지 않았습니다. 사실 요청 바디는 JSON으로 파싱되지도 않고, `bytes`로 직접 읽습니다. 그리고 함수 `magic_data_reader()`가 어떤 방식으로든 이를 파싱하는 역할을 담당합니다.
그럼에도 불구하고, 요청 바디에 대해 기대하는 스키마를 선언할 수 있습니다.
### 사용자 정의 OpenAPI 콘텐츠 타입 { #custom-openapi-content-type }
같은 트릭을 사용하면, Pydantic 모델을 이용해 JSON Schema를 정의하고 이를 *경로 처리*의 사용자 정의 OpenAPI 스키마 섹션에 포함시킬 수 있습니다.
요청의 데이터 타입이 JSON이 아니더라도 이렇게 할 수 있습니다.
예를 들어 이 애플리케이션에서는 Pydantic 모델에서 JSON Schema를 추출하는 FastAPI의 통합 기능도, JSON에 대한 자동 검증도 사용하지 않습니다. 실제로 요청 콘텐츠 타입을 JSON이 아니라 YAML로 선언합니다:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
그럼에도 기본 통합 기능을 사용하지 않더라도, YAML로 받고자 하는 데이터에 대한 JSON Schema를 수동으로 생성하기 위해 Pydantic 모델을 여전히 사용합니다.
그 다음 요청을 직접 사용하고, 바디를 `bytes`로 추출합니다. 이는 FastAPI가 요청 페이로드를 JSON으로 파싱하려고 시도조차 하지 않는다는 뜻입니다.
그리고 코드에서 YAML 콘텐츠를 직접 파싱한 뒤, 다시 같은 Pydantic 모델을 사용해 YAML 콘텐츠를 검증합니다:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | 팁
여기서는 같은 Pydantic 모델을 재사용합니다.
하지만 마찬가지로, 다른 방식으로 검증할 수도 있습니다.
///
================================================
FILE: docs/ko/docs/advanced/response-change-status-code.md
================================================
# 응답 - 상태 코드 변경 { #response-change-status-code }
기본 [응답 상태 코드 설정](../tutorial/response-status-code.md)이 가능하다는 걸 이미 알고 계실 겁니다.
하지만 경우에 따라 기본 설정과 다른 상태 코드를 반환해야 할 때가 있습니다.
## 사용 예 { #use-case }
예를 들어 기본적으로 HTTP 상태 코드 "OK" `200`을 반환하고 싶다고 가정해 봅시다.
하지만 데이터가 존재하지 않으면 이를 새로 생성하고, HTTP 상태 코드 "CREATED" `201`을 반환하고자 할 때가 있을 수 있습니다.
하지만 여전히 `response_model`을 사용하여 반환하는 데이터를 필터링하고 변환할 수 있기를 원합니다.
이런 경우에는 `Response` 파라미터를 사용할 수 있습니다.
## `Response` 파라미터 사용하기 { #use-a-response-parameter }
*경로 처리 함수*에 `Response` 타입의 파라미터를 선언할 수 있습니다. (쿠키와 헤더에 대해 선언하는 것과 유사하게)
그리고 이 *임시* 응답 객체에서 `status_code`를 설정할 수 있습니다.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
그리고 평소처럼 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
`response_model`을 선언했다면 반환된 객체는 여전히 필터링되고 변환됩니다.
**FastAPI**는 이 *임시* 응답 객체에서 상태 코드(쿠키와 헤더 포함)를 추출하여, `response_model`로 필터링된 반환 값을 포함하는 최종 응답에 넣습니다.
또한, 의존성에서도 `Response` 파라미터를 선언하고 그 안에서 상태 코드를 설정할 수 있습니다. 단, 마지막으로 설정된 상태 코드가 우선 적용된다는 점을 유의하세요.
================================================
FILE: docs/ko/docs/advanced/response-cookies.md
================================================
# 응답 쿠키 { #response-cookies }
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
*경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
그런 다음 해당 *임시* 응답 객체에서 쿠키를 설정할 수 있습니다.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
그런 다음 일반적으로 하듯이 필요한 어떤 객체든 반환할 수 있습니다(`dict`, 데이터베이스 모델 등).
그리고 `response_model`을 선언했다면 반환한 객체를 거르고 변환하는 데 여전히 사용됩니다.
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, `response_model`로 필터링된 반환 값이 포함된 최종 응답에 이를 넣습니다.
또한 의존관계에서 `Response` 매개변수를 선언하고, 해당 의존성에서 쿠키(및 헤더)를 설정할 수도 있습니다.
## `Response`를 직접 반환하기 { #return-a-response-directly }
코드에서 `Response`를 직접 반환할 때도 쿠키를 생성할 수 있습니다.
이를 위해 [Response를 직접 반환하기](response-directly.md)에서 설명한 대로 응답을 생성할 수 있습니다.
그런 다음 쿠키를 설정하고 반환하면 됩니다:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip
`Response` 매개변수를 사용하지 않고 응답을 직접 반환하는 경우, FastAPI는 이를 직접 반환한다는 점에 유의하세요.
따라서 데이터가 올바른 유형인지 확인해야 합니다. 예: `JSONResponse`를 반환하는 경우, JSON과 호환되는지 확인하세요.
또한 `response_model`로 필터링되어야 했던 데이터를 전송하지 않도록 하세요.
///
### 추가 정보 { #more-info }
/// note | 기술 세부사항
`from starlette.responses import Response` 또는 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 제공됩니다.
또한 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용되므로, **FastAPI**는 이를 `fastapi.Response`로도 제공합니다.
///
사용 가능한 모든 매개변수와 옵션은 [Starlette의 문서](https://www.starlette.dev/responses/#set-cookie)에서 확인할 수 있습니다.
================================================
FILE: docs/ko/docs/advanced/response-directly.md
================================================
# 응답을 직접 반환하기 { #return-a-response-directly }
**FastAPI**에서 *경로 처리(path operation)*를 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
[응답 모델](../tutorial/response-model.md)을 선언하면 FastAPI는 Pydantic을 사용해 데이터를 JSON으로 직렬화합니다.
응답 모델을 선언하지 않으면, FastAPI는 [JSON 호환 가능 인코더](../tutorial/encoder.md)에 설명된 `jsonable_encoder`를 사용해 데이터를 변환하고 이를 `JSONResponse`에 넣습니다.
또한 `JSONResponse`를 직접 생성해 반환할 수도 있습니다.
/// tip | 팁
일반적으로 `JSONResponse`를 직접 반환하는 것보다 [응답 모델](../tutorial/response-model.md)을 사용하는 편이 성능이 훨씬 좋습니다. 이렇게 하면 Pydantic이 Rust에서 데이터를 직렬화합니다.
///
## `Response` 반환하기 { #return-a-response }
`Response` 또는 그 하위 클래스를 반환할 수 있습니다.
/// info | 정보
`JSONResponse` 자체도 `Response`의 하위 클래스입니다.
///
그리고 `Response`를 반환하면 **FastAPI**가 이를 그대로 전달합니다.
Pydantic 모델로 데이터 변환을 수행하지 않으며, 내용을 다른 형식으로 변환하지 않습니다.
이로 인해 많은 유연성을 얻을 수 있습니다. 어떤 데이터 유형이든 반환할 수 있고, 데이터 선언이나 유효성 검사를 재정의할 수 있습니다.
또한 많은 책임도 따릅니다. 반환하는 데이터가 올바르고, 올바른 형식이며, 직렬화가 가능하도록 여러분이 직접 보장해야 합니다.
## `Response`에서 `jsonable_encoder` 사용하기 { #using-the-jsonable-encoder-in-a-response }
**FastAPI**는 반환하는 `Response`에 아무런 변경도 하지 않으므로, 그 내용이 준비되어 있는지 확인해야 합니다.
예를 들어, Pydantic 모델을 먼저 `dict`로 변환하고 `datetime`, `UUID` 등의 모든 데이터 타입을 JSON 호환 타입으로 변환하지 않으면 Pydantic 모델을 `JSONResponse`에 넣을 수 없습니다.
이러한 경우, 데이터를 응답에 전달하기 전에 `jsonable_encoder`를 사용하여 변환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | 기술 세부사항
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공합니다. 하지만 대부분의 사용 가능한 응답은 Starlette에서 직접 제공합니다.
///
## 사용자 정의 `Response` 반환하기 { #returning-a-custom-response }
위 예제는 필요한 모든 부분을 보여주지만, 아직은 그다지 유용하지 않습니다. `item`을 그냥 직접 반환했어도 **FastAPI**가 기본으로 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등의 작업을 모두 수행해 주었을 것이기 때문입니다.
이제, 이를 사용해 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
예를 들어 [XML](https://en.wikipedia.org/wiki/XML) 응답을 반환하고 싶다고 가정해 보겠습니다.
XML 내용을 문자열에 넣고, 이를 `Response`에 넣어 반환할 수 있습니다:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## 응답 모델 동작 방식 { #how-a-response-model-works }
경로 처리에서 [응답 모델 - 반환 타입](../tutorial/response-model.md)을 선언하면 **FastAPI**는 Pydantic을 사용해 데이터를 JSON으로 직렬화합니다.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
이는 Rust 측에서 처리되므로, 일반적인 Python과 `JSONResponse` 클래스로 수행하는 것보다 성능이 훨씬 좋습니다.
`response_model` 또는 반환 타입을 사용할 때 FastAPI는 `jsonable_encoder`로 데이터를 변환(이는 더 느립니다)하지도 않고, `JSONResponse` 클래스를 사용하지도 않습니다.
대신 응답 모델(또는 반환 타입)을 사용해 Pydantic이 생성한 JSON 바이트를 가져와, JSON에 맞는 미디어 타입(`application/json`)을 가진 `Response`를 직접 반환합니다.
## 참고 사항 { #notes }
`Response`를 직접 반환할 때, 그 데이터는 자동으로 유효성 검사되거나, 변환(직렬화)되거나, 문서화되지 않습니다.
그러나 [OpenAPI에서 추가 응답](additional-responses.md)에서 설명된 대로 문서화할 수 있습니다.
이후 섹션에서 자동 데이터 변환, 문서화 등을 계속 사용하면서 이러한 사용자 정의 `Response`를 사용하는/선언하는 방법을 확인할 수 있습니다.
================================================
FILE: docs/ko/docs/advanced/response-headers.md
================================================
# 응답 헤더 { #response-headers }
## `Response` 매개변수 사용하기 { #use-a-response-parameter }
여러분은 *경로 처리 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다 (쿠키와 같이 사용할 수 있습니다).
그런 다음, 여러분은 해당 *임시* 응답 객체에서 헤더를 설정할 수 있습니다.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
그 후, 일반적으로 사용하듯이 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
`response_model`을 선언한 경우, 반환한 객체를 필터링하고 변환하는 데 여전히 사용됩니다.
**FastAPI**는 해당 *임시* 응답에서 헤더(쿠키와 상태 코드도 포함)를 추출하여, 여러분이 반환한 값을 포함하는 최종 응답에 `response_model`로 필터링된 값을 넣습니다.
또한, 종속성에서 `Response` 매개변수를 선언하고 그 안에서 헤더(및 쿠키)를 설정할 수 있습니다.
## `Response` 직접 반환하기 { #return-a-response-directly }
`Response`를 직접 반환할 때에도 헤더를 추가할 수 있습니다.
[응답을 직접 반환하기](response-directly.md)에 설명한 대로 응답을 생성하고, 헤더를 추가 매개변수로 전달하세요:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | 기술 세부사항
`from starlette.responses import Response`나 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `starlette.responses`와 동일한 것을 `fastapi.responses`로도 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 제공합니다.
그리고 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용될 수 있으므로, **FastAPI**는 `fastapi.Response`로도 이를 제공합니다.
///
## 커스텀 헤더 { #custom-headers }
커스텀 사설 헤더는 [`X-` 접두어를 사용하여](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) 추가할 수 있다는 점을 기억하세요.
하지만, 여러분이 브라우저에서 클라이언트가 볼 수 있기를 원하는 커스텀 헤더가 있는 경우, CORS 설정에 이를 추가해야 합니다([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)에서 자세히 알아보세요). [Starlette의 CORS 문서](https://www.starlette.dev/middleware/#corsmiddleware)에 문서화된 `expose_headers` 매개변수를 사용하세요.
================================================
FILE: docs/ko/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
가장 단순한 경우에는 HTTP Basic Auth를 사용할 수 있습니다.
HTTP Basic Auth에서는 애플리케이션이 사용자명과 비밀번호가 들어 있는 헤더를 기대합니다.
이를 받지 못하면 HTTP 401 "Unauthorized" 오류를 반환합니다.
그리고 값이 `Basic`이고 선택적으로 `realm` 파라미터를 포함하는 `WWW-Authenticate` 헤더를 반환합니다.
이는 브라우저가 사용자명과 비밀번호를 입력하는 통합 프롬프트를 표시하도록 알려줍니다.
그다음 사용자명과 비밀번호를 입력하면, 브라우저가 자동으로 해당 값을 헤더에 담아 전송합니다.
## 간단한 HTTP Basic Auth { #simple-http-basic-auth }
* `HTTPBasic`과 `HTTPBasicCredentials`를 임포트합니다.
* `HTTPBasic`을 사용해 "`security` scheme"을 생성합니다.
* *경로 처리*에서 dependency로 해당 `security`를 사용합니다.
* `HTTPBasicCredentials` 타입의 객체를 반환합니다:
* 전송된 `username`과 `password`를 포함합니다.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
처음으로 URL을 열어보면(또는 문서에서 "Execute" 버튼을 클릭하면) 브라우저가 사용자명과 비밀번호를 물어봅니다:
## 사용자명 확인하기 { #check-the-username }
더 완전한 예시입니다.
dependency를 사용해 사용자명과 비밀번호가 올바른지 확인하세요.
이를 위해 Python 표준 모듈 [`secrets`](https://docs.python.org/3/library/secrets.html)를 사용해 사용자명과 비밀번호를 확인합니다.
`secrets.compare_digest()`는 `bytes` 또는 ASCII 문자(영어에서 사용하는 문자)만 포함한 `str`을 받아야 합니다. 즉, `Sebastián`의 `á` 같은 문자가 있으면 동작하지 않습니다.
이를 처리하기 위해 먼저 `username`과 `password`를 UTF-8로 인코딩해서 `bytes`로 변환합니다.
그런 다음 `secrets.compare_digest()`를 사용해 `credentials.username`이 `"stanleyjobson"`이고 `credentials.password`가 `"swordfish"`인지 확실히 확인할 수 있습니다.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
이는 다음과 비슷합니다:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# 어떤 오류를 반환
...
```
하지만 `secrets.compare_digest()`를 사용하면 "timing attacks"라고 불리는 한 유형의 공격에 대해 안전해집니다.
### 타이밍 공격 { #timing-attacks }
그렇다면 "timing attack"이란 무엇일까요?
공격자들이 사용자명과 비밀번호를 추측하려고 한다고 가정해봅시다.
그리고 사용자명 `johndoe`, 비밀번호 `love123`으로 요청을 보냅니다.
그러면 애플리케이션의 Python 코드는 대략 다음과 같을 것입니다:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
하지만 Python이 `johndoe`의 첫 글자 `j`를 `stanleyjobson`의 첫 글자 `s`와 비교하는 순간, 두 문자열이 같지 않다는 것을 이미 알게 되어 `False`를 반환합니다. 이는 “나머지 글자들을 비교하느라 계산을 더 낭비할 필요가 없다”고 판단하기 때문입니다. 그리고 애플리케이션은 "Incorrect username or password"라고 말합니다.
그런데 공격자들이 사용자명을 `stanleyjobsox`, 비밀번호를 `love123`으로 다시 시도합니다.
그러면 애플리케이션 코드는 다음과 비슷하게 동작합니다:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python은 두 문자열이 같지 않다는 것을 알아차리기 전까지 `stanleyjobsox`와 `stanleyjobson` 양쪽의 `stanleyjobso` 전체를 비교해야 합니다. 그래서 "Incorrect username or password"라고 응답하기까지 추가로 몇 마이크로초가 더 걸릴 것입니다.
#### 응답 시간은 공격자에게 도움이 됩니다 { #the-time-to-answer-helps-the-attackers }
이 시점에서 서버가 "Incorrect username or password" 응답을 보내는 데 몇 마이크로초 더 걸렸다는 것을 알아채면, 공격자들은 _무언가_ 맞았다는 것(초기 몇 글자가 맞았다는 것)을 알게 됩니다.
그리고 `johndoe`보다는 `stanleyjobsox`에 더 가까운 값을 시도해야 한다는 것을 알고 다시 시도할 수 있습니다.
#### "전문적인" 공격 { #a-professional-attack }
물론 공격자들은 이런 작업을 손으로 하지 않습니다. 보통 초당 수천~수백만 번 테스트할 수 있는 프로그램을 작성할 것이고, 한 번에 정답 글자 하나씩 추가로 얻어낼 수 있습니다.
그렇게 하면 몇 분 또는 몇 시간 만에, 응답에 걸린 시간만을 이용해(우리 애플리케이션의 “도움”을 받아) 올바른 사용자명과 비밀번호를 추측할 수 있게 됩니다.
#### `secrets.compare_digest()`로 해결하기 { #fix-it-with-secrets-compare-digest }
하지만 우리 코드는 실제로 `secrets.compare_digest()`를 사용하고 있습니다.
요약하면, `stanleyjobsox`와 `stanleyjobson`을 비교하는 데 걸리는 시간은 `johndoe`와 `stanleyjobson`을 비교하는 데 걸리는 시간과 같아집니다. 비밀번호도 마찬가지입니다.
이렇게 애플리케이션 코드에서 `secrets.compare_digest()`를 사용하면, 이러한 범위의 보안 공격 전반에 대해 안전해집니다.
### 오류 반환하기 { #return-the-error }
자격 증명이 올바르지 않다고 판단되면, 상태 코드 401(자격 증명이 제공되지 않았을 때와 동일)을 사용하는 `HTTPException`을 반환하고 브라우저가 로그인 프롬프트를 다시 표시하도록 `WWW-Authenticate` 헤더를 추가하세요:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/ko/docs/advanced/security/index.md
================================================
# 고급 보안 { #advanced-security }
## 추가 기능 { #additional-features }
[튜토리얼 - 사용자 가이드: 보안](../../tutorial/security/index.md)에서 다룬 내용 외에도, 보안을 처리하기 위한 몇 가지 추가 기능이 있습니다.
/// tip | 팁
다음 섹션들은 **반드시 "고급"이라고 할 수는 없습니다**.
그리고 사용 사례에 따라, 그중 하나에 해결책이 있을 수도 있습니다.
///
## 먼저 튜토리얼 읽기 { #read-the-tutorial-first }
다음 섹션은 주요 [튜토리얼 - 사용자 가이드: 보안](../../tutorial/security/index.md)을 이미 읽었다고 가정합니다.
모두 동일한 개념을 기반으로 하지만, 몇 가지 추가 기능을 사용할 수 있게 해줍니다.
================================================
FILE: docs/ko/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2 스코프 { #oauth2-scopes }
**FastAPI**에서 OAuth2 스코프를 직접 사용할 수 있으며, 자연스럽게 동작하도록 통합되어 있습니다.
이를 통해 OAuth2 표준을 따르는 더 세밀한 권한 시스템을 OpenAPI 애플리케이션(및 API 문서)에 통합할 수 있습니다.
스코프를 사용하는 OAuth2는 Facebook, Google, GitHub, Microsoft, X(Twitter) 등 많은 대형 인증 제공자가 사용하는 메커니즘입니다. 이들은 이를 통해 사용자와 애플리케이션에 특정 권한을 제공합니다.
Facebook, Google, GitHub, Microsoft, X(Twitter)로 “로그인”할 때마다, 해당 애플리케이션은 스코프가 있는 OAuth2를 사용하고 있습니다.
이 섹션에서는 **FastAPI** 애플리케이션에서 동일한 “스코프가 있는 OAuth2”로 인증(Authentication)과 인가(Authorization)를 관리하는 방법을 확인합니다.
/// warning | 경고
이 섹션은 다소 고급 내용입니다. 이제 막 시작했다면 건너뛰어도 됩니다.
OAuth2 스코프가 반드시 필요한 것은 아니며, 인증과 인가는 원하는 방식으로 처리할 수 있습니다.
하지만 스코프가 있는 OAuth2는 (OpenAPI와 함께) API 및 API 문서에 깔끔하게 통합될 수 있습니다.
그럼에도 불구하고, 해당 스코프(또는 그 밖의 어떤 보안/인가 요구사항이든)는 코드에서 필요에 맞게 직접 강제해야 합니다.
많은 경우 스코프가 있는 OAuth2는 과한 선택일 수 있습니다.
하지만 필요하다고 알고 있거나 궁금하다면 계속 읽어보세요.
///
## OAuth2 스코프와 OpenAPI { #oauth2-scopes-and-openapi }
OAuth2 사양은 “스코프(scopes)”를 공백으로 구분된 문자열 목록으로 정의합니다.
각 문자열의 내용은 어떤 형식이든 될 수 있지만, 공백을 포함하면 안 됩니다.
이 스코프들은 “권한”을 나타냅니다.
OpenAPI(예: API 문서)에서는 “security schemes”를 정의할 수 있습니다.
이 security scheme 중 하나가 OAuth2를 사용한다면, 스코프도 선언하고 사용할 수 있습니다.
각 “스코프”는 (공백 없는) 문자열일 뿐입니다.
보통 다음과 같이 특정 보안 권한을 선언하는 데 사용합니다:
* `users:read` 또는 `users:write` 는 흔한 예시입니다.
* `instagram_basic` 는 Facebook/Instagram에서 사용합니다.
* `https://www.googleapis.com/auth/drive` 는 Google에서 사용합니다.
/// info | 정보
OAuth2에서 “스코프”는 필요한 특정 권한을 선언하는 문자열일 뿐입니다.
`:` 같은 다른 문자가 있거나 URL이어도 상관없습니다.
그런 세부사항은 구현에 따라 달라집니다.
OAuth2 입장에서는 그저 문자열입니다.
///
## 전체 개요 { #global-view }
먼저, 메인 **튜토리얼 - 사용자 가이드**의 [비밀번호(및 해싱)를 사용하는 OAuth2, JWT 토큰을 사용하는 Bearer](../../tutorial/security/oauth2-jwt.md) 예제에서 어떤 부분이 바뀌는지 빠르게 살펴보겠습니다. 이제 OAuth2 스코프를 사용합니다:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
이제 변경 사항을 단계별로 살펴보겠습니다.
## OAuth2 보안 스킴 { #oauth2-security-scheme }
첫 번째 변경 사항은 이제 사용 가능한 스코프 2개(`me`, `items`)로 OAuth2 보안 스킴을 선언한다는 점입니다.
`scopes` 매개변수는 각 스코프를 키로 하고, 설명을 값으로 하는 `dict`를 받습니다:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
이제 스코프를 선언했기 때문에, 로그인/인가할 때 API 문서에 스코프가 표시됩니다.
그리고 접근을 허용할 스코프를 선택할 수 있게 됩니다: `me`와 `items`.
이는 Facebook, Google, GitHub 등으로 로그인하면서 권한을 부여할 때 사용되는 것과 동일한 메커니즘입니다:
## 스코프를 포함한 JWT 토큰 { #jwt-token-with-scopes }
이제 토큰 *경로 처리*를 수정해, 요청된 스코프를 반환하도록 합니다.
여전히 동일한 `OAuth2PasswordRequestForm`을 사용합니다. 여기에는 요청에서 받은 각 스코프를 담는 `scopes` 속성이 있으며, 타입은 `str`의 `list`입니다.
그리고 JWT 토큰의 일부로 스코프를 반환합니다.
/// danger | 위험
단순화를 위해, 여기서는 요청으로 받은 스코프를 그대로 토큰에 추가하고 있습니다.
하지만 실제 애플리케이션에서는 보안을 위해, 사용자가 실제로 가질 수 있는 스코프만(또는 미리 정의한 것만) 추가하도록 반드시 확인해야 합니다.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## *경로 처리*와 의존성에서 스코프 선언하기 { #declare-scopes-in-path-operations-and-dependencies }
이제 `/users/me/items/`에 대한 *경로 처리*가 스코프 `items`를 요구한다고 선언합니다.
이를 위해 `fastapi`에서 `Security`를 import하여 사용합니다.
`Security`는 (`Depends`처럼) 의존성을 선언하는 데 사용할 수 있지만, `Security`는 스코프(문자열) 목록을 받는 `scopes` 매개변수도 받습니다.
이 경우, 의존성 함수 `get_current_active_user`를 `Security`에 전달합니다(`Depends`로 할 때와 같은 방식).
하지만 스코프 `list`도 함께 전달합니다. 여기서는 스코프 하나만: `items`(더 많을 수도 있습니다).
또한 의존성 함수 `get_current_active_user`는 `Depends`뿐 아니라 `Security`로도 하위 의존성을 선언할 수 있습니다. 자체 하위 의존성 함수(`get_current_user`)와 추가 스코프 요구사항을 선언합니다.
이 경우에는 스코프 `me`를 요구합니다(여러 스코프를 요구할 수도 있습니다).
/// note | 참고
반드시 서로 다른 곳에 서로 다른 스코프를 추가해야 하는 것은 아닙니다.
여기서는 **FastAPI**가 서로 다른 레벨에서 선언된 스코프를 어떻게 처리하는지 보여주기 위해 이렇게 합니다.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | 기술 세부사항
`Security`는 실제로 `Depends`의 서브클래스이며, 나중에 보게 될 추가 매개변수 하나만 더 있습니다.
하지만 `Depends` 대신 `Security`를 사용하면, **FastAPI**는 보안 스코프를 선언할 수 있음을 알고 내부적으로 이를 사용하며, OpenAPI로 API를 문서화할 수 있습니다.
하지만 `fastapi`에서 `Query`, `Path`, `Depends`, `Security` 등을 import할 때, 이것들은 실제로 특수한 클래스를 반환하는 함수입니다.
///
## `SecurityScopes` 사용하기 { #use-securityscopes }
이제 의존성 `get_current_user`를 업데이트합니다.
이는 위의 의존성들이 사용하는 것입니다.
여기에서 앞서 만든 동일한 OAuth2 스킴을 의존성으로 선언하여 사용합니다: `oauth2_scheme`.
이 의존성 함수 자체에는 스코프 요구사항이 없기 때문에, `oauth2_scheme`와 함께 `Depends`를 사용할 수 있습니다. 보안 스코프를 지정할 필요가 없을 때는 `Security`를 쓸 필요가 없습니다.
또한 `fastapi.security`에서 import한 `SecurityScopes` 타입의 특별한 매개변수를 선언합니다.
이 `SecurityScopes` 클래스는 `Request`와 비슷합니다(`Request`는 요청 객체를 직접 얻기 위해 사용했습니다).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## `scopes` 사용하기 { #use-the-scopes }
매개변수 `security_scopes`의 타입은 `SecurityScopes`입니다.
여기에는 `scopes` 속성이 있으며, 자기 자신과 이 함수를 하위 의존성으로 사용하는 모든 의존성이 요구하는 스코프 전체를 담은 `list`를 가집니다. 즉, 모든 “dependants”... 다소 헷갈릴 수 있는데, 아래에서 다시 설명합니다.
`security_scopes` 객체(`SecurityScopes` 클래스)에는 또한 `scope_str` 속성이 있는데, 공백으로 구분된 단일 문자열로 스코프들을 담고 있습니다(이를 사용할 것입니다).
나중에 여러 지점에서 재사용(`raise`)할 수 있는 `HTTPException`을 생성합니다.
이 예외에는 필요한 스코프(있다면)를 공백으로 구분된 문자열(`scope_str`)로 포함합니다. 그리고 그 스코프 문자열을 `WWW-Authenticate` 헤더에 넣습니다(이는 사양의 일부입니다).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## `username`과 데이터 형태 검증하기 { #verify-the-username-and-data-shape }
`username`을 얻었는지 확인하고, 스코프를 추출합니다.
그런 다음 Pydantic 모델로 데이터를 검증합니다(`ValidationError` 예외를 잡습니다). JWT 토큰을 읽거나 Pydantic으로 데이터를 검증하는 과정에서 오류가 나면, 앞에서 만든 `HTTPException`을 raise합니다.
이를 위해 Pydantic 모델 `TokenData`에 새 속성 `scopes`를 추가합니다.
Pydantic으로 데이터를 검증하면, 예를 들어 스코프가 정확히 `str`의 `list`이고 `username`이 `str`인지 등을 보장할 수 있습니다.
예를 들어 `dict`나 다른 형태라면, 나중에 애플리케이션이 어느 시점에 깨지면서 보안 위험이 될 수 있습니다.
또한 해당 username을 가진 사용자가 있는지 확인하고, 없다면 앞에서 만든 동일한 예외를 raise합니다.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## `scopes` 검증하기 { #verify-the-scopes }
이제 이 의존성과 모든 dependant( *경로 처리* 포함)가 요구하는 모든 스코프가, 받은 토큰의 스코프에 포함되어 있는지 확인합니다. 그렇지 않으면 `HTTPException`을 raise합니다.
이를 위해, 모든 스코프를 `str`로 담고 있는 `security_scopes.scopes`를 사용합니다.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## 의존성 트리와 스코프 { #dependency-tree-and-scopes }
이 의존성 트리와 스코프를 다시 살펴보겠습니다.
`get_current_active_user` 의존성은 `get_current_user`를 하위 의존성으로 가지므로, `get_current_active_user`에서 선언된 스코프 `"me"`는 `get_current_user`에 전달되는 `security_scopes.scopes`의 요구 스코프 목록에 포함됩니다.
*경로 처리* 자체도 스코프 `"items"`를 선언하므로, 이것 또한 `get_current_user`에 전달되는 `security_scopes.scopes` 목록에 포함됩니다.
의존성과 스코프의 계층 구조는 다음과 같습니다:
* *경로 처리* `read_own_items`는:
* 의존성과 함께 요구 스코프 `["items"]`를 가집니다:
* `get_current_active_user`:
* 의존성 함수 `get_current_active_user`는:
* 의존성과 함께 요구 스코프 `["me"]`를 가집니다:
* `get_current_user`:
* 의존성 함수 `get_current_user`는:
* 자체적으로는 요구 스코프가 없습니다.
* `oauth2_scheme`를 사용하는 의존성이 있습니다.
* `SecurityScopes` 타입의 `security_scopes` 매개변수가 있습니다:
* 이 `security_scopes` 매개변수는 위에서 선언된 모든 스코프를 담은 `list`인 `scopes` 속성을 가지므로:
* *경로 처리* `read_own_items`의 경우 `security_scopes.scopes`에는 `["me", "items"]`가 들어갑니다.
* *경로 처리* `read_users_me`의 경우 `security_scopes.scopes`에는 `["me"]`가 들어갑니다. 이는 의존성 `get_current_active_user`에서 선언되기 때문입니다.
* *경로 처리* `read_system_status`의 경우 `security_scopes.scopes`에는 `[]`(없음)가 들어갑니다. `scopes`가 있는 `Security`를 선언하지 않았고, 그 의존성인 `get_current_user`도 `scopes`를 선언하지 않았기 때문입니다.
/// tip | 팁
여기서 중요한 “마법 같은” 점은 `get_current_user`가 각 *경로 처리*마다 검사해야 하는 `scopes` 목록이 달라진다는 것입니다.
이는 특정 *경로 처리*에 대한 의존성 트리에서, 각 *경로 처리*와 각 의존성에 선언된 `scopes`에 따라 달라집니다.
///
## `SecurityScopes`에 대한 추가 설명 { #more-details-about-securityscopes }
`SecurityScopes`는 어느 지점에서든, 그리고 여러 곳에서 사용할 수 있으며, “루트” 의존성에만 있어야 하는 것은 아닙니다.
`SecurityScopes`는 **해당 특정** *경로 처리*와 **해당 특정** 의존성 트리에 대해, 현재 `Security` 의존성과 모든 dependant에 선언된 보안 스코프를 항상 갖고 있습니다.
`SecurityScopes`에는 dependant가 선언한 모든 스코프가 포함되므로, 중앙의 의존성 함수에서 토큰이 필요한 스코프를 가지고 있는지 검증한 다음, 서로 다른 *경로 처리*에서 서로 다른 스코프 요구사항을 선언할 수 있습니다.
이들은 각 *경로 처리*마다 독립적으로 검사됩니다.
## 확인하기 { #check-it }
API 문서를 열면, 인증하고 인가할 스코프를 지정할 수 있습니다.
어떤 스코프도 선택하지 않으면 “인증”은 되지만, `/users/me/` 또는 `/users/me/items/`에 접근하려고 하면 권한이 충분하지 않다는 오류가 발생합니다. `/status/`에는 여전히 접근할 수 있습니다.
그리고 스코프 `me`는 선택했지만 스코프 `items`는 선택하지 않았다면, `/users/me/`에는 접근할 수 있지만 `/users/me/items/`에는 접근할 수 없습니다.
이는 사용자가 애플리케이션에 얼마나 많은 권한을 부여했는지에 따라, 제3자 애플리케이션이 사용자로부터 제공받은 토큰으로 이 *경로 처리*들 중 하나에 접근하려고 할 때 발생하는 상황과 같습니다.
## 제3자 통합에 대해 { #about-third-party-integrations }
이 예제에서는 OAuth2 “password” 플로우를 사용하고 있습니다.
이는 보통 자체 프론트엔드가 있는 우리 애플리케이션에 로그인할 때 적합합니다.
우리가 이를 통제하므로 `username`과 `password`를 받는 것을 신뢰할 수 있기 때문입니다.
하지만 다른 사람들이 연결할 OAuth2 애플리케이션(즉, Facebook, Google, GitHub 등과 동등한 인증 제공자를 만들고 있다면)을 구축한다면, 다른 플로우 중 하나를 사용해야 합니다.
가장 흔한 것은 implicit 플로우입니다.
가장 안전한 것은 code 플로우이지만, 더 많은 단계가 필요해 구현이 더 복잡합니다. 복잡하기 때문에 많은 제공자는 implicit 플로우를 권장하게 됩니다.
/// note | 참고
인증 제공자마다 자신들의 브랜드의 일부로 만들기 위해, 각 플로우를 서로 다른 방식으로 이름 붙이는 경우가 흔합니다.
하지만 결국, 동일한 OAuth2 표준을 구현하고 있는 것입니다.
///
**FastAPI**는 `fastapi.security.oauth2`에 이러한 모든 OAuth2 인증 플로우를 위한 유틸리티를 포함합니다.
## 데코레이터 `dependencies`에서의 `Security` { #security-in-decorator-dependencies }
[경로 처리 데코레이터의 의존성](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)에서 설명한 것처럼 데코레이터의 `dependencies` 매개변수에 `Depends`의 `list`를 정의할 수 있는 것과 같은 방식으로, 거기에서 `scopes`와 함께 `Security`를 사용할 수도 있습니다.
================================================
FILE: docs/ko/docs/advanced/settings.md
================================================
# 설정과 환경 변수 { #settings-and-environment-variables }
많은 경우 애플리케이션에는 외부 설정이나 구성(예: secret key, 데이터베이스 자격 증명, 이메일 서비스 자격 증명 등)이 필요할 수 있습니다.
이러한 설정 대부분은 데이터베이스 URL처럼 변동 가능(변경될 수 있음)합니다. 그리고 많은 설정은 secret처럼 민감할 수 있습니다.
이 때문에 보통 애플리케이션이 읽어들이는 환경 변수로 이를 제공하는 것이 일반적입니다.
/// tip | 팁
환경 변수를 이해하려면 [환경 변수](../environment-variables.md)를 읽어보세요.
///
## 타입과 검증 { #types-and-validation }
이 환경 변수들은 Python 외부에 있으며 다른 프로그램 및 시스템의 나머지 부분(그리고 Linux, Windows, macOS 같은 서로 다른 운영체제와도)과 호환되어야 하므로, 텍스트 문자열만 다룰 수 있습니다.
즉, Python에서 환경 변수로부터 읽어온 어떤 값이든 `str`이 되며, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다.
## Pydantic `Settings` { #pydantic-settings }
다행히 Pydantic은 환경 변수에서 오는 이러한 설정을 처리할 수 있는 훌륭한 유틸리티를 [Pydantic: Settings 관리](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)로 제공합니다.
### `pydantic-settings` 설치하기 { #install-pydantic-settings }
먼저 [가상 환경](../virtual-environments.md)을 만들고 활성화한 다음, `pydantic-settings` 패키지를 설치하세요:
### `Settings` 객체 만들기 { #create-the-settings-object }
Pydantic에서 `BaseSettings`를 import하고, Pydantic 모델과 매우 비슷하게 서브클래스를 만드세요.
Pydantic 모델과 같은 방식으로, 타입 어노테이션(그리고 필요하다면 기본값)과 함께 클래스 속성을 선언합니다.
다양한 데이터 타입, `Field()`로 추가 검증 등 Pydantic 모델에서 사용하는 동일한 검증 기능과 도구를 모두 사용할 수 있습니다.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | 팁
빠르게 복사/붙여넣기할 예시가 필요하다면, 이 예시는 사용하지 말고 아래의 마지막 예시를 사용하세요.
///
그 다음, 해당 `Settings` 클래스의 인스턴스(여기서는 `settings` 객체)를 생성하면 Pydantic이 대소문자를 구분하지 않고 환경 변수를 읽습니다. 따라서 대문자 변수 `APP_NAME`도 `app_name` 속성에 대해 읽힙니다.
이후 데이터를 변환하고 검증합니다. 그래서 그 `settings` 객체를 사용할 때는 선언한 타입의 데이터를 갖게 됩니다(예: `items_per_user`는 `int`가 됩니다).
### `settings` 사용하기 { #use-the-settings }
이제 애플리케이션에서 새 `settings` 객체를 사용할 수 있습니다:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### 서버 실행하기 { #run-the-server }
다음으로 환경 변수를 통해 구성을 전달하면서 서버를 실행합니다. 예를 들어 다음처럼 `ADMIN_EMAIL`과 `APP_NAME`을 설정할 수 있습니다:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | 팁
하나의 명령에 여러 env var를 설정하려면 공백으로 구분하고, 모두 명령 앞에 두세요.
///
그러면 `admin_email` 설정은 `"deadpool@example.com"`으로 설정됩니다.
`app_name`은 `"ChimichangApp"`이 됩니다.
그리고 `items_per_user`는 기본값 `50`을 유지합니다.
## 다른 모듈의 설정 { #settings-in-another-module }
[더 큰 애플리케이션 - 여러 파일](../tutorial/bigger-applications.md)에서 본 것처럼, 설정을 다른 모듈 파일에 넣을 수도 있습니다.
예를 들어 `config.py` 파일을 다음처럼 만들 수 있습니다:
{* ../../docs_src/settings/app01_py310/config.py *}
그리고 `main.py` 파일에서 이를 사용합니다:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | 팁
[더 큰 애플리케이션 - 여러 파일](../tutorial/bigger-applications.md)에서 본 것처럼 `__init__.py` 파일도 필요합니다.
///
## 의존성에서 설정 사용하기 { #settings-in-a-dependency }
어떤 경우에는 어디서나 사용되는 전역 `settings` 객체를 두는 대신, 의존성에서 설정을 제공하는 것이 유용할 수 있습니다.
이는 특히 테스트 중에 유용할 수 있는데, 사용자 정의 설정으로 의존성을 override하기가 매우 쉽기 때문입니다.
### config 파일 { #the-config-file }
이전 예시에서 이어서, `config.py` 파일은 다음과 같을 수 있습니다:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
이제는 기본 인스턴스 `settings = Settings()`를 생성하지 않는다는 점에 유의하세요.
### 메인 앱 파일 { #the-main-app-file }
이제 새로운 `config.Settings()`를 반환하는 의존성을 생성합니다.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | 팁
`@lru_cache`는 조금 뒤에 다룹니다.
지금은 `get_settings()`가 일반 함수라고 가정해도 됩니다.
///
그 다음 *경로 처리 함수*에서 이를 의존성으로 요구하고, 필요한 어디서든 사용할 수 있습니다.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### 설정과 테스트 { #settings-and-testing }
그 다음, `get_settings`에 대한 의존성 override를 만들어 테스트 중에 다른 설정 객체를 제공하기가 매우 쉬워집니다:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
의존성 override에서는 새 `Settings` 객체를 생성할 때 `admin_email`의 새 값을 설정하고, 그 새 객체를 반환합니다.
그 다음 그것이 사용되는지 테스트할 수 있습니다.
## `.env` 파일 읽기 { #reading-a-env-file }
많이 바뀔 수 있는 설정이 많고, 서로 다른 환경에서 사용한다면, 이를 파일에 넣어 환경 변수인 것처럼 읽는 것이 유용할 수 있습니다.
이 관행은 충분히 흔해서 이름도 있는데, 이러한 환경 변수들은 보통 `.env` 파일에 두며, 그 파일을 "dotenv"라고 부릅니다.
/// tip | 팁
점(`.`)으로 시작하는 파일은 Linux, macOS 같은 Unix 계열 시스템에서 숨김 파일입니다.
하지만 dotenv 파일이 꼭 그 정확한 파일명을 가져야 하는 것은 아닙니다.
///
Pydantic은 외부 라이브러리를 사용해 이런 유형의 파일에서 읽는 기능을 지원합니다. 자세한 내용은 [Pydantic Settings: Dotenv (.env) 지원](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support)을 참고하세요.
/// tip | 팁
이를 사용하려면 `pip install python-dotenv`가 필요합니다.
///
### `.env` 파일 { #the-env-file }
다음과 같은 `.env` 파일을 둘 수 있습니다:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### `.env`에서 설정 읽기 { #read-settings-from-env }
그리고 `config.py`를 다음처럼 업데이트합니다:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | 팁
`model_config` 속성은 Pydantic 설정을 위한 것입니다. 자세한 내용은 [Pydantic: 개념: 구성](https://docs.pydantic.dev/latest/concepts/config/)을 참고하세요.
///
여기서는 Pydantic `Settings` 클래스 안에 config `env_file`을 정의하고, 사용하려는 dotenv 파일의 파일명을 값으로 설정합니다.
### `lru_cache`로 `Settings`를 한 번만 생성하기 { #creating-the-settings-only-once-with-lru-cache }
디스크에서 파일을 읽는 것은 보통 비용이 큰(느린) 작업이므로, 각 요청마다 읽기보다는 한 번만 수행하고 동일한 settings 객체를 재사용하는 것이 좋습니다.
하지만 매번 다음을 수행하면:
```Python
Settings()
```
새 `Settings` 객체가 생성되고, 생성 시점에 `.env` 파일을 다시 읽게 됩니다.
의존성 함수가 단순히 다음과 같다면:
```Python
def get_settings():
return Settings()
```
요청마다 객체를 생성하게 되고, 요청마다 `.env` 파일을 읽게 됩니다. ⚠️
하지만 위에 `@lru_cache` 데코레이터를 사용하고 있으므로, `Settings` 객체는 최초 호출 시 딱 한 번만 생성됩니다. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
그 다음 요청들에서 의존성으로 `get_settings()`가 다시 호출될 때마다, `get_settings()`의 내부 코드를 실행해서 새 `Settings` 객체를 만드는 대신, 첫 호출에서 반환된 동일한 객체를 계속 반환합니다.
#### `lru_cache` 기술 세부사항 { #lru-cache-technical-details }
`@lru_cache`는 데코레이션한 함수가 매번 다시 계산하는 대신, 첫 번째에 반환된 동일한 값을 반환하도록 함수를 수정합니다(즉, 매번 함수 코드를 실행하지 않습니다).
따라서 아래의 함수는 인자 조합마다 한 번씩 실행됩니다. 그리고 각각의 인자 조합에 대해 반환된 값은, 함수가 정확히 같은 인자 조합으로 호출될 때마다 반복해서 사용됩니다.
예를 들어 다음 함수가 있다면:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
프로그램은 다음과 같이 실행될 수 있습니다:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
우리의 의존성 `get_settings()`의 경우, 함수가 어떤 인자도 받지 않으므로 항상 같은 값을 반환합니다.
이렇게 하면 거의 전역 변수처럼 동작합니다. 하지만 의존성 함수를 사용하므로 테스트를 위해 쉽게 override할 수 있습니다.
`@lru_cache`는 Python 표준 라이브러리의 `functools`에 포함되어 있으며, 자세한 내용은 [`@lru_cache`에 대한 Python 문서](https://docs.python.org/3/library/functools.html#functools.lru_cache)에서 확인할 수 있습니다.
## 정리 { #recap }
Pydantic Settings를 사용하면 Pydantic 모델의 모든 강력한 기능을 활용해 애플리케이션의 설정 또는 구성을 처리할 수 있습니다.
* 의존성을 사용하면 테스트를 단순화할 수 있습니다.
* `.env` 파일을 사용할 수 있습니다.
* `@lru_cache`를 사용하면 각 요청마다 dotenv 파일을 반복해서 읽는 것을 피하면서도, 테스트 중에는 이를 override할 수 있습니다.
================================================
FILE: docs/ko/docs/advanced/stream-data.md
================================================
# 데이터 스트리밍 { #stream-data }
JSON으로 구조화할 수 있는 데이터를 스트리밍하려면 [JSON Lines 스트리밍](../tutorial/stream-json-lines.md)을 사용하세요.
하지만 순수 바이너리 데이터나 문자열을 스트리밍하려면 다음과 같이 하면 됩니다.
/// info | 정보
FastAPI 0.134.0에 추가되었습니다.
///
## 사용 예시 { #use-cases }
예를 들어 AI LLM 서비스의 출력에서 바로 순수 문자열을 스트리밍하고 싶다면 이를 사용할 수 있습니다.
또한 큰 바이너리 파일을 스트리밍하는 데 사용할 수 있습니다. 한 번에 모두 메모리로 읽지 않고, 읽는 즉시 데이터 청크를 순차적으로 스트리밍합니다.
이 방식으로 비디오나 오디오를 스트리밍할 수도 있으며, 처리하면서 생성된 데이터를 곧바로 전송할 수도 있습니다.
## `yield`와 함께 `StreamingResponse` 사용하기 { #a-streamingresponse-with-yield }
경로 처리 함수에서 `response_class=StreamingResponse`를 선언하면 `yield`를 사용해 데이터 청크를 순차적으로 보낼 수 있습니다.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI는 각 데이터 청크를 있는 그대로 `StreamingResponse`에 전달하며, JSON 등으로 변환하려고 하지 않습니다.
### async가 아닌 경로 처리 함수 { #non-async-path-operation-functions }
`async`가 없는 일반 `def` 함수에서도 동일하게 `yield`를 사용할 수 있습니다.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### 타입 애너테이션 생략하기 { #no-annotation }
바이너리 데이터를 스트리밍할 때는 반환 타입 애너테이션을 굳이 선언할 필요가 없습니다.
FastAPI는 데이터를 Pydantic으로 JSON으로 변환하거나 어떤 방식으로든 직렬화하지 않으므로, 이 경우 타입 애너테이션은 편집기나 도구를 위한 용도일 뿐이며 FastAPI에서는 사용되지 않습니다.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
이는 곧 `StreamingResponse`를 사용할 때 타입 애너테이션과 무관하게, 전송 기준에 맞춰 바이트 데이터를 생성하고 인코딩할 자유와 책임이 여러분에게 있음을 의미합니다. 🤓
### 바이트 스트리밍 { #stream-bytes }
주요 사용 사례 중 하나는 문자열 대신 `bytes`를 스트리밍하는 것입니다. 물론 그렇게 할 수 있습니다.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## 사용자 정의 `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
위 예시에서는 바이트 데이터를 스트리밍했지만, 응답에 `Content-Type` 헤더가 없어 클라이언트는 어떤 유형의 데이터를 받는지 알 수 없습니다.
스트리밍하는 데이터 유형에 맞춰 `Content-Type` 헤더를 설정하는 `StreamingResponse`의 하위 클래스를 직접 만들 수 있습니다.
예를 들어 `media_type` 속성을 사용해 `Content-Type` 헤더를 `image/png`로 설정하는 `PNGStreamingResponse`를 만들 수 있습니다:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
그런 다음 경로 처리 함수에서 `response_class=PNGStreamingResponse`로 이 새 클래스를 사용할 수 있습니다:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### 파일 시뮬레이션 { #simulate-a-file }
이 예시에서는 `io.BytesIO`로 파일을 시뮬레이션합니다. 이는 메모리에서만 존재하지만 파일과 동일한 인터페이스를 제공하는 파일 유사 객체입니다.
예를 들어 실제 파일처럼 내용을 소비하기 위해 순회(iterate)할 수 있습니다.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | 기술 세부사항
다른 두 변수 `image_base64`와 `binary_image`는 이미지를 Base64로 인코딩한 뒤 바이트로 변환한 것이며, 이를 `io.BytesIO`에 전달합니다.
이 예시에서 하나의 파일 안에 모두 담아, 그대로 복사해 실행할 수 있도록 하기 위한 목적입니다. 🥚
///
`with` 블록을 사용하면 제너레이터 함수(`yield`가 있는 함수)가 끝난 뒤, 즉 응답 전송이 완료된 후 파일 유사 객체가 닫히도록 보장합니다.
이 예시처럼 메모리 상의 가짜 파일(`io.BytesIO`)이라면 크게 중요하지 않지만, 실제 파일의 경우 작업이 끝난 뒤 파일을 닫는 것이 매우 중요합니다.
### 파일과 비동기 { #files-and-async }
대부분의 경우 파일 유사 객체는 기본적으로 async/await와 호환되지 않습니다.
예를 들어 `await file.read()`나 `async for chunk in file`과 같은 패턴을 지원하지 않습니다.
또한 디스크나 네트워크에서 읽기 때문에, 많은 경우 읽기 작업은 이벤트 루프를 막을 수 있는 블로킹 연산입니다.
/// info | 정보
위의 예시는 예외적인 경우입니다. `io.BytesIO` 객체는 이미 메모리에 있으므로 읽기가 아무 것도 차단하지 않습니다.
하지만 실제 파일이나 파일 유사 객체를 읽을 때는 블로킹되는 경우가 많습니다.
///
이벤트 루프가 블로킹되는 것을 피하려면 경로 처리 함수를 `async def` 대신 일반 `def`로 선언하세요. 그러면 FastAPI가 스레드풀 워커에서 실행하여 메인 루프가 막히지 않도록 합니다.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | 팁
비동기 함수 안에서 블로킹 코드를 호출해야 하거나, 반대로 블로킹 함수 안에서 비동기 함수를 호출해야 한다면 FastAPI의 형제 라이브러리인 [Asyncer](https://asyncer.tiangolo.com)를 사용할 수 있습니다.
///
### `yield from` { #yield-from }
파일 유사 객체처럼 어떤 것을 순회하면서 각 항목마다 `yield`를 하는 대신, `yield from`을 사용해 각 항목을 직접 전달하고 `for` 루프를 생략할 수 있습니다.
이는 FastAPI에 특화된 기능이 아니라 순수한 파이썬 기능이지만, 알아두면 유용한 트릭입니다. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/ko/docs/advanced/strict-content-type.md
================================================
# 엄격한 Content-Type 확인 { #strict-content-type-checking }
기본적으로 **FastAPI**는 JSON 요청 본문에 대해 엄격한 `Content-Type` 헤더 검사를 사용합니다. 이는 JSON 요청의 본문을 JSON으로 파싱하려면 유효한 `Content-Type` 헤더(예: `application/json`)를 반드시 포함해야 함을 의미합니다.
## CSRF 위험 { #csrf-risk }
이 기본 동작은 매우 특정한 시나리오에서 **Cross-Site Request Forgery (CSRF)** 공격의 한 유형에 대한 보호를 제공합니다.
이러한 공격은 브라우저가 다음과 같은 경우 CORS 사전 요청(preflight) 검사를 수행하지 않고 스크립트가 요청을 보내도록 허용한다는 점을 악용합니다:
- `Content-Type` 헤더가 없음(예: `Blob` 본문과 함께 `fetch()` 사용)
- 그리고 어떠한 인증 자격 증명도 보내지 않음
이 유형의 공격은 주로 다음과 같은 경우에 관련이 있습니다:
- 애플리케이션이 로컬(예: `localhost`) 또는 내부 네트워크에서 실행 중이고
- 애플리케이션에 인증이 없어 같은 네트워크에서 오는 모든 요청을 신뢰한다고 가정하는 경우
## 공격 예시 { #example-attack }
로컬 AI 에이전트를 실행하는 방법을 만들었다고 가정해 봅시다.
이 에이전트는 다음 위치에 API를 제공합니다:
```
http://localhost:8000/v1/agents/multivac
```
또한 다음 위치에 프론트엔드가 있습니다:
```
http://localhost:8000
```
/// tip | 팁
두 주소 모두 같은 호스트를 사용합니다.
///
그런 다음 프론트엔드를 통해 AI 에이전트가 여러분을 대신해 작업을 수행하도록 할 수 있습니다.
이는 공개 인터넷이 아니라 로컬에서 실행되므로, 여러분은 로컬 네트워크 접근만을 신뢰하고 별도의 인증을 설정하지 않기로 합니다.
그 후 사용자는 이를 설치해 로컬에서 실행할 수 있습니다.
그리고 다음과 같은 악성 웹사이트를 열 수 있습니다:
```
https://evilhackers.example.com
```
그 악성 웹사이트가 `Blob` 본문으로 `fetch()`를 사용해 로컬 API로 요청을 보냅니다:
```
http://localhost:8000/v1/agents/multivac
```
악성 웹사이트의 호스트와 로컬 앱의 호스트가 다르더라도, 브라우저는 다음과 같은 이유로 CORS 사전 요청(preflight)을 트리거하지 않습니다:
- 인증 없이 동작하므로 자격 증명을 보낼 필요가 없습니다.
- 브라우저는 JSON을 보내지 않는다고 판단합니다(`Content-Type` 헤더가 없기 때문).
그러면 악성 웹사이트가 로컬 AI 에이전트로 하여금 사용자의 전 직장 상사에게 화난 메시지를 보내게 하거나... 더 나쁜 일을 시킬 수도 있습니다. 😅
## 공개 인터넷 { #open-internet }
여러분의 앱이 공개 인터넷에 있다면, '네트워크를 신뢰'하여 누구나 인증 없이 권한 있는 요청을 보내도록 두지는 않을 것입니다.
공격자는 브라우저 상호작용 없이도 스크립트를 실행해 여러분의 API로 요청을 보낼 수 있으므로, 아마 이미 권한이 필요한 엔드포인트는 보호하고 있을 것입니다.
그런 경우에는 이 공격/위험은 해당하지 않습니다.
이 위험과 공격은 주로 앱이 **로컬 네트워크**에서 실행되고 그것이 **유일한 보호수단**이라고 가정할 때 관련이 있습니다.
## Content-Type 없이 요청 허용하기 { #allowing-requests-without-content-type }
만약 `Content-Type` 헤더를 보내지 않는 클라이언트를 지원해야 한다면, `strict_content_type=False`로 설정해 엄격한 검사를 비활성화할 수 있습니다:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
이 설정을 사용하면 `Content-Type` 헤더가 없는 요청도 본문이 JSON으로 파싱됩니다. 이는 이전 버전의 FastAPI와 동일한 동작입니다.
/// info | 정보
이 동작과 설정은 FastAPI 0.132.0에 추가되었습니다.
///
================================================
FILE: docs/ko/docs/advanced/sub-applications.md
================================================
# 하위 애플리케이션 - 마운트 { #sub-applications-mounts }
각각의 독립적인 OpenAPI와 문서 UI를 갖는 두 개의 독립적인 FastAPI 애플리케이션이 필요하다면, 메인 앱을 두고 하나(또는 그 이상)의 하위 애플리케이션을 "마운트"할 수 있습니다.
## **FastAPI** 애플리케이션 마운트 { #mounting-a-fastapi-application }
"마운트"란 완전히 "독립적인" 애플리케이션을 특정 경로에 추가하고, 그 하위 애플리케이션에 선언된 _경로 처리_로 해당 경로 아래의 모든 것을 처리하도록 하는 것을 의미합니다.
### 최상위 애플리케이션 { #top-level-application }
먼저, 메인 최상위 **FastAPI** 애플리케이션과 그 *경로 처리*를 생성합니다:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### 하위 애플리케이션 { #sub-application }
그 다음, 하위 애플리케이션과 그 *경로 처리*를 생성합니다.
이 하위 애플리케이션은 또 다른 표준 FastAPI 애플리케이션이지만, "마운트"될 애플리케이션입니다:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### 하위 애플리케이션 마운트 { #mount-the-sub-application }
최상위 애플리케이션 `app`에서 하위 애플리케이션 `subapi`를 마운트합니다.
이 경우 `/subapi` 경로에 마운트됩니다:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### 자동 API 문서 확인 { #check-the-automatic-api-docs }
이제 `fastapi` 명령어를 실행하세요:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
그리고 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)에서 문서를 여세요.
메인 앱의 자동 API 문서를 보게 될 것이며, 메인 앱 자체의 _경로 처리_만 포함됩니다:
그 다음, [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs)에서 하위 애플리케이션의 문서를 여세요.
하위 애플리케이션의 자동 API 문서를 보게 될 것이며, 하위 경로 접두사 `/subapi` 아래에 올바르게 포함된 하위 애플리케이션 자체의 _경로 처리_만 포함됩니다:
두 사용자 인터페이스 중 어느 것과 상호작용을 시도하더라도 올바르게 동작할 것입니다. 브라우저가 각 특정 앱 또는 하위 앱과 통신할 수 있기 때문입니다.
### 기술적 세부사항: `root_path` { #technical-details-root-path }
위에서 설명한 대로 하위 애플리케이션을 마운트하면, FastAPI는 ASGI 명세의 메커니즘인 `root_path`를 사용해 하위 애플리케이션에 대한 마운트 경로를 전달하는 작업을 처리합니다.
이렇게 하면 하위 애플리케이션은 문서 UI를 위해 해당 경로 접두사를 사용해야 한다는 것을 알게 됩니다.
또한 하위 애플리케이션도 자체적으로 하위 앱을 마운트할 수 있으며, FastAPI가 이 모든 `root_path`를 자동으로 처리하기 때문에 모든 것이 올바르게 동작합니다.
`root_path`와 이를 명시적으로 사용하는 방법에 대해서는 [프록시 뒤](behind-a-proxy.md) 섹션에서 더 알아볼 수 있습니다.
================================================
FILE: docs/ko/docs/advanced/templates.md
================================================
# 템플릿 { #templates }
**FastAPI**와 함께 원하는 어떤 템플릿 엔진도 사용할 수 있습니다.
일반적인 선택은 Jinja2로, Flask와 다른 도구에서도 사용됩니다.
설정을 쉽게 할 수 있는 유틸리티가 있으며, 이를 **FastAPI** 애플리케이션에서 직접 사용할 수 있습니다(Starlette 제공).
## 의존성 설치 { #install-dependencies }
[가상 환경](../virtual-environments.md)을 생성하고, 활성화한 후 `jinja2`를 설치해야 합니다:
/// info | 정보
패키지 의존성을 정의하고 설치하는 다른 형식과 도구도 있습니다.
///
### **FastAPI** 코드 생성하기 { #create-the-fastapi-code }
* `app` 디렉터리를 만들고 들어갑니다.
* 빈 파일 `__init__.py`를 만듭니다.
* 다음 내용으로 `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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
이제 같은 프로젝트 디렉터리에 다음 내용으로 `Dockerfile` 파일을 만듭니다:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. 공식 Python 베이스 이미지에서 시작합니다.
2. 현재 작업 디렉터리를 `/code`로 설정합니다.
여기에 `requirements.txt` 파일과 `app` 디렉터리를 둘 것입니다.
3. 요구사항 파일을 `/code` 디렉터리로 복사합니다.
처음에는 요구사항 파일만 **단독으로** 복사하고, 나머지 코드는 복사하지 않습니다.
이 파일은 **자주 바뀌지 않기** 때문에 Docker는 이를 감지하여 이 단계에서 **캐시**를 사용하고, 다음 단계에서도 캐시를 사용할 수 있게 해줍니다.
4. 요구사항 파일에 있는 패키지 의존성을 설치합니다.
`--no-cache-dir` 옵션은 `pip`가 다운로드한 패키지를 로컬에 저장하지 않도록 합니다. 이는 `pip`가 같은 패키지를 설치하기 위해 다시 실행될 때만 의미가 있지만, 컨테이너 작업에서는 그렇지 않기 때문입니다.
/// note | 참고
`--no-cache-dir`는 `pip`에만 관련되어 있으며 Docker나 컨테이너와는 관련이 없습니다.
///
`--upgrade` 옵션은 이미 설치된 패키지가 있다면 `pip`가 이를 업그레이드하도록 합니다.
이전 단계에서 파일을 복사한 것이 **Docker 캐시**에 의해 감지될 수 있으므로, 이 단계에서도 가능하면 **Docker 캐시를 사용**합니다.
이 단계에서 캐시를 사용하면 개발 중에 이미지를 반복해서 빌드할 때, 의존성을 **매번 다운로드하고 설치하는** 대신 많은 **시간**을 **절약**할 수 있습니다.
5. `./app` 디렉터리를 `/code` 디렉터리 안으로 복사합니다.
이 디렉터리에는 **가장 자주 변경되는** 코드가 모두 포함되어 있으므로, Docker **캐시**는 이 단계나 **이후 단계들**에서는 쉽게 사용되지 않습니다.
따라서 컨테이너 이미지 빌드 시간을 최적화하려면 `Dockerfile`의 **끝부분 근처**에 두는 것이 중요합니다.
6. 내부적으로 Uvicorn을 사용하는 `fastapi run`을 사용하도록 **명령**을 설정합니다.
`CMD`는 문자열 리스트를 받으며, 각 문자열은 커맨드 라인에서 공백으로 구분해 입력하는 항목들입니다.
이 명령은 **현재 작업 디렉터리**에서 실행되며, 이는 위에서 `WORKDIR /code`로 설정한 `/code` 디렉터리와 같습니다.
/// tip | 팁
코드의 각 숫자 버블을 클릭해 각 줄이 하는 일을 확인하세요. 👆
///
/// warning | 경고
아래에서 설명하는 것처럼 `CMD` 지시어는 **항상** **exec form**을 사용해야 합니다.
///
#### `CMD` 사용하기 - Exec Form { #use-cmd-exec-form }
Docker 지시어 [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd)는 두 가지 형식으로 작성할 수 있습니다:
✅ **Exec** form:
```Dockerfile
# ✅ 이렇게 하세요
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ 이렇게 하지 마세요
CMD fastapi run app/main.py --port 80
```
FastAPI가 정상적으로 종료(graceful shutdown)되고 [lifespan 이벤트](../advanced/events.md)가 트리거되도록 하려면, 항상 **exec** form을 사용하세요.
자세한 내용은 [shell and exec form에 대한 Docker 문서](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form)를 참고하세요.
이는 `docker compose`를 사용할 때 꽤 눈에 띌 수 있습니다. 좀 더 기술적인 상세 내용은 Docker Compose FAQ 섹션을 참고하세요: [Why do my services take 10 seconds to recreate or stop?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### 디렉터리 구조 { #directory-structure }
이제 다음과 같은 디렉터리 구조가 되어야 합니다:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### TLS 종료 프록시의 배후 { #behind-a-tls-termination-proxy }
Nginx나 Traefik 같은 TLS 종료 프록시(로드 밸런서) 뒤에서 컨테이너를 실행하고 있다면 `--proxy-headers` 옵션을 추가하세요. 이 옵션은 (FastAPI CLI를 통해) Uvicorn에게 해당 프록시가 보낸 헤더를 신뢰하도록 하여, 애플리케이션이 HTTPS 뒤에서 실행 중임을 알게 합니다.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### 도커 캐시 { #docker-cache }
이 `Dockerfile`에는 중요한 트릭이 있습니다. 먼저 **의존성 파일만** 복사하고, 나머지 코드는 복사하지 않는 것입니다. 왜 그런지 설명하겠습니다.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker와 다른 도구들은 `Dockerfile`의 위에서부터 시작해, 각 지시어가 만든 파일을 포함하며 **레이어를 하나씩 위에 쌓는 방식으로** 컨테이너 이미지를 **점진적으로** 빌드합니다.
Docker와 유사한 도구들은 이미지를 빌드할 때 **내부 캐시**도 사용합니다. 어떤 파일이 마지막으로 컨테이너 이미지를 빌드했을 때부터 바뀌지 않았다면, 파일을 다시 복사하고 새 레이어를 처음부터 만드는 대신, 이전에 만든 **같은 레이어를 재사용**합니다.
파일 복사를 피하는 것만으로 큰 개선이 생기지는 않을 수 있지만, 해당 단계에서 캐시를 사용했기 때문에 **다음 단계에서도 캐시를 사용할 수** 있습니다. 예를 들어 다음과 같이 의존성을 설치하는 지시어에서 캐시를 사용할 수 있습니다:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
패키지 요구사항 파일은 **자주 변경되지 않습니다**. 따라서 그 파일만 복사하면 Docker는 그 단계에서 **캐시를 사용할 수** 있습니다.
그리고 Docker는 그 다음 단계에서 의존성을 다운로드하고 설치할 때도 **캐시를 사용할 수** 있습니다. 바로 여기에서 **많은 시간을 절약**하게 됩니다. ✨ ...그리고 기다리며 지루해지는 것도 피할 수 있습니다. 😪😆
패키지 의존성을 다운로드하고 설치하는 데에는 **몇 분**이 걸릴 수 있지만, **캐시**를 사용하면 많아야 **몇 초**면 끝납니다.
또한 개발 중에 코드 변경 사항이 동작하는지 확인하기 위해 컨테이너 이미지를 계속 빌드하게 되므로, 이렇게 절약되는 시간은 누중되어 상당히 커집니다.
그 다음 `Dockerfile`의 끝부분 근처에서 모든 코드를 복사합니다. 이 부분은 **가장 자주 변경되는** 부분이므로, 거의 항상 이 단계 이후에는 캐시를 사용할 수 없기 때문에 끝부분에 둡니다.
```Dockerfile
COPY ./app /code/app
```
### 도커 이미지 생성하기 { #build-the-docker-image }
이제 모든 파일이 제자리에 있으니 컨테이너 이미지를 빌드해봅시다.
* 프로젝트 디렉터리로 이동합니다(`Dockerfile`이 있고 `app` 디렉터리를 포함하는 위치).
* FastAPI 이미지를 빌드합니다:
/// tip | 팁
끝에 있는 `.`에 주목하세요. 이는 `./`와 동일하며, Docker에게 컨테이너 이미지를 빌드할 때 사용할 디렉터리를 알려줍니다.
이 경우 현재 디렉터리(`.`)입니다.
///
### 도커 컨테이너 시작하기 { #start-the-docker-container }
* 여러분의 이미지에 기반하여 컨테이너를 실행합니다:
## 확인하기 { #check-it }
Docker 컨테이너의 URL에서 확인할 수 있어야 합니다. 예를 들어: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) 또는 [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery)(또는 Docker 호스트를 사용해 동등하게 확인할 수 있습니다).
아래와 같은 것을 보게 될 것입니다:
```JSON
{"item_id": 5, "q": "somequery"}
```
## 인터랙티브 API 문서 { #interactive-api-docs }
이제 [http://192.168.99.100/docs](http://192.168.99.100/docs) 또는 [http://127.0.0.1/docs](http://127.0.0.1/docs)(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수 있습니다.
자동으로 생성된 인터랙티브 API 문서([Swagger UI](https://github.com/swagger-api/swagger-ui) 제공)를 볼 수 있습니다:

## 대안 API 문서 { #alternative-api-docs }
또한 [http://192.168.99.100/redoc](http://192.168.99.100/redoc) 또는 [http://127.0.0.1/redoc](http://127.0.0.1/redoc)(또는 Docker 호스트를 사용해 동등하게 접근)로 이동할 수도 있습니다.
대안 자동 문서([ReDoc](https://github.com/Rebilly/ReDoc) 제공)를 볼 수 있습니다:

## 단일 파일 FastAPI로 도커 이미지 빌드하기 { #build-a-docker-image-with-a-single-file-fastapi }
FastAPI가 단일 파일(예: `./app` 디렉터리 없이 `main.py`만 있는 경우)이라면, 파일 구조는 다음과 같을 수 있습니다:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
그런 다음 `Dockerfile`에서 해당 파일을 복사하도록 경로만 맞게 변경하면 됩니다:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. `main.py` 파일을 `/code` 디렉터리로 직접 복사합니다(`./app` 디렉터리 없이).
2. 단일 파일 `main.py`에 있는 애플리케이션을 제공(serve)하기 위해 `fastapi run`을 사용합니다.
`fastapi run`에 파일을 전달하면, 이것이 패키지의 일부가 아닌 단일 파일이라는 것을 자동으로 감지하고, 어떻게 임포트해서 FastAPI 앱을 제공할지 알아냅니다. 😎
## 배포 개념 { #deployment-concepts }
컨테이너 관점에서 같은 [배포 개념](concepts.md)들을 다시 이야기해 봅시다.
컨테이너는 주로 애플리케이션의 **빌드 및 배포** 과정을 단순화하는 도구이지만, 이러한 **배포 개념**을 처리하는 특정 접근 방식을 강제하지는 않으며, 가능한 전략은 여러 가지입니다.
**좋은 소식**은 각 전략마다 모든 배포 개념을 다룰 수 있는 방법이 있다는 점입니다. 🎉
컨테이너 관점에서 이 **배포 개념**들을 살펴봅시다:
* HTTPS
* 시작 시 자동 실행
* 재시작
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작 전 사전 단계
## HTTPS { #https }
FastAPI 애플리케이션의 **컨테이너 이미지**(그리고 나중에 실행 중인 **컨테이너**)에만 집중한다면, HTTPS는 보통 다른 도구에 의해 **외부적으로** 처리됩니다.
예를 들어 [Traefik](https://traefik.io/)을 사용하는 다른 컨테이너가 **HTTPS**와 **인증서**의 **자동** 획득을 처리할 수 있습니다.
/// tip | 팁
Traefik은 Docker, Kubernetes 등과 통합되어 있어, 이를 사용해 컨테이너에 HTTPS를 설정하고 구성하기가 매우 쉽습니다.
///
또는 HTTPS를 클라우드 제공자가 서비스의 일부로 처리할 수도 있습니다(애플리케이션은 여전히 컨테이너에서 실행됩니다).
## 시작 시 자동 실행과 재시작 { #running-on-startup-and-restarts }
보통 컨테이너를 **시작하고 실행**하는 역할을 담당하는 다른 도구가 있습니다.
직접 **Docker**일 수도 있고, **Docker Compose**, **Kubernetes**, **클라우드 서비스** 등일 수도 있습니다.
대부분(또는 전부)의 경우, 시작 시 컨테이너를 실행하고 실패 시 재시작을 활성화하는 간단한 옵션이 있습니다. 예를 들어 Docker에서는 커맨드 라인 옵션 `--restart`입니다.
컨테이너를 사용하지 않으면 애플리케이션을 시작 시 자동 실행하고 재시작까지 구성하는 것이 번거롭고 어렵습니다. 하지만 **컨테이너로 작업할 때**는 대부분의 경우 그 기능이 기본으로 포함되어 있습니다. ✨
## 복제 - 프로세스 개수 { #replication-number-of-processes }
**Kubernetes**, Docker Swarm Mode, Nomad 등의 복잡한 시스템으로 여러 머신에 분산된 컨테이너를 관리하는 클러스터를 사용한다면, 각 컨테이너에서(**워커를 사용하는 Uvicorn** 같은) **프로세스 매니저**를 쓰는 대신, **클러스터 레벨**에서 **복제를 처리**하고 싶을 가능성이 큽니다.
Kubernetes 같은 분산 컨테이너 관리 시스템은 보통 들어오는 요청에 대한 **로드 밸런싱**을 지원하면서도, **컨테이너 복제**를 처리하는 통합된 방법을 가지고 있습니다. 모두 **클러스터 레벨**에서요.
그런 경우에는 [위에서 설명한 대로](#dockerfile) 의존성을 설치하고, 여러 Uvicorn 워커를 사용하는 대신 **단일 Uvicorn 프로세스**를 실행하는 **처음부터 만든 Docker 이미지**를 사용하는 것이 좋을 것입니다.
### 로드 밸런서 { #load-balancer }
컨테이너를 사용할 때는 보통 **메인 포트에서 대기(listening)하는** 컴포넌트가 있습니다. **HTTPS**를 처리하기 위한 **TLS 종료 프록시** 역할을 하는 다른 컨테이너일 수도 있고, 유사한 도구일 수도 있습니다.
이 컴포넌트가 요청의 **부하(load)**를 받아 워커들에 (가능하면) **균형 있게** 분산한다면, 보통 **로드 밸런서**라고 부릅니다.
/// tip | 팁
HTTPS에 사용되는 동일한 **TLS 종료 프록시** 컴포넌트가 **로드 밸런서**이기도 한 경우가 많습니다.
///
또한 컨테이너로 작업할 때, 이를 시작하고 관리하는 시스템은 이미 해당 **로드 밸런서**(또는 **TLS 종료 프록시**)에서 여러분의 앱이 있는 컨테이너로 **네트워크 통신**(예: HTTP 요청)을 전달하는 내부 도구를 가지고 있습니다.
### 하나의 로드 밸런서 - 여러 워커 컨테이너 { #one-load-balancer-multiple-worker-containers }
**Kubernetes** 같은 분산 컨테이너 관리 시스템에서는 내부 네트워킹 메커니즘을 통해, 메인 **포트**에서 대기하는 단일 **로드 밸런서**가 여러분의 앱을 실행하는 **여러 컨테이너**로 통신(요청)을 전달할 수 있습니다.
앱을 실행하는 각 컨테이너는 보통 **프로세스 하나만** 가집니다(예: FastAPI 애플리케이션을 실행하는 Uvicorn 프로세스). 모두 같은 것을 실행하는 **동일한 컨테이너**이지만, 각자 고유한 프로세스, 메모리 등을 가집니다. 이렇게 하면 CPU의 **서로 다른 코어** 또는 **서로 다른 머신**에서 **병렬화**의 이점을 얻을 수 있습니다.
그리고 **로드 밸런서**가 있는 분산 컨테이너 시스템은 여러분의 앱을 실행하는 각 컨테이너에 **번갈아가며** 요청을 **분산**합니다. 따라서 각 요청은 여러분의 앱을 실행하는 여러 **복제된 컨테이너** 중 하나에서 처리될 수 있습니다.
또한 보통 이 **로드 밸런서**는 클러스터 내 *다른* 앱으로 가는 요청(예: 다른 도메인, 또는 다른 URL 경로 접두사 아래로 가는 요청)도 처리할 수 있으며, 그 통신을 클러스터에서 실행 중인 *그 다른* 애플리케이션의 올바른 컨테이너로 전달할 수 있습니다.
### 컨테이너당 하나의 프로세스 { #one-process-per-container }
이 시나리오에서는 이미 클러스터 레벨에서 복제를 처리하고 있으므로, **컨테이너당 단일 (Uvicorn) 프로세스**를 두는 것이 좋을 가능성이 큽니다.
따라서 이 경우 컨테이너에서 `--workers` 커맨드 라인 옵션 같은 방식으로 여러 워커를 두고 싶지는 **않을** 것입니다. 컨테이너당 **단일 Uvicorn 프로세스**만 두고(하지만 컨테이너는 여러 개일 수 있습니다) 싶을 것입니다.
컨테이너 내부에 (여러 워커를 위한) 또 다른 프로세스 매니저를 두는 것은, 이미 클러스터 시스템에서 처리하고 있는 **불필요한 복잡성**만 추가할 가능성이 큽니다.
### 여러 프로세스를 가진 컨테이너와 특수한 경우 { #containers-with-multiple-processes-and-special-cases }
물론 컨테이너 하나에 여러 **Uvicorn 워커 프로세스**를 두고 싶을 수 있는 **특수한 경우**도 있습니다.
그런 경우에는 `--workers` 커맨드 라인 옵션을 사용해 실행할 워커 수를 설정할 수 있습니다:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. 여기서는 `--workers` 커맨드 라인 옵션으로 워커 수를 4로 설정합니다.
이런 방식이 의미가 있을 수 있는 예시는 다음과 같습니다:
#### 단순한 앱 { #a-simple-app }
애플리케이션이 **충분히 단순**해서 클러스터가 아닌 **단일 서버**에서 실행할 수 있다면, 컨테이너에 프로세스 매니저를 두고 싶을 수 있습니다.
#### Docker Compose { #docker-compose }
**Docker Compose**로 클러스터가 아닌 **단일 서버**에 배포하는 경우, 공유 네트워크와 **로드 밸런싱**을 유지하면서(Docker Compose로) 컨테이너 복제를 관리하는 쉬운 방법이 없을 수 있습니다.
그렇다면 **프로세스 매니저**가 컨테이너 내부에서 **여러 워커 프로세스**를 시작하는 **단일 컨테이너**를 원할 수 있습니다.
---
핵심은, 이것들 중 **어느 것도** 무조건 따라야 하는 **절대적인 규칙**은 아니라는 것입니다. 이 아이디어들을 사용해 **여러분의 사용 사례를 평가**하고, 여러분의 시스템에 가장 적합한 접근 방식을 결정하면서 다음 개념을 어떻게 관리할지 확인할 수 있습니다:
* 보안 - HTTPS
* 시작 시 자동 실행
* 재시작
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작 전 사전 단계
## 메모리 { #memory }
**컨테이너당 단일 프로세스**를 실행하면, 각 컨테이너(복제된 경우 여러 개)마다 소비하는 메모리 양이 대체로 잘 정의되고 안정적이며 제한된 값이 됩니다.
그런 다음 컨테이너 관리 시스템(예: **Kubernetes**) 설정에서 동일하게 메모리 제한과 요구사항을 설정할 수 있습니다. 그러면 클러스터에서 사용 가능한 머신에 있는 메모리와 컨테이너가 필요로 하는 메모리 양을 고려해 **컨테이너를 복제**할 수 있습니다.
애플리케이션이 **단순**하다면 이는 아마도 **문제가 되지 않을** 것이고, 엄격한 메모리 제한을 지정할 필요가 없을 수도 있습니다. 하지만 **많은 메모리를 사용한다면**(예: **머신 러닝** 모델), 얼마나 많은 메모리를 소비하는지 확인하고, **각 머신**에서 실행되는 **컨테이너 수**를 조정해야 합니다(필요하다면 클러스터에 머신을 더 추가할 수도 있습니다).
**컨테이너당 여러 프로세스**를 실행한다면, 시작되는 프로세스 수가 사용 가능한 것보다 **더 많은 메모리를 소비하지** 않는지 확인해야 합니다.
## 시작 전 단계와 컨테이너 { #previous-steps-before-starting-and-containers }
컨테이너(예: Docker, Kubernetes)를 사용한다면, 사용할 수 있는 주요 접근 방식은 두 가지입니다.
### 여러 컨테이너 { #multiple-containers }
**여러 컨테이너**가 있고 각 컨테이너가 보통 **단일 프로세스**를 실행한다면(예: **Kubernetes** 클러스터), 복제된 워커 컨테이너를 실행하기 **전에**, 단일 컨테이너에서 단일 프로세스로 **시작 전 사전 단계**를 수행하는 **별도의 컨테이너**를 두고 싶을 가능성이 큽니다.
/// info | 정보
Kubernetes를 사용한다면, 이는 아마도 [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)일 것입니다.
///
사용 사례에서 시작 전 사전 단계를 **여러 번 병렬로 실행**해도 문제가 없다면(예: 데이터베이스 마이그레이션을 실행하는 것이 아니라, 데이터베이스가 준비되었는지 확인만 하는 경우), 메인 프로세스를 시작하기 직전에 각 컨테이너에 그 단계를 넣을 수도 있습니다.
### 단일 컨테이너 { #single-container }
**단일 컨테이너**에서 여러 **워커 프로세스**(또는 단일 프로세스)를 시작하는 단순한 셋업이라면, 앱이 있는 프로세스를 시작하기 직전에 같은 컨테이너에서 시작 전 사전 단계를 실행할 수 있습니다.
### 베이스 도커 이미지 { #base-docker-image }
과거에는 공식 FastAPI Docker 이미지가 있었습니다: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). 하지만 이제는 deprecated되었습니다. ⛔️
아마도 이 베이스 도커 이미지(또는 유사한 다른 이미지)는 **사용하지 않는** 것이 좋습니다.
**Kubernetes**(또는 다른 도구)를 사용하고, 클러스터 레벨에서 여러 **컨테이너**로 **복제**를 이미 설정해 둔 경우라면, 위에서 설명한 대로 **처음부터 이미지를 빌드하는 것**이 더 낫습니다: [FastAPI를 위한 도커 이미지 빌드하기](#build-a-docker-image-for-fastapi).
그리고 여러 워커가 필요하다면, `--workers` 커맨드 라인 옵션을 간단히 사용하면 됩니다.
/// note | 기술 세부사항
이 Docker 이미지는 Uvicorn이 죽은 워커를 관리하고 재시작하는 기능을 지원하지 않던 시기에 만들어졌습니다. 그래서 Gunicorn과 Uvicorn을 함께 사용해야 했고, Gunicorn이 Uvicorn 워커 프로세스를 관리하고 재시작하도록 하기 위해 상당한 복잡성이 추가되었습니다.
하지만 이제 Uvicorn(그리고 `fastapi` 명령)은 `--workers`를 지원하므로, 베이스 도커 이미지를 사용하는 대신 직접 이미지를 빌드하지 않을 이유가 없습니다(코드 양도 사실상 거의 같습니다 😅).
///
## 컨테이너 이미지 배포하기 { #deploy-the-container-image }
컨테이너(Docker) 이미지를 만든 후에는 이를 배포하는 여러 방법이 있습니다.
예를 들어:
* 단일 서버에서 **Docker Compose**로
* **Kubernetes** 클러스터로
* Docker Swarm Mode 클러스터로
* Nomad 같은 다른 도구로
* 컨테이너 이미지를 받아 배포해주는 클라우드 서비스로
## `uv`를 사용하는 도커 이미지 { #docker-image-with-uv }
프로젝트를 설치하고 관리하기 위해 [uv](https://github.com/astral-sh/uv)를 사용한다면, [uv Docker guide](https://docs.astral.sh/uv/guides/integration/docker/)를 따를 수 있습니다.
## 요약 { #recap }
컨테이너 시스템(예: **Docker**, **Kubernetes**)을 사용하면 모든 **배포 개념**을 다루는 것이 상당히 단순해집니다:
* HTTPS
* 시작 시 자동 실행
* 재시작
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작 전 사전 단계
대부분의 경우 베이스 이미지는 사용하지 않고, 공식 Python Docker 이미지에 기반해 **처음부터 컨테이너 이미지를 빌드**하는 것이 좋습니다.
`Dockerfile`에서 지시어의 **순서**와 **Docker 캐시**를 신경 쓰면 **빌드 시간을 최소화**해 생산성을 최대화할 수 있습니다(그리고 지루함도 피할 수 있습니다). 😎
================================================
FILE: docs/ko/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
**한 번의 명령**으로 FastAPI 앱을 [FastAPI Cloud](https://fastapicloud.com)에 배포할 수 있습니다. 아직이라면 대기자 명단에 등록해 보세요. 🚀
## 로그인하기 { #login }
먼저 **FastAPI Cloud** 계정이 이미 있는지 확인하세요(대기자 명단에서 초대해 드렸을 거예요 😉).
그다음 로그인합니다:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## 배포하기 { #deploy }
이제 **한 번의 명령**으로 앱을 배포합니다:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
## FastAPI Cloud 소개 { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**는 **FastAPI**를 만든 동일한 저자와 팀이 구축했습니다.
최소한의 노력으로 API를 **구축**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 만들 때의 동일한 **개발자 경험**을, 클라우드에 **배포**할 때도 제공합니다. 🎉
또한 앱을 배포할 때 보통 필요한 대부분의 것들도 처리해 줍니다. 예를 들면:
* HTTPS
* 요청을 기반으로 자동 스케일링하는 복제(Replication)
* 등
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 스폰서이자 자금 지원 제공자입니다. ✨
## 다른 클라우드 제공업체에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 원하는 어떤 클라우드 제공업체에도 FastAPI 앱을 배포할 수 있습니다.
해당 클라우드 제공업체의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 자체 서버에 배포하기 { #deploy-your-own-server }
또한 이 **Deployment** 가이드에서 이후에 모든 세부사항을 알려드릴 거예요. 그래서 무슨 일이 일어나고 있는지, 무엇이 필요하며, 본인의 서버를 포함해 직접 FastAPI 앱을 어떻게 배포하는지까지 이해할 수 있게 될 것입니다. 🤓
================================================
FILE: docs/ko/docs/deployment/https.md
================================================
# HTTPS 알아보기 { #about-https }
HTTPS는 그냥 “켜져 있거나” 아니면 “꺼져 있는” 것이라고 생각하기 쉽습니다.
하지만 실제로는 훨씬 더 복잡합니다.
/// tip | 팁
바쁘거나 별로 신경 쓰고 싶지 않다면, 다음 섹션에서 다양한 기법으로 모든 것을 설정하는 단계별 안내를 계속 보세요.
///
소비자 관점에서 **HTTPS의 기본을 배우려면** [https://howhttps.works/](https://howhttps.works/)를 확인하세요.
이제 **개발자 관점**에서 HTTPS를 생각할 때 염두에 두어야 할 여러 가지가 있습니다:
* HTTPS를 사용하려면, **서버**가 **제3자**가 발급한 **"인증서(certificates)"**를 **보유**해야 합니다.
* 이 인증서는 실제로 '생성'되는 것이 아니라 제3자로부터 **발급/획득**하는 것입니다.
* 인증서에는 **유효 기간**이 있습니다.
* 즉, **만료**됩니다.
* 그리고 나면 제3자로부터 다시 **갱신**해서 **재발급/재획득**해야 합니다.
* 연결의 암호화는 **TCP 레벨**에서 일어납니다.
* 이는 **HTTP보다 한 계층 아래**입니다.
* 따라서 **인증서와 암호화** 처리는 **HTTP 이전**에 수행됩니다.
* **TCP는 "도메인"을 모릅니다.** IP 주소만 압니다.
* 어떤 **특정 도메인**을 요청했는지에 대한 정보는 **HTTP 데이터**에 들어 있습니다.
* **HTTPS 인증서**는 특정 **도메인**을 “인증”하지만, 프로토콜과 암호화는 TCP 레벨에서 일어나며, 어떤 도메인을 다루는지 **알기 전에** 처리됩니다.
* **기본적으로** 이는 IP 주소 하나당 **HTTPS 인증서 하나만** 둘 수 있다는 뜻입니다.
* 서버가 아무리 크든, 그 위에 올린 각 애플리케이션이 아무리 작든 상관없습니다.
* 하지만 이에 대한 **해결책**이 있습니다.
* **TLS** 프로토콜(HTTP 이전, TCP 레벨에서 암호화를 처리하는 것)에 대한 **확장** 중에 **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**라는 것이 있습니다.
* 이 SNI 확장을 사용하면, 단일 서버(**단일 IP 주소**)에서 **여러 HTTPS 인증서**를 사용하고 **여러 HTTPS 도메인/애플리케이션**을 제공할 수 있습니다.
* 이를 위해서는 서버에서 **공개 IP 주소**로 리스닝하는 **하나의** 컴포넌트(프로그램)가 서버에 있는 **모든 HTTPS 인증서**에 접근할 수 있어야 합니다.
* 보안 연결을 얻은 **이후에도**, 통신 프로토콜 자체는 **여전히 HTTP**입니다.
* **HTTP 프로토콜**로 전송되더라도, 내용은 **암호화**되어 있습니다.
일반적으로 서버(머신, 호스트 등)에는 **프로그램/HTTP 서버 하나**를 실행해 **HTTPS 관련 부분 전체**를 관리하게 합니다: **암호화된 HTTPS 요청**을 받고, 복호화된 **HTTP 요청**을 같은 서버에서 실행 중인 실제 HTTP 애플리케이션(이 경우 **FastAPI** 애플리케이션)으로 전달하고, 애플리케이션의 **HTTP 응답**을 받아 적절한 **HTTPS 인증서**로 **암호화**한 뒤 **HTTPS**로 클라이언트에 다시 보내는 역할입니다. 이런 서버를 흔히 **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)**라고 부릅니다.
TLS Termination Proxy로 사용할 수 있는 옵션은 다음과 같습니다:
* Traefik (인증서 갱신도 처리 가능)
* Caddy (인증서 갱신도 처리 가능)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Let's Encrypt 이전에는 이러한 **HTTPS 인증서**가 신뢰할 수 있는 제3자에 의해 판매되었습니다.
인증서를 획득하는 과정은 번거롭고, 꽤 많은 서류 작업이 필요했으며, 인증서도 상당히 비쌌습니다.
하지만 그 후 **[Let's Encrypt](https://letsencrypt.org/)**가 만들어졌습니다.
이는 Linux Foundation의 프로젝트입니다. 표준 암호학적 보안을 모두 사용하는 **HTTPS 인증서**를 **무료로**, 자동화된 방식으로 제공합니다. 이 인증서들은 수명이 짧고(약 3개월) 그래서 유효 기간이 짧은 만큼 **실제로 보안이 더 좋아지기도** 합니다.
도메인은 안전하게 검증되며 인증서는 자동으로 생성됩니다. 또한 이로 인해 인증서 갱신도 자동화할 수 있습니다.
목표는 인증서의 발급과 갱신을 자동화하여 **무료로, 영구히, 안전한 HTTPS**를 사용할 수 있게 하는 것입니다.
## 개발자를 위한 HTTPS { #https-for-developers }
개발자에게 중요한 개념들을 중심으로, HTTPS API가 단계별로 어떻게 보일 수 있는지 예시를 들어 보겠습니다.
### 도메인 이름 { #domain-name }
아마도 시작은 **도메인 이름**을 **획득**하는 것일 겁니다. 그 다음 DNS 서버(아마 같은 클라우드 제공업체)에서 이를 설정합니다.
대개 클라우드 서버(가상 머신) 같은 것을 사용하게 되고, 거기에는 고정 **공개 IP 주소**가 있습니다.
DNS 서버(들)에서 **도메인**이 서버의 **공개 IP 주소**를 가리키도록 레코드(“`A record`”)를 설정합니다.
보통은 처음 한 번, 모든 것을 설정할 때만 이 작업을 합니다.
/// tip | 팁
도메인 이름 부분은 HTTPS보다 훨씬 이전 단계지만, 모든 것이 도메인과 IP 주소에 의존하므로 여기서 언급할 가치가 있습니다.
///
### DNS { #dns }
이제 실제 HTTPS 부분에 집중해 보겠습니다.
먼저 브라우저는 **DNS 서버**에 질의하여, 여기서는 `someapp.example.com`이라는 **도메인에 대한 IP**가 무엇인지 확인합니다.
DNS 서버는 브라우저에게 특정 **IP 주소**를 사용하라고 알려줍니다. 이는 DNS 서버에 설정해 둔, 서버가 사용하는 공개 IP 주소입니다.
### TLS 핸드셰이크 시작 { #tls-handshake-start }
그 다음 브라우저는 **포트 443**(HTTPS 포트)에서 해당 IP 주소와 통신합니다.
통신의 첫 부분은 클라이언트와 서버 사이의 연결을 설정하고, 사용할 암호화 키 등을 결정하는 과정입니다.
클라이언트와 서버가 TLS 연결을 설정하기 위해 상호작용하는 이 과정을 **TLS 핸드셰이크**라고 합니다.
### SNI 확장을 사용하는 TLS { #tls-with-sni-extension }
서버에서는 특정 **IP 주소**의 특정 **포트**에서 **하나의 프로세스만** 리스닝할 수 있습니다. 같은 IP 주소에서 다른 포트로 리스닝하는 프로세스는 있을 수 있지만, IP 주소와 포트 조합마다 하나만 가능합니다.
TLS(HTTPS)는 기본적으로 특정 포트 `443`을 사용합니다. 따라서 우리가 필요한 포트는 이것입니다.
이 포트에서 하나의 프로세스만 리스닝할 수 있으므로, 그 역할을 하는 프로세스는 **TLS Termination Proxy**가 됩니다.
TLS Termination Proxy는 하나 이상의 **TLS 인증서**(HTTPS 인증서)에 접근할 수 있습니다.
앞에서 설명한 **SNI 확장**을 사용해, TLS Termination Proxy는 이 연결에 사용할 수 있는 TLS(HTTPS) 인증서들 중에서 클라이언트가 기대하는 도메인과 일치하는 것을 확인해 선택합니다.
이 경우에는 `someapp.example.com`에 대한 인증서를 사용합니다.
클라이언트는 이미 해당 TLS 인증서를 생성한 주체(여기서는 Let's Encrypt이지만, 이는 뒤에서 다시 보겠습니다)를 **신뢰**하므로, 인증서가 유효한지 **검증**할 수 있습니다.
그 다음 인증서를 사용해 클라이언트와 TLS Termination Proxy는 나머지 **TCP 통신**을 어떻게 **암호화할지 결정**합니다. 이로써 **TLS 핸드셰이크** 단계가 완료됩니다.
이후 클라이언트와 서버는 TLS가 제공하는 **암호화된 TCP 연결**을 갖게 됩니다. 그리고 그 연결을 사용해 실제 **HTTP 통신**을 시작할 수 있습니다.
이것이 바로 **HTTPS**입니다. 순수(암호화되지 않은) TCP 연결 대신 **안전한 TLS 연결** 안에서 **HTTP**를 그대로 사용하는 것입니다.
/// tip | 팁
통신의 암호화는 HTTP 레벨이 아니라 **TCP 레벨**에서 일어난다는 점에 주의하세요.
///
### HTTPS 요청 { #https-request }
이제 클라이언트와 서버(구체적으로는 브라우저와 TLS Termination Proxy)가 **암호화된 TCP 연결**을 갖게 되었으니 **HTTP 통신**을 시작할 수 있습니다.
따라서 클라이언트는 **HTTPS 요청**을 보냅니다. 이는 암호화된 TLS 연결을 통해 전달되는 HTTP 요청일 뿐입니다.
### 요청 복호화 { #decrypt-the-request }
TLS Termination Proxy는 합의된 암호화를 사용해 **요청을 복호화**하고, 애플리케이션을 실행 중인 프로세스(예: FastAPI 애플리케이션을 실행하는 Uvicorn 프로세스)에 **일반(복호화된) HTTP 요청**을 전달합니다.
### HTTP 응답 { #http-response }
애플리케이션은 요청을 처리하고 **일반(암호화되지 않은) HTTP 응답**을 TLS Termination Proxy로 보냅니다.
### HTTPS 응답 { #https-response }
그 다음 TLS Termination Proxy는 이전에 합의한 암호화( `someapp.example.com` 인증서로 시작된 것)를 사용해 **응답을 암호화**하고, 브라우저로 다시 보냅니다.
이후 브라우저는 응답이 유효한지, 올바른 암호화 키로 암호화되었는지 등을 확인합니다. 그런 다음 **응답을 복호화**하고 처리합니다.
클라이언트(브라우저)는 앞서 **HTTPS 인증서**로 합의한 암호화를 사용하고 있으므로, 해당 응답이 올바른 서버에서 왔다는 것을 알 수 있습니다.
### 여러 애플리케이션 { #multiple-applications }
같은 서버(또는 여러 서버)에는 예를 들어 다른 API 프로그램이나 데이터베이스처럼 **여러 애플리케이션**이 있을 수 있습니다.
특정 IP와 포트 조합은 하나의 프로세스만 처리할 수 있지만(예시에서는 TLS Termination Proxy), 다른 애플리케이션/프로세스도 **공개 IP와 포트 조합**을 동일하게 쓰려고만 하지 않는다면 서버에서 함께 실행될 수 있습니다.
이렇게 하면 TLS Termination Proxy가 **여러 도메인**에 대한 HTTPS와 인증서를 **여러 애플리케이션**에 대해 처리하고, 각 경우에 맞는 애플리케이션으로 요청을 전달할 수 있습니다.
### 인증서 갱신 { #certificate-renewal }
미래의 어느 시점에는 각 인증서가 **만료**됩니다(획득 후 약 3개월).
그 다음에는 또 다른 프로그램(경우에 따라 별도 프로그램일 수도 있고, 경우에 따라 같은 TLS Termination Proxy일 수도 있습니다)이 Let's Encrypt와 통신하여 인증서를 갱신합니다.
**TLS 인증서**는 IP 주소가 아니라 **도메인 이름**과 **연결**되어 있습니다.
따라서 인증서를 갱신하려면, 갱신 프로그램이 권한 기관(Let's Encrypt)에게 해당 도메인을 실제로 **“소유”하고 제어하고 있음**을 **증명**해야 합니다.
이를 위해, 그리고 다양한 애플리케이션 요구를 수용하기 위해 여러 방법이 있습니다. 널리 쓰이는 방법은 다음과 같습니다:
* **일부 DNS 레코드 수정**.
* 이를 위해서는 갱신 프로그램이 DNS 제공업체의 API를 지원해야 하므로, 사용하는 DNS 제공업체에 따라 가능할 수도, 아닐 수도 있습니다.
* 도메인과 연결된 공개 IP 주소에서 **서버로 실행**(적어도 인증서 발급 과정 동안).
* 앞에서 말했듯 특정 IP와 포트에서는 하나의 프로세스만 리스닝할 수 있습니다.
* 이것이 동일한 TLS Termination Proxy가 인증서 갱신 과정까지 처리할 때 매우 유용한 이유 중 하나입니다.
* 그렇지 않으면 TLS Termination Proxy를 잠시 중지하고, 갱신 프로그램을 시작해 인증서를 획득한 다음, TLS Termination Proxy에 인증서를 설정하고, 다시 TLS Termination Proxy를 재시작해야 할 수도 있습니다. 이는 TLS Termination Proxy가 꺼져 있는 동안 앱(들)을 사용할 수 없으므로 이상적이지 않습니다.
앱을 계속 제공하면서 이 갱신 과정을 처리할 수 있는 것은, 애플리케이션 서버(예: Uvicorn)에서 TLS 인증서를 직접 쓰는 대신 TLS Termination Proxy로 HTTPS를 처리하는 **별도의 시스템**을 두고 싶어지는 주요 이유 중 하나입니다.
## 프록시 전달 헤더 { #proxy-forwarded-headers }
프록시를 사용해 HTTPS를 처리할 때, **애플리케이션 서버**(예: FastAPI CLI를 통한 Uvicorn)는 HTTPS 과정에 대해 아무것도 알지 못하고 **TLS Termination Proxy**와는 일반 HTTP로 통신합니다.
이 **프록시**는 보통 요청을 **애플리케이션 서버**에 전달하기 전에, 요청이 프록시에 의해 **전달(forwarded)**되고 있음을 애플리케이션 서버가 알 수 있도록 일부 HTTP 헤더를 즉석에서 설정합니다.
/// note | 기술 세부사항
프록시 헤더는 다음과 같습니다:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
그럼에도 불구하고 **애플리케이션 서버**는 자신이 신뢰할 수 있는 **프록시** 뒤에 있다는 것을 모르므로, 기본적으로는 그 헤더들을 신뢰하지 않습니다.
하지만 **애플리케이션 서버**가 **프록시**가 보낸 *forwarded* 헤더를 신뢰하도록 설정할 수 있습니다. FastAPI CLI를 사용하고 있다면, *CLI Option* `--forwarded-allow-ips`를 사용해 어떤 IP에서 온 *forwarded* 헤더를 신뢰할지 지정할 수 있습니다.
예를 들어 **애플리케이션 서버**가 신뢰하는 **프록시**로부터만 통신을 받는다면, `--forwarded-allow-ips="*"`로 설정해 들어오는 모든 IP를 신뢰하게 할 수 있습니다. 어차피 **프록시**가 사용하는 IP에서만 요청을 받게 될 것이기 때문입니다.
이렇게 하면 애플리케이션은 자신이 사용하는 공개 URL이 무엇인지, HTTPS를 사용하는지, 도메인이 무엇인지 등을 알 수 있습니다.
예를 들어 리다이렉트를 올바르게 처리하는 데 유용합니다.
/// tip | 팁
이에 대해서는 [프록시 뒤에서 실행하기 - 프록시 전달 헤더 활성화](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers) 문서에서 더 알아볼 수 있습니다.
///
## 요약 { #recap }
**HTTPS**는 매우 중요하며, 대부분의 경우 상당히 **핵심적**입니다. 개발자가 HTTPS와 관련해 해야 하는 노력의 대부분은 결국 **이 개념들을 이해**하고 그것들이 어떻게 동작하는지 파악하는 것입니다.
하지만 **개발자를 위한 HTTPS**의 기본 정보를 알고 나면, 여러 도구를 쉽게 조합하고 설정하여 모든 것을 간단하게 관리할 수 있습니다.
다음 장들에서는 **FastAPI** 애플리케이션을 위한 **HTTPS** 설정 방법을 여러 구체적인 예시로 보여드리겠습니다. 🔒
================================================
FILE: docs/ko/docs/deployment/index.md
================================================
# 배포 { #deployment }
**FastAPI** 애플리케이션을 배포하는 것은 비교적 쉽습니다.
## 배포의 의미 { #what-does-deployment-mean }
애플리케이션을 **배포**한다는 것은 **사용자가 사용**할 수 있도록 하는 데 필요한 단계를 수행하는 것을 의미합니다.
**웹 API**의 경우, 일반적으로 **원격 머신**에 이를 설치하고, 좋은 성능, 안정성 등을 제공하는 **서버 프로그램**과 함께 구성하여 **사용자**가 중단이나 문제 없이 애플리케이션에 효율적으로 **접근**할 수 있게 하는 것을 포함합니다.
이는 지속적으로 코드를 변경하고, 망가뜨리고 고치고, 개발 서버를 중지했다가 다시 시작하는 등의 **개발** 단계와 대조됩니다.
## 배포 전략 { #deployment-strategies }
구체적인 사용 사례와 사용하는 도구에 따라 여러 가지 방법이 있습니다.
여러 도구를 조합해 직접 **서버를 배포**할 수도 있고, 작업의 일부를 대신해 주는 **클라우드 서비스**를 사용할 수도 있으며, 다른 가능한 선택지도 있습니다.
예를 들어, FastAPI 뒤에 있는 저희 팀은 FastAPI로 작업하는 것과 같은 개발자 경험을 유지하면서, FastAPI 앱을 클라우드에 가능한 한 간소화된 방식으로 배포할 수 있도록 [**FastAPI Cloud**](https://fastapicloud.com)를 만들었습니다.
**FastAPI** 애플리케이션을 배포할 때 아마 염두에 두어야 할 몇 가지 주요 개념을 보여드리겠습니다(대부분은 다른 유형의 웹 애플리케이션에도 적용됩니다).
다음 섹션에서 염두에 둘 더 많은 세부사항과 이를 위한 몇 가지 기술을 볼 수 있습니다. ✨
================================================
FILE: docs/ko/docs/deployment/manually.md
================================================
# 서버를 수동으로 실행하기 { #run-a-server-manually }
## `fastapi run` 명령 사용하기 { #use-the-fastapi-run-command }
요약하면, `fastapi run`을 사용해 FastAPI 애플리케이션을 서비스하세요:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
대부분의 경우에는 이것으로 동작합니다. 😎
예를 들어 이 명령은 컨테이너나 서버 등에서 **FastAPI** 앱을 시작할 때 사용할 수 있습니다.
## ASGI 서버 { #asgi-servers }
이제 조금 더 자세히 살펴보겠습니다.
FastAPI는 ASGI라고 불리는, Python 웹 프레임워크와 서버를 만들기 위한 표준을 사용합니다. FastAPI는 ASGI 웹 프레임워크입니다.
원격 서버 머신에서 **FastAPI** 애플리케이션(또는 다른 ASGI 애플리케이션)을 실행하기 위해 필요한 핵심 요소는 **Uvicorn** 같은 ASGI 서버 프로그램입니다. `fastapi` 명령에는 기본으로 이것이 포함되어 있습니다.
다음을 포함해 여러 대안이 있습니다:
* [Uvicorn](https://www.uvicorn.dev/): 고성능 ASGI 서버.
* [Hypercorn](https://hypercorn.readthedocs.io/): HTTP/2 및 Trio 등 여러 기능과 호환되는 ASGI 서버.
* [Daphne](https://github.com/django/daphne): Django Channels를 위해 만들어진 ASGI 서버.
* [Granian](https://github.com/emmett-framework/granian): Python 애플리케이션을 위한 Rust HTTP 서버.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit은 가볍고 다용도로 사용할 수 있는 웹 애플리케이션 런타임입니다.
## 서버 머신과 서버 프로그램 { #server-machine-and-server-program }
이름에 관해 기억해 둘 작은 디테일이 있습니다. 💡
"**server**"라는 단어는 보통 원격/클라우드 컴퓨터(물리 또는 가상 머신)와, 그 머신에서 실행 중인 프로그램(예: Uvicorn) 둘 다를 가리키는 데 사용됩니다.
일반적으로 "server"를 읽을 때, 이 두 가지 중 하나를 의미할 수 있다는 점을 기억하세요.
원격 머신을 가리킬 때는 **server**라고 부르는 것이 일반적이지만, **machine**, **VM**(virtual machine), **node**라고 부르기도 합니다. 이것들은 보통 Linux를 실행하는 원격 머신의 한 형태를 뜻하며, 그곳에서 프로그램을 실행합니다.
## 서버 프로그램 설치하기 { #install-the-server-program }
FastAPI를 설치하면 프로덕션 서버인 Uvicorn이 함께 설치되며, `fastapi run` 명령으로 시작할 수 있습니다.
하지만 ASGI 서버를 수동으로 설치할 수도 있습니다.
[가상 환경](../virtual-environments.md)을 만들고 활성화한 다음, 서버 애플리케이션을 설치하세요.
예를 들어 Uvicorn을 설치하려면:
다른 어떤 ASGI 서버 프로그램도 비슷한 과정이 적용됩니다.
/// tip | 팁
`standard`를 추가하면 Uvicorn이 권장되는 추가 의존성 몇 가지를 설치하고 사용합니다.
여기에는 `uvloop`가 포함되며, 이는 `asyncio`를 고성능으로 대체할 수 있는 드롭인 대체재로, 큰 동시성 성능 향상을 제공합니다.
`pip install "fastapi[standard]"` 같은 방식으로 FastAPI를 설치하면 `uvicorn[standard]`도 함께 설치됩니다.
///
## 서버 프로그램 실행하기 { #run-the-server-program }
ASGI 서버를 수동으로 설치했다면, 보통 FastAPI 애플리케이션을 임포트하기 위해 특별한 형식의 import string을 전달해야 합니다:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | 참고
`uvicorn main:app` 명령은 다음을 가리킵니다:
* `main`: 파일 `main.py`(Python "module").
* `app`: `main.py` 안에서 `app = FastAPI()` 라인으로 생성된 객체.
이는 다음과 동일합니다:
```Python
from main import app
```
///
각 ASGI 서버 프로그램의 대안도 비슷한 명령을 갖고 있으며, 자세한 내용은 각자의 문서를 참고하세요.
/// warning | 경고
Uvicorn과 다른 서버는 개발 중에 유용한 `--reload` 옵션을 지원합니다.
`--reload` 옵션은 훨씬 더 많은 리소스를 소비하고, 더 불안정합니다.
**개발** 중에는 큰 도움이 되지만, **프로덕션**에서는 사용하지 **말아야** 합니다.
///
## 배포 개념 { #deployment-concepts }
이 예제들은 서버 프로그램(예: Uvicorn)을 실행하여 **단일 프로세스**를 시작하고, 사전에 정한 포트(예: `80`)에서 모든 IP(`0.0.0.0`)로 들어오는 요청을 받도록 합니다.
이것이 기본 아이디어입니다. 하지만 보통은 다음과 같은 추가 사항들도 처리해야 합니다:
* 보안 - HTTPS
* 시작 시 자동 실행
* 재시작
* 복제(실행 중인 프로세스 수)
* 메모리
* 시작 전 선행 단계
다음 장들에서 이 각각의 개념을 어떻게 생각해야 하는지, 그리고 이를 다루기 위한 전략의 구체적인 예시를 더 알려드리겠습니다. 🚀
================================================
FILE: docs/ko/docs/deployment/server-workers.md
================================================
# 서버 워커 - 워커와 함께 사용하는 Uvicorn { #server-workers-uvicorn-with-workers }
이전의 배포 개념들을 다시 확인해보겠습니다:
* 보안 - HTTPS
* 서버 시작 시 실행
* 재시작
* **복제(실행 중인 프로세스 수)**
* 메모리
* 시작하기 전의 이전 단계
지금까지 문서의 모든 튜토리얼을 참고하면서, `fastapi` 명령어처럼 Uvicorn을 실행하는 **서버 프로그램**을 사용해 **단일 프로세스**로 실행해 왔을 가능성이 큽니다.
애플리케이션을 배포할 때는 **다중 코어**를 활용하고 더 많은 요청을 처리할 수 있도록 **프로세스 복제**를 하고 싶을 가능성이 큽니다.
이전 장의 [배포 개념들](concepts.md)에서 본 것처럼, 사용할 수 있는 전략이 여러 가지 있습니다.
여기서는 `fastapi` 명령어를 사용하거나 `uvicorn` 명령어를 직접 사용해서, **워커 프로세스**와 함께 **Uvicorn**을 사용하는 방법을 보여드리겠습니다.
/// info | 정보
Docker나 Kubernetes 같은 컨테이너를 사용하고 있다면, 다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md)에서 더 자세히 설명하겠습니다.
특히 **Kubernetes**에서 실행할 때는 워커를 사용하기보다는, 대신 **컨테이너당 단일 Uvicorn 프로세스 하나**를 실행하고 싶을 가능성이 크지만, 해당 내용은 그 장의 뒤에서 설명하겠습니다.
///
## 여러 워커 { #multiple-workers }
`--workers` 명령어 옵션으로 여러 워커를 시작할 수 있습니다:
//// tab | `fastapi`
`fastapi` 명령어를 사용한다면:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
`uvicorn` 명령어를 직접 사용하는 편이 좋다면:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
여기서 새로운 옵션은 `--workers`뿐이며, Uvicorn에게 워커 프로세스 4개를 시작하라고 알려줍니다.
또한 각 프로세스의 **PID**도 확인할 수 있는데, 상위 프로세스(이것이 **프로세스 관리자**)의 PID는 `27365`이고, 각 워커 프로세스의 PID는 `27368`, `27369`, `27370`, `27367`입니다.
## 배포 개념들 { #deployment-concepts }
여기서는 여러 **워커**를 사용해 애플리케이션 실행을 **병렬화**하고, CPU의 **다중 코어**를 활용하며, **더 많은 요청**을 제공할 수 있는 방법을 살펴봤습니다.
위의 배포 개념 목록에서 워커를 사용하는 것은 주로 **복제** 부분에 도움이 되고, **재시작**에도 약간 도움이 되지만, 나머지 항목들도 여전히 신경 써야 합니다:
* **보안 - HTTPS**
* **서버 시작 시 실행**
* ***재시작***
* 복제(실행 중인 프로세스 수)
* **메모리**
* **시작하기 전의 이전 단계**
## 컨테이너와 도커 { #containers-and-docker }
다음 장인 [컨테이너에서의 FastAPI - 도커](docker.md)에서는 다른 **배포 개념들**을 처리하기 위해 사용할 수 있는 몇 가지 전략을 설명하겠습니다.
단일 Uvicorn 프로세스를 실행하기 위해, **처음부터 여러분만의 이미지를 직접 빌드**하는 방법을 보여드리겠습니다. 이는 간단한 과정이며, **Kubernetes** 같은 분산 컨테이너 관리 시스템을 사용할 때 아마도 이렇게 하고 싶을 것입니다.
## 요약 { #recap }
`fastapi` 또는 `uvicorn` 명령어에서 `--workers` CLI 옵션을 사용해 여러 워커 프로세스를 실행하면, **멀티 코어 CPU**를 활용해 **여러 프로세스를 병렬로 실행**할 수 있습니다.
다른 배포 개념들을 직접 처리하면서 **자체 배포 시스템**을 구축하는 경우, 이러한 도구와 아이디어를 활용할 수 있습니다.
다음 장에서 컨테이너(예: Docker 및 Kubernetes)와 함께 사용하는 **FastAPI**에 대해 알아보세요. 해당 도구들이 다른 **배포 개념들**도 간단히 해결하는 방법이 있다는 것을 확인할 수 있습니다. ✨
================================================
FILE: docs/ko/docs/deployment/versions.md
================================================
# FastAPI 버전들에 대하여 { #about-fastapi-versions }
**FastAPI**는 이미 많은 애플리케이션과 시스템에서 프로덕션으로 사용되고 있습니다. 그리고 테스트 커버리지는 100%로 유지됩니다. 하지만 개발은 여전히 빠르게 진행되고 있습니다.
새로운 기능이 자주 추가되고, 버그가 규칙적으로 수정되며, 코드는 계속해서 지속적으로 개선되고 있습니다.
그래서 현재 버전이 아직 `0.x.x`인 것입니다. 이는 각 버전이 잠재적으로 하위 호환성이 깨지는 변경을 포함할 수 있음을 반영합니다. 이는 [Semantic Versioning](https://semver.org/) 관례를 따릅니다.
지금 바로 **FastAPI**로 프로덕션 애플리케이션을 만들 수 있습니다(그리고 아마도 한동안 그렇게 해오셨을 것입니다). 다만 나머지 코드와 함께 올바르게 동작하는 버전을 사용하고 있는지 확인하기만 하면 됩니다.
## `fastapi` 버전을 고정하기 { #pin-your-fastapi-version }
가장 먼저 해야 할 일은 여러분의 애플리케이션에서 올바르게 동작하는 것으로 알고 있는 **FastAPI**의 최신 구체 버전에 맞춰 사용 중인 버전을 "고정(pin)"하는 것입니다.
예를 들어, 앱에서 `0.112.0` 버전을 사용하고 있다고 가정해 보겠습니다.
`requirements.txt` 파일을 사용한다면 다음과 같이 버전을 지정할 수 있습니다:
```txt
fastapi[standard]==0.112.0
```
이는 정확히 `0.112.0` 버전을 사용한다는 의미입니다.
또는 다음과 같이 고정할 수도 있습니다:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
이는 `0.112.0` 이상이면서 `0.113.0` 미만의 버전을 사용한다는 의미입니다. 예를 들어 `0.112.2` 버전도 허용됩니다.
`uv`, Poetry, Pipenv 등 다른 도구로 설치를 관리한다면, 모두 패키지의 특정 버전을 정의할 수 있는 방법을 제공합니다.
## 이용 가능한 버전들 { #available-versions }
사용 가능한 버전(예: 현재 최신 버전이 무엇인지 확인하기 위해)은 [릴리스 노트](../release-notes.md)에서 확인할 수 있습니다.
## 버전들에 대해 { #about-versions }
Semantic Versioning 관례에 따르면, `1.0.0` 미만의 어떤 버전이든 잠재적으로 하위 호환성이 깨지는 변경을 추가할 수 있습니다.
FastAPI는 또한 "PATCH" 버전 변경은 버그 수정과 하위 호환성이 깨지지 않는 변경을 위한 것이라는 관례를 따릅니다.
/// tip | 팁
"PATCH"는 마지막 숫자입니다. 예를 들어 `0.2.3`에서 PATCH 버전은 `3`입니다.
///
따라서 다음과 같이 버전을 고정할 수 있어야 합니다:
```txt
fastapi>=0.45.0,<0.46.0
```
하위 호환성이 깨지는 변경과 새로운 기능은 "MINOR" 버전에 추가됩니다.
/// tip | 팁
"MINOR"는 가운데 숫자입니다. 예를 들어 `0.2.3`에서 MINOR 버전은 `2`입니다.
///
## FastAPI 버전 업그레이드하기 { #upgrading-the-fastapi-versions }
앱에 테스트를 추가해야 합니다.
**FastAPI**에서는 매우 쉽습니다(Starlette 덕분에). 문서를 확인해 보세요: [테스트](../tutorial/testing.md)
테스트를 갖춘 뒤에는 **FastAPI** 버전을 더 최신 버전으로 업그레이드하고, 테스트를 실행하여 모든 코드가 올바르게 동작하는지 확인하세요.
모든 것이 동작하거나 필요한 변경을 한 뒤 모든 테스트가 통과한다면, `fastapi`를 그 새로운 최신 버전으로 고정할 수 있습니다.
## Starlette에 대해 { #about-starlette }
`starlette`의 버전은 고정하지 않는 것이 좋습니다.
서로 다른 **FastAPI** 버전은 Starlette의 특정한 더 새로운 버전을 사용하게 됩니다.
따라서 **FastAPI**가 올바른 Starlette 버전을 사용하도록 그냥 두면 됩니다.
## Pydantic에 대해 { #about-pydantic }
Pydantic은 자체 테스트에 **FastAPI**에 대한 테스트도 포함하고 있으므로, Pydantic의 새 버전(`1.0.0` 초과)은 항상 FastAPI와 호환됩니다.
여러분에게 맞는 `1.0.0` 초과의 어떤 Pydantic 버전으로든 고정할 수 있습니다.
예를 들어:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/ko/docs/editor-support.md
================================================
# 에디터 지원 { #editor-support }
공식 [FastAPI 확장](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode)은 FastAPI 개발 워크플로우를 강화해 줍니다. *경로 처리* 탐색 및 이동, FastAPI Cloud 배포, 실시간 로그 스트리밍을 제공합니다.
확장에 대한 자세한 내용은 [GitHub 저장소](https://github.com/fastapi/fastapi-vscode)의 README를 참고하세요.
## 설치 및 설정 { #setup-and-installation }
**FastAPI 확장**은 [VS Code](https://code.visualstudio.com/)와 [Cursor](https://www.cursor.com/)에서 사용할 수 있습니다. 각 에디터의 확장(Extensions) 패널에서 "FastAPI"로 검색한 뒤 **FastAPI Labs**가 배포한 확장을 선택해 바로 설치할 수 있습니다. 또한 [vscode.dev](https://vscode.dev), [github.dev](https://github.dev) 같은 브라우저 기반 에디터에서도 동작합니다.
### 애플리케이션 자동 감지 { #application-discovery }
기본적으로 이 확장은 작업 공간에서 `FastAPI()`를 생성하는 파일을 스캔하여 FastAPI 애플리케이션을 자동으로 감지합니다. 프로젝트 구조상 자동 감지가 어려운 경우, `pyproject.toml`의 `[tool.fastapi]` 항목이나 VS Code 설정 `fastapi.entryPoint`에 모듈 표기(예: `myapp.main:app`)로 엔트리포인트를 지정할 수 있습니다.
## 기능 { #features }
- **경로 처리 탐색기** - 애플리케이션의 모든 *경로 처리*를 사이드바 트리 뷰로 확인합니다. 클릭하면 해당 경로 또는 라우터 정의로 바로 이동합니다.
- **경로 검색** - Ctrl + Shift + E (macOS: Cmd + Shift + E)로 경로, 메서드, 이름으로 검색합니다.
- **CodeLens 탐색** - 테스트 클라이언트 호출(예: `client.get('/items')`) 위의 클릭 가능한 링크를 통해 해당 *경로 처리*로 즉시 이동하여 테스트와 구현 간을 빠르게 오갈 수 있습니다.
- **FastAPI Cloud에 배포** - [FastAPI Cloud](https://fastapicloud.com/)로 원클릭 배포를 지원합니다.
- **애플리케이션 로그 스트리밍** - FastAPI Cloud에 배포된 애플리케이션의 로그를 실시간으로 스트리밍하며, 레벨 필터링과 텍스트 검색을 제공합니다.
확장의 기능을 먼저 익혀보고 싶다면, 명령 팔레트(Ctrl + Shift + P, macOS: Cmd + Shift + P)를 열고 "Welcome: Open walkthrough..."를 선택한 뒤 "Get started with FastAPI" walkthrough를 선택해 보세요.
================================================
FILE: docs/ko/docs/environment-variables.md
================================================
# 환경 변수 { #environment-variables }
/// tip | 팁
만약 "환경 변수"가 무엇이고, 어떻게 사용하는지 알고 계시다면, 이 챕터를 스킵하셔도 좋습니다.
///
환경 변수(또는 "**env var**"라고도 합니다)는 파이썬 코드의 **바깥**인, **운영 체제**에 존재하는 변수이며, 파이썬 코드(또는 다른 프로그램에서도)에서 읽을 수 있습니다.
환경 변수는 애플리케이션 **설정**을 처리하거나, 파이썬의 **설치** 과정의 일부로 유용할 수 있습니다.
## 환경 변수를 만들고 사용하기 { #create-and-use-env-vars }
파이썬 없이도, **셸 (터미널)** 에서 환경 변수를 **생성** 하고 사용할 수 있습니다.
//// tab | Linux, macOS, Windows Bash
```console
// 환경 변수 MY_NAME을 다음과 같이 생성할 수 있습니다
$ export MY_NAME="Wade Wilson"
// 그런 다음 다른 프로그램과 함께 사용할 수 있습니다. 예:
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// 환경 변수 MY_NAME 생성
$ $Env:MY_NAME = "Wade Wilson"
// 다른 프로그램과 함께 사용하기. 예:
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## 파이썬에서 env var 읽기 { #read-env-vars-in-python }
파이썬 **바깥**인 터미널에서(또는 다른 어떤 방법으로든) 환경 변수를 만들고, 그런 다음 **파이썬에서 읽을 수 있습니다**.
예를 들어 다음과 같은 `main.py` 파일이 있다고 합시다:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | 팁
[`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) 의 두 번째 인자는 반환할 기본값입니다.
제공하지 않으면 기본값은 `None`이며, 여기서는 사용할 기본값으로 `"World"`를 제공합니다.
///
그러면 해당 파이썬 프로그램을 다음과 같이 호출할 수 있습니다:
//// tab | Linux, macOS, Windows Bash
```console
// 여기서는 아직 환경 변수를 설정하지 않습니다
$ python main.py
// 환경 변수를 설정하지 않았으므로 기본값이 사용됩니다
Hello World from Python
// 하지만 먼저 환경 변수를 생성하면
$ export MY_NAME="Wade Wilson"
// 그리고 프로그램을 다시 실행하면
$ python main.py
// 이제 환경 변수를 읽을 수 있습니다
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// 여기서는 아직 환경 변수를 설정하지 않습니다
$ python main.py
// 환경 변수를 설정하지 않았으므로 기본값이 사용됩니다
Hello World from Python
// 하지만 먼저 환경 변수를 생성하면
$ $Env:MY_NAME = "Wade Wilson"
// 그리고 프로그램을 다시 실행하면
$ python main.py
// 이제 환경 변수를 읽을 수 있습니다
Hello Wade Wilson from Python
```
////
환경변수는 코드 바깥에서 설정될 수 있지만, 코드에서 읽을 수 있고, 나머지 파일과 함께 저장(`git`에 커밋)할 필요가 없으므로, 구성이나 **설정** 에 사용하는 것이 일반적입니다.
또한 **특정 프로그램 호출**에 대해서만 사용할 수 있는 환경 변수를 만들 수도 있는데, 해당 프로그램에서만 사용할 수 있고, 해당 프로그램이 실행되는 동안만 사용할 수 있습니다.
그렇게 하려면 프로그램 바로 앞, 같은 줄에 환경 변수를 만들어야 합니다:
```console
// 이 프로그램 호출을 위해 같은 줄에서 환경 변수 MY_NAME 생성
$ MY_NAME="Wade Wilson" python main.py
// 이제 환경 변수를 읽을 수 있습니다
Hello Wade Wilson from Python
// 이후에는 해당 환경 변수가 존재하지 않습니다
$ python main.py
Hello World from Python
```
/// tip | 팁
[The Twelve-Factor App: Config](https://12factor.net/config) 에서 좀 더 자세히 알아볼 수 있습니다.
///
## 타입과 검증 { #types-and-validation }
이 환경변수들은 오직 **텍스트 문자열**로만 처리할 수 있습니다. 텍스트 문자열은 파이썬 외부에 있으며 다른 프로그램 및 나머지 시스템(그리고 Linux, Windows, macOS 같은 서로 다른 운영 체제에서도)과 호환되어야 합니다.
즉, 파이썬에서 환경 변수로부터 읽은 **모든 값**은 **`str`**이 되고, 다른 타입으로의 변환이나 검증은 코드에서 수행해야 합니다.
**애플리케이션 설정**을 처리하기 위한 환경 변수 사용에 대한 자세한 내용은 [고급 사용자 가이드 - 설정 및 환경 변수](./advanced/settings.md) 에서 확인할 수 있습니다.
## `PATH` 환경 변수 { #path-environment-variable }
**`PATH`**라고 불리는, **특별한** 환경변수가 있습니다. 운영체제(Linux, macOS, Windows)에서 실행할 프로그램을 찾기위해 사용됩니다.
변수 `PATH`의 값은 Linux와 macOS에서는 콜론 `:`, Windows에서는 세미콜론 `;`으로 구분된 디렉토리로 구성된 긴 문자열입니다.
예를 들어, `PATH` 환경 변수는 다음과 같습니다:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
이는 시스템이 다음 디렉토리에서 프로그램을 찾아야 함을 의미합니다:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
터미널에 **명령어**를 입력하면 운영 체제는 `PATH` 환경 변수에 나열된 **각 디렉토리**에서 프로그램을 **찾습니다.**
예를 들어 터미널에 `python`을 입력하면 운영 체제는 해당 목록의 **첫 번째 디렉토리**에서 `python`이라는 프로그램을 찾습니다.
찾으면 **사용합니다**. 그렇지 않으면 **다른 디렉토리**에서 계속 찾습니다.
### 파이썬 설치와 `PATH` 업데이트 { #installing-python-and-updating-the-path }
파이썬을 설치할 때, 아마 `PATH` 환경 변수를 업데이트 할 것이냐고 물어봤을 겁니다.
//// tab | Linux, macOS
파이썬을 설치하고 그것이 `/opt/custompython/bin` 디렉토리에 있다고 가정해 보겠습니다.
`PATH` 환경 변수를 업데이트하도록 "예"라고 하면 설치 관리자가 `/opt/custompython/bin`을 `PATH` 환경 변수에 추가합니다.
다음과 같이 보일 수 있습니다:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
이렇게 하면 터미널에 `python`을 입력할 때, 시스템이 `/opt/custompython/bin`(마지막 디렉토리)에서 파이썬 프로그램을 찾아 사용합니다.
////
//// tab | Windows
파이썬을 설치하고 그것이 `C:\opt\custompython\bin` 디렉토리에 있다고 가정해 보겠습니다.
`PATH` 환경 변수를 업데이트하도록 "예"라고 하면 설치 관리자가 `C:\opt\custompython\bin`을 `PATH` 환경 변수에 추가합니다.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
이렇게 하면 터미널에 `python`을 입력할 때, 시스템이 `C:\opt\custompython\bin`(마지막 디렉토리)에서 파이썬 프로그램을 찾아 사용합니다.
////
그래서, 다음과 같이 입력한다면:
```console
$ python
```
//// tab | Linux, macOS
시스템은 `/opt/custompython/bin`에서 `python` 프로그램을 **찾아** 실행합니다.
다음과 같이 입력하는 것과 거의 같습니다:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
시스템은 `C:\opt\custompython\bin\python`에서 `python` 프로그램을 **찾아** 실행합니다.
다음과 같이 입력하는 것과 거의 같습니다:
```console
$ C:\opt\custompython\bin\python
```
////
이 정보는 [가상 환경](virtual-environments.md) 에 대해 알아볼 때 유용할 것입니다.
## 결론 { #conclusion }
이 문서를 통해 **환경 변수**가 무엇이고 파이썬에서 어떻게 사용하는지 기본적으로 이해하셨을 겁니다.
또한 [환경 변수에 대한 위키피디아](https://en.wikipedia.org/wiki/Environment_variable)에서 이에 대해 자세히 알아볼 수 있습니다.
많은 경우에서, 환경 변수가 어떻게 유용하고 적용 가능한지 바로 명확하게 알 수는 없습니다. 하지만 개발할 때 다양한 시나리오에서 계속 나타나므로 이에 대해 아는 것이 좋습니다.
예를 들어, 다음 섹션인 [가상 환경](virtual-environments.md)에서 이 정보가 필요합니다.
================================================
FILE: docs/ko/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI**는 FastAPI 애플리케이션을 서빙하고, FastAPI 프로젝트를 관리하는 등 다양한 작업에 사용할 수 있는 커맨드 라인 프로그램입니다.
FastAPI를 설치하면(예: `pip install "fastapi[standard]"`) 터미널에서 실행할 수 있는 커맨드 라인 프로그램이 함께 제공됩니다.
개발용으로 FastAPI 애플리케이션을 실행하려면 `fastapi dev` 명령어를 사용할 수 있습니다:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | 팁
프로덕션에서는 `fastapi dev` 대신 `fastapi run`을 사용합니다. 🚀
///
내부적으로 **FastAPI CLI**는 고성능의, 프로덕션에 적합한 ASGI 서버인 [Uvicorn](https://www.uvicorn.dev)을 사용합니다. 😎
`fastapi` CLI는 기본적으로 실행할 FastAPI 앱을 자동으로 감지하려고 시도합니다. `main.py` 파일 안의 `app`이라는 객체(또는 몇 가지 변형)가 있다고 가정합니다.
하지만 사용할 앱을 명시적으로 구성할 수도 있습니다.
## `pyproject.toml`에서 앱 `entrypoint` 구성하기 { #configure-the-app-entrypoint-in-pyproject-toml }
`pyproject.toml` 파일에서 앱이 어디에 있는지 다음과 같이 구성할 수 있습니다:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
이 `entrypoint`는 `fastapi` 명령어에 다음과 같이 앱을 임포트하라고 알려줍니다:
```python
from main import app
```
코드 구조가 다음과 같다면:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
`entrypoint`를 다음과 같이 설정합니다:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
이는 다음과 동일합니다:
```python
from backend.main import app
```
### 경로와 함께 `fastapi dev` { #fastapi-dev-with-path }
`fastapi dev` 명령어에 파일 경로를 전달할 수도 있으며, 그러면 사용할 FastAPI 앱 객체를 추정합니다:
```console
$ fastapi dev main.py
```
하지만 매번 `fastapi` 명령어를 호출할 때 올바른 경로를 전달하는 것을 기억해야 합니다.
또한 [VS Code 확장](editor-support.md)이나 [FastAPI Cloud](https://fastapicloud.com) 같은 다른 도구에서는 이를 찾지 못할 수도 있으므로, `pyproject.toml`의 `entrypoint`를 사용하는 것을 권장합니다.
## `fastapi dev` { #fastapi-dev }
`fastapi dev`를 실행하면 개발 모드가 시작됩니다.
기본적으로 **auto-reload**가 활성화되어 코드에 변경이 생기면 서버를 자동으로 다시 로드합니다. 이는 리소스를 많이 사용하며, 비활성화했을 때보다 안정성이 떨어질 수 있습니다. 개발 환경에서만 사용해야 합니다. 또한 컴퓨터가 자신과만 통신하기 위한(`localhost`) IP인 `127.0.0.1`에서 연결을 대기합니다.
## `fastapi run` { #fastapi-run }
`fastapi run`을 실행하면 프로덕션 모드로 FastAPI가 시작됩니다.
기본적으로 **auto-reload**는 비활성화되어 있습니다. 또한 사용 가능한 모든 IP 주소를 의미하는 `0.0.0.0`에서 연결을 대기하므로, 해당 컴퓨터와 통신할 수 있는 누구에게나 공개적으로 접근 가능해집니다. 보통 프로덕션에서는 이렇게 실행하며, 예를 들어 컨테이너에서 이런 방식으로 실행합니다.
대부분의 경우 위에 "termination proxy"를 두고 HTTPS를 처리하게(그리고 처리해야) 됩니다. 이는 애플리케이션을 배포하는 방식에 따라 달라지며, 제공자가 이 작업을 대신 처리해줄 수도 있고 직접 설정해야 할 수도 있습니다.
/// tip | 팁
자세한 내용은 [배포 문서](deployment/index.md)에서 확인할 수 있습니다.
///
================================================
FILE: docs/ko/docs/features.md
================================================
# 기능 { #features }
## FastAPI의 기능 { #fastapi-features }
**FastAPI**는 다음과 같은 기능을 제공합니다:
### 개방형 표준을 기반으로 { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification): 경로 처리, 매개변수, 요청 본문, 보안 등의 선언을 포함하여 API를 생성합니다.
* [**JSON Schema**](https://json-schema.org/)를 사용한 자동 데이터 모델 문서화(OpenAPI 자체가 JSON Schema를 기반으로 하기 때문입니다).
* 단순히 떠올려서 덧붙인 레이어가 아니라, 세심한 검토를 거친 뒤 이러한 표준을 중심으로 설계되었습니다.
* 이는 또한 다양한 언어로 자동 **클라이언트 코드 생성**을 사용할 수 있게 해줍니다.
### 문서 자동화 { #automatic-docs }
대화형 API 문서와 탐색용 웹 사용자 인터페이스를 제공합니다. 프레임워크가 OpenAPI를 기반으로 하기에 여러 옵션이 있으며, 기본으로 2가지가 포함됩니다.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui)로 대화형 탐색이 가능하며 브라우저에서 직접 API를 호출하고 테스트할 수 있습니다.

* [**ReDoc**](https://github.com/Rebilly/ReDoc)을 이용한 대체 API 문서화.

### 그저 현대 파이썬 { #just-modern-python }
(Pydantic 덕분에) 모든 것이 표준 **Python 타입** 선언을 기반으로 합니다. 새로 배울 문법이 없습니다. 그저 표준적인 현대 파이썬입니다.
Python 타입을 어떻게 사용하는지 2분 정도 복습이 필요하다면(FastAPI를 사용하지 않더라도), 다음의 짧은 자습서를 확인하세요: [Python 타입](python-types.md).
여러분은 타입이 있는 표준 Python을 다음과 같이 작성합니다:
```Python
from datetime import date
from pydantic import BaseModel
# 변수를 str로 선언합니다
# 그리고 함수 내부에서 편집기 지원을 받습니다
def main(user_id: str):
return user_id
# Pydantic 모델
class User(BaseModel):
id: int
name: str
joined: date
```
그 다음 다음과 같이 사용할 수 있습니다:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | 정보
`**second_user_data`는 다음을 의미합니다:
`second_user_data` `dict`의 키와 값을 키-값 인자로서 바로 넘겨주는 것으로, 다음과 동일합니다: `User(id=4, name="Mary", joined="2018-11-30")`
///
### 편집기 지원 { #editor-support }
프레임워크 전체는 사용하기 쉽고 직관적으로 설계되었으며, 최고의 개발 경험을 보장하기 위해 개발을 시작하기도 전에 모든 결정은 여러 편집기에서 테스트되었습니다.
Python 개발자 설문조사에서 [가장 많이 사용되는 기능 중 하나가 "자동 완성"이라는 점](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features)이 분명합니다.
**FastAPI** 프레임워크 전체는 이를 만족하기 위해 만들어졌습니다. 자동 완성은 어디서나 작동합니다.
문서로 다시 돌아올 일은 거의 없을 것입니다.
편집기가 여러분을 어떻게 도와줄 수 있는지 살펴보세요:
* [Visual Studio Code](https://code.visualstudio.com/)에서:

* [PyCharm](https://www.jetbrains.com/pycharm/)에서:

이전에 불가능하다고 생각했을 코드에서도 자동 완성을 받을 수 있습니다. 예를 들어, 요청에서 전달되는(중첩될 수도 있는) JSON 본문 내부의 `price` 키 같은 경우입니다.
더 이상 잘못된 키 이름을 입력하거나, 문서 사이를 왔다 갔다 하거나, `username`을 썼는지 `user_name`을 썼는지 찾으려고 위아래로 스크롤할 필요가 없습니다.
### 간결함 { #short }
선택적 구성을 어디서나 할 수 있도록 하면서도, 모든 것에 합리적인 **기본값**이 설정되어 있습니다. 모든 매개변수는 필요한 작업을 하거나 필요한 API를 정의하기 위해 미세하게 조정할 수 있습니다.
하지만 기본적으로 모든 것이 **"그냥 작동합니다"**.
### 검증 { #validation }
* 다음을 포함해 대부분(혹은 전부?)의 Python **데이터 타입**에 대한 검증:
* JSON 객체 (`dict`).
* 아이템 타입을 정의하는 JSON 배열 (`list`).
* 최소/최대 길이를 정의하는 문자열(`str`) 필드.
* 최소/최대 값을 가지는 숫자(`int`, `float`) 등.
* 다음과 같은 좀 더 이색적인 타입에 대한 검증:
* URL.
* Email.
* UUID.
* ...그 외.
모든 검증은 잘 확립되어 있고 견고한 **Pydantic**이 처리합니다.
### 보안과 인증 { #security-and-authentication }
보안과 인증이 통합되어 있습니다. 데이터베이스나 데이터 모델과 타협하지 않습니다.
다음을 포함해 OpenAPI에 정의된 모든 보안 스키마:
* HTTP Basic.
* **OAuth2**(**JWT tokens** 또한 포함). [JWT를 사용한 OAuth2](tutorial/security/oauth2-jwt.md) 자습서를 확인해 보세요.
* 다음에 들어 있는 API 키:
* 헤더.
* 쿼리 매개변수.
* 쿠키 등.
추가로 Starlette의 모든 보안 기능(**세션 쿠키** 포함)도 제공합니다.
모두 재사용 가능한 도구와 컴포넌트로 만들어져 있어, 여러분의 시스템, 데이터 저장소, 관계형 및 NoSQL 데이터베이스 등과 쉽게 통합할 수 있습니다.
### 의존성 주입 { #dependency-injection }
FastAPI는 사용하기 매우 쉽지만, 매우 강력한 의존성 주입 시스템을 포함하고 있습니다.
* 의존성도 의존성을 가질 수 있어, 의존성의 계층 또는 **의존성의 "그래프"**를 생성합니다.
* 모든 것이 프레임워크에 의해 **자동으로 처리됩니다**.
* 모든 의존성은 요청에서 데이터를 요구할 수 있으며, **경로 처리** 제약과 자동 문서화를 강화할 수 있습니다.
* 의존성에 정의된 *경로 처리* 매개변수에 대해서도 **자동 검증**을 합니다.
* 복잡한 사용자 인증 시스템, **데이터베이스 연결** 등을 지원합니다.
* 데이터베이스, 프론트엔드 등과 **타협하지 않습니다**. 하지만 모두와 쉽게 통합할 수 있습니다.
### 제한 없는 "플러그인" { #unlimited-plug-ins }
또 다른 방식으로는, 그것들이 필요 없습니다. 필요한 코드를 임포트해서 사용하면 됩니다.
어떤 통합이든(의존성과 함께) 사용하기 매우 간단하도록 설계되어 있어, *경로 처리*에 사용된 것과 동일한 구조와 문법을 사용해 2줄의 코드로 애플리케이션용 "플러그인"을 만들 수 있습니다.
### 테스트됨 { #tested }
* 100% 테스트 커버리지.
* 100% 타입 어노테이션 코드 베이스.
* 프로덕션 애플리케이션에서 사용됩니다.
## Starlette 기능 { #starlette-features }
**FastAPI**는 [**Starlette**](https://www.starlette.dev/)와 완전히 호환되며(또한 이를 기반으로 합니다). 따라서 추가로 가지고 있는 Starlette 코드도 모두 동작합니다.
`FastAPI`는 실제로 `Starlette`의 하위 클래스입니다. 그래서 Starlette을 이미 알고 있거나 사용하고 있다면, 대부분의 기능이 같은 방식으로 동작할 것입니다.
**FastAPI**를 사용하면 **Starlette**의 모든 기능을 얻게 됩니다(FastAPI는 Starlette에 강력한 기능을 더한 것입니다):
* 정말 인상적인 성능. [**NodeJS**와 **Go**에 버금가는, 사용 가능한 가장 빠른 Python 프레임워크 중 하나입니다](https://github.com/encode/starlette#performance).
* **WebSocket** 지원.
* 프로세스 내 백그라운드 작업.
* 시작 및 종료 이벤트.
* HTTPX 기반 테스트 클라이언트.
* **CORS**, GZip, 정적 파일, 스트리밍 응답.
* **세션과 쿠키** 지원.
* 100% test coverage.
* 100% type annotated codebase.
## Pydantic 기능 { #pydantic-features }
**FastAPI**는 [**Pydantic**](https://docs.pydantic.dev/)과 완벽하게 호환되며(또한 이를 기반으로 합니다). 따라서 추가로 가지고 있는 Pydantic 코드도 모두 동작합니다.
데이터베이스를 위한 ORM, ODM과 같은, Pydantic을 기반으로 하는 외부 라이브러리도 포함합니다.
이는 모든 것이 자동으로 검증되기 때문에, 많은 경우 요청에서 얻은 동일한 객체를 **직접 데이터베이스로** 넘겨줄 수 있다는 의미이기도 합니다.
반대로도 마찬가지이며, 많은 경우 데이터베이스에서 얻은 객체를 **직접 클라이언트로** 그대로 넘겨줄 수 있습니다.
**FastAPI**를 사용하면(모든 데이터 처리를 위해 FastAPI가 Pydantic을 기반으로 하기에) **Pydantic**의 모든 기능을 얻게 됩니다:
* **No brainfuck**:
* 새로운 스키마 정의 마이크로 언어를 배울 필요가 없습니다.
* Python 타입을 알고 있다면 Pydantic 사용법도 알고 있는 것입니다.
* 여러분의 **IDE/린터/뇌**와 잘 어울립니다:
* pydantic 데이터 구조는 여러분이 정의한 클래스 인스턴스일 뿐이므로, 자동 완성, 린팅, mypy, 그리고 직관까지도 검증된 데이터와 함께 제대로 작동합니다.
* **복잡한 구조**를 검증합니다:
* 계층적인 Pydantic 모델, Python `typing`의 `List`와 `Dict` 등을 사용합니다.
* 그리고 validator는 복잡한 데이터 스키마를 명확하고 쉽게 정의하고, 검사하고, JSON Schema로 문서화할 수 있게 해줍니다.
* 깊게 **중첩된 JSON** 객체를 가질 수 있으며, 이를 모두 검증하고 주석을 달 수 있습니다.
* **확장 가능**:
* Pydantic은 사용자 정의 데이터 타입을 정의할 수 있게 하거나, validator decorator가 붙은 모델 메서드로 검증을 확장할 수 있습니다.
* 100% test coverage.
================================================
FILE: docs/ko/docs/help-fastapi.md
================================================
# FastAPI 지원 - 도움 받기 { #help-fastapi-get-help }
**FastAPI** 가 마음에 드시나요?
FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요?
혹은 **FastAPI** 에 대해 도움이 필요하신가요?
아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로).
또한 도움을 받을 수 있는 방법도 몇 가지 있습니다.
## 뉴스레터 구독 { #subscribe-to-the-newsletter }
(자주 발송되지 않는) [**FastAPI and friends** 뉴스레터](newsletter.md)를 구독하여 최신 정보를 유지할 수 있습니다:
* FastAPI and friends에 대한 뉴스 🚀
* 가이드 📝
* 기능 ✨
* 획기적인 변화 🚨
* 팁과 요령 ✅
## X(Twitter)에서 FastAPI 팔로우하기 { #follow-fastapi-on-x-twitter }
[**X (Twitter)**의 @fastapi를 팔로우](https://x.com/fastapi)하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦
## GitHub에서 **FastAPI**에 Star 주기 { #star-fastapi-in-github }
GitHub에서 FastAPI에 "star"를 붙일 수 있습니다 (오른쪽 상단의 star 버튼을 클릭): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다.
## 릴리즈 확인을 위해 GitHub 저장소 보기 { #watch-the-github-repository-for-releases }
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 "watch" 버튼을 클릭): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
여기서 "Releases only"를 선택할 수 있습니다.
이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 릴리즈(새 버전)가 있을 때마다 (이메일) 통지를 받을 수 있습니다.
## 개발자와의 연결 { #connect-with-the-author }
개발자(작성자)인 [저(Sebastián Ramírez / `tiangolo`)](https://tiangolo.com)와 연락을 취할 수 있습니다.
여러분은 할 수 있습니다:
* [**GitHub**에서 팔로우하기](https://github.com/tiangolo).
* 여러분에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오.
* 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오.
* [**X (Twitter)**](https://x.com/tiangolo) 또는 [Mastodon](https://fosstodon.org/@tiangolo)에서 팔로우하기.
* FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다).
* 발표나 새로운 툴 출시 소식을 받아보십시오.
* [X(Twitter)에서 @fastapi를 팔로우](https://x.com/fastapi) (별도 계정에서) 할 수 있습니다.
* [**LinkedIn**에서 팔로우하기](https://www.linkedin.com/in/tiangolo/).
* 새로운 툴의 발표나 출시 소식을 받아보십시오 (단, X (Twitter)를 더 자주 사용합니다 🤷♂).
* [**Dev.to**](https://dev.to/tiangolo) 또는 [**Medium**](https://medium.com/@tiangolo)에서 제가 작성한 내용을 읽어 보십시오 (또는 팔로우).
* 다른 아이디어와 기사들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오.
* 새로운 내용을 게시할 때 읽기 위해 팔로우 하십시오.
## **FastAPI**에 대해 트윗하기 { #tweet-about-fastapi }
[**FastAPI**에 대해 트윗](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) 하고 저와 다른 사람들에게 FastAPI가 마음에 드는 이유를 알려주세요. 🎉
**FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다.
## FastAPI에 투표하기 { #vote-for-fastapi }
* [Slant에서 **FastAPI** 에 대해 투표하십시오](https://www.slant.co/options/34241/~fastapi-review).
* [AlternativeTo에서 **FastAPI** 에 대해 투표하십시오](https://alternativeto.net/software/fastapi/about/).
* [StackShare에서 **FastAPI**를 사용한다고 표시하세요](https://stackshare.io/pypi-fastapi).
## GitHub에서 질문으로 다른 사람 돕기 { #help-others-with-questions-in-github }
다른 사람들의 질문에 도움을 줄 수 있습니다:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
많은 경우, 여러분은 이미 그 질문에 대한 답을 알고 있을 수도 있습니다. 🤓
만약 많은 사람들의 질문을 도와준다면, 공식적인 [FastAPI 전문가](fastapi-people.md#fastapi-experts)가 될 것입니다. 🎉
가장 중요한 점은: 친절하려고 노력하는 것입니다. 사람들은 좌절감을 안고 오며, 많은 경우 최선의 방식으로 질문하지 않을 수도 있습니다. 하지만 최대한 친절하게 대하려고 노력하세요. 🤗
**FastAPI** 커뮤니티의 목표는 친절하고 환영하는 것입니다. 동시에, 괴롭힘이나 무례한 행동을 받아들이지 마세요. 우리는 서로를 돌봐야 합니다.
---
다른 사람들의 질문(디스커션 또는 이슈에서) 해결을 도울 수 있는 방법은 다음과 같습니다.
### 질문 이해하기 { #understand-the-question }
* 질문하는 사람이 가진 **목적**과 사용 사례를 이해할 수 있는지 확인하세요.
* 그런 다음 질문(대부분은 질문입니다)이 **명확**한지 확인하세요.
* 많은 경우 사용자가 상상한 해결책에 대한 질문을 하지만, 더 **좋은** 해결책이 있을 수 있습니다. 문제와 사용 사례를 더 잘 이해하면 더 나은 **대안적인 해결책**을 제안할 수 있습니다.
* 질문을 이해할 수 없다면, 더 **자세한 정보**를 요청하세요.
### 문제 재현하기 { #reproduce-the-problem }
대부분의 경우 그리고 대부분의 질문에서는 질문자의 **원본 코드**와 관련된 내용이 있습니다.
많은 경우, 코드의 일부만 복사해서 올리지만, 그것만으로는 **문제를 재현**하기에 충분하지 않습니다.
* 질문자에게 [최소한의 재현 가능한 예제](https://stackoverflow.com/help/minimal-reproducible-example)를 제공해달라고 요청할 수 있습니다. 이렇게 하면 코드를 **복사-붙여넣기**하여 로컬에서 실행하고, 질문자가 보고 있는 것과 동일한 오류나 동작을 확인하거나 사용 사례를 더 잘 이해할 수 있습니다.
* 너그러운 마음이 든다면, 문제 설명만을 기반으로 직접 **예제를 만들어**볼 수도 있습니다. 다만 이는 시간이 많이 걸릴 수 있으므로, 먼저 문제를 명확히 해달라고 요청하는 것이 더 나을 수 있다는 점을 기억하세요.
### 해결책 제안하기 { #suggest-solutions }
* 질문을 충분히 이해한 후에는 가능한 **답변**을 제공할 수 있습니다.
* 많은 경우, 질문자의 **근본적인 문제나 사용 사례**를 이해하는 것이 더 좋습니다. 그들이 시도하는 방법보다 더 나은 해결책이 있을 수 있기 때문입니다.
### 종료 요청하기 { #ask-to-close }
질문자가 답변을 하면, 여러분이 문제를 해결했을 가능성이 높습니다. 축하합니다, **여러분은 영웅입니다**! 🦸
* 이제 문제를 해결했다면, 질문자에게 다음을 요청할 수 있습니다.
* GitHub Discussions에서: 댓글을 **답변**으로 표시하도록 요청하세요.
* GitHub Issues에서: 이슈를 **닫아달라고** 요청하세요.
## GitHub 저장소 보기 { #watch-the-github-repository }
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 "watch" 버튼을 클릭): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
"Releases only" 대신 "Watching"을 선택하면 누군가가 새 이슈나 질문을 만들 때 알림을 받게 됩니다. 또한 새 이슈, 디스커션, PR 등만 알림을 받도록 지정할 수도 있습니다.
그런 다음 이런 질문들을 해결하도록 도와줄 수 있습니다.
## 질문하기 { #ask-questions }
GitHub 저장소에서 [새 질문을 생성](https://github.com/fastapi/fastapi/discussions/new?category=questions)할 수 있습니다. 예를 들면 다음과 같습니다:
* **질문**을 하거나 **문제**에 대해 질문합니다.
* 새로운 **기능**을 제안 합니다.
**참고**: 만약 이렇게 한다면, 저는 여러분에게 다른 사람들도 도와달라고 요청할 것입니다. 😉
## Pull Request 리뷰하기 { #review-pull-requests }
다른 사람들이 만든 pull request를 리뷰하는 데 저를 도와줄 수 있습니다.
다시 한번 말하지만, 최대한 친절하게 리뷰해 주세요. 🤗
---
Pull request를 리뷰할 때 고려해야 할 사항과 방법은 다음과 같습니다:
### 문제 이해하기 { #understand-the-problem }
* 먼저, 해당 pull request가 해결하려는 **문제를 이해하는지** 확인하세요. GitHub Discussion 또는 이슈에서 더 긴 논의가 있었을 수도 있습니다.
* Pull request가 실제로 필요하지 않을 가능성도 큽니다. 문제가 **다른 방식**으로 해결될 수 있기 때문입니다. 그런 경우 그 방법을 제안하거나 질문할 수 있습니다.
### 스타일에 너무 신경 쓰지 않기 { #dont-worry-about-style }
* 커밋 메시지 스타일 같은 것에 너무 신경 쓰지 마세요. 저는 커밋을 수동으로 조정해서 squash and merge를 할 것입니다.
* 코드 스타일 규칙도 걱정할 필요 없습니다. 이미 자동화된 도구들이 이를 검사하고 있습니다.
그리고 다른 스타일이나 일관성 관련 필요 사항이 있다면, 제가 직접 요청하거나 필요한 변경 사항을 위에 커밋으로 추가할 것입니다.
### 코드 확인하기 { #check-the-code }
* 코드를 확인하고 읽어서 말이 되는지 보고, **로컬에서 실행**해 실제로 문제가 해결되는지 확인하세요.
* 그런 다음 그렇게 했다고 **댓글**로 남겨 주세요. 그래야 제가 정말로 확인했음을 알 수 있습니다.
/// info | 정보
불행히도, 제가 단순히 여러 개의 승인만으로 PR을 신뢰할 수는 없습니다.
여러 번, 설명이 그럴듯해서인지 3개, 5개 이상의 승인이 달린 PR이 있었지만, 제가 확인해보면 실제로는 깨져 있거나, 버그가 있거나, 주장하는 문제를 해결하지 못하는 경우가 있었습니다. 😅
따라서, 정말로 코드를 읽고 실행한 뒤, 댓글로 확인 내용을 남겨 주는 것이 매우 중요합니다. 🤓
///
* PR을 더 단순하게 만들 수 있다면 그렇게 요청할 수 있지만, 너무 까다로울 필요는 없습니다. 주관적인 견해가 많이 있을 수 있기 때문입니다(그리고 저도 제 견해가 있을 거예요 🙈). 따라서 핵심적인 부분에 집중하는 것이 좋습니다.
### 테스트 { #tests }
* PR에 **테스트**가 포함되어 있는지 확인하는 데 도움을 주세요.
* PR 전에는 테스트가 **실패**하는지 확인하세요. 🚨
* 그런 다음 PR 후에는 테스트가 **통과**하는지 확인하세요. ✅
* 많은 PR에는 테스트가 없습니다. 테스트를 추가하도록 **상기**시켜줄 수도 있고, 직접 테스트를 **제안**할 수도 있습니다. 이는 시간이 가장 많이 드는 것들 중 하나이며, 그 부분을 많이 도와줄 수 있습니다.
* 그리고 시도한 내용을 댓글로 남겨주세요. 그러면 제가 확인했다는 걸 알 수 있습니다. 🤓
## Pull Request 만들기 { #create-a-pull-request }
Pull Requests를 이용하여 소스 코드에 [기여](contributing.md)할 수 있습니다. 예를 들면 다음과 같습니다:
* 문서에서 발견한 오타를 수정할 때.
* FastAPI에 대한 글, 비디오, 팟캐스트를 작성했거나 발견했다면 [이 파일을 편집](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml)하여 공유할 때.
* 해당 섹션의 시작 부분에 링크를 추가해야 합니다.
* 여러분의 언어로 [문서 번역에](contributing.md#translations) 도움을 줄 때.
* 다른 사람이 작성한 번역을 검토하는 것도 도울 수 있습니다.
* 새로운 문서 섹션을 제안할 때.
* 기존 이슈/버그를 수정할 때.
* 테스트를 반드시 추가해야 합니다.
* 새로운 기능을 추가할 때.
* 테스트를 반드시 추가해야 합니다.
* 관련 문서가 있다면 반드시 추가해야 합니다.
## FastAPI 유지 관리 돕기 { #help-maintain-fastapi }
**FastAPI** 유지를 도와주세요! 🤓
할 일이 많고, 그중 대부분은 **여러분**이 할 수 있습니다.
지금 할 수 있는 주요 작업은:
* [GitHub에서 질문으로 다른 사람 돕기](#help-others-with-questions-in-github) (위의 섹션을 참조하세요).
* [Pull Request 리뷰하기](#review-pull-requests) (위의 섹션을 참조하세요).
이 두 작업이 **가장 많은 시간을 소모**합니다. 이것이 FastAPI를 유지 관리하는 주요 작업입니다.
이 작업을 도와주신다면, **FastAPI 유지를 돕는 것**이며 FastAPI가 **더 빠르고 더 잘 발전하는 것**을 보장하는 것입니다. 🚀
## 채팅에 참여하기 { #join-the-chat }
👥 [Discord 채팅 서버](https://discord.gg/VQjSZaeJmf) 👥 에 참여해서 FastAPI 커뮤니티의 다른 사람들과 어울리세요.
/// tip | 팁
질문은 [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions)에서 하세요. [FastAPI Experts](fastapi-people.md#fastapi-experts)로부터 도움을 받을 가능성이 훨씬 높습니다.
채팅은 다른 일반적인 대화를 위해서만 사용하세요.
///
### 질문을 위해 채팅을 사용하지 마세요 { #dont-use-the-chat-for-questions }
채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 답하기 어려운 질문을 쉽게 할 수 있어 답변을 받지 못할 수도 있다는 점을 기억하세요.
GitHub에서는 템플릿이 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나, 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 제가 항상 모든 것에 답하도록 보장할 수 있습니다. 채팅 시스템에서는 제가 개인적으로 그렇게 할 수 없습니다. 😅
채팅 시스템에서의 대화 또한 GitHub만큼 쉽게 검색할 수 없기 때문에, 질문과 답변이 대화 속에서 사라질 수 있습니다. 그리고 GitHub에 있는 것만 [FastAPI Expert](fastapi-people.md#fastapi-experts)가 되는 것으로 인정되므로, GitHub에서 더 많은 관심을 받게 될 가능성이 큽니다.
반면, 채팅 시스템에는 수천 명의 사용자가 있으므로, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄
## 개발자 스폰서 되기 { #sponsor-the-author }
여러분의 **제품/회사**가 **FastAPI**에 의존하거나 관련되어 있고, FastAPI 사용자를 대상으로 알리고 싶다면 [GitHub sponsors](https://github.com/sponsors/tiangolo)를 통해 개발자(저)를 스폰서할 수 있습니다. 티어에 따라 문서에 배지 같은 추가 혜택을 받을 수도 있습니다. 🎁
---
감사합니다! 🚀
================================================
FILE: docs/ko/docs/history-design-future.md
================================================
# 역사, 디자인 그리고 미래 { #history-design-and-future }
얼마 전, [한 **FastAPI** 사용자가 이렇게 물었습니다](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> 이 프로젝트의 역사는 무엇인가요? 몇 주 만에 아무 데서도 갑자기 나타나 엄청나게 좋아진 것처럼 보이네요 [...]
여기서 그 역사에 대해 간단히 설명하겠습니다.
## 대안 { #alternatives }
저는 여러 해 동안 복잡한 요구사항(머신러닝, 분산 시스템, 비동기 작업, NoSQL 데이터베이스 등)을 가진 API를 만들면서 여러 개발 팀을 이끌어 왔습니다.
그 과정에서 많은 대안을 조사하고, 테스트하고, 사용해야 했습니다.
**FastAPI**의 역사는 상당 부분 그 이전에 있던 도구들의 역사입니다.
[대안](alternatives.md) 섹션에서 언급된 것처럼:
**FastAPI**는 다른 사람들이 이전에 해온 작업이 없었다면 존재하지 않았을 것입니다.
그 전에 만들어진 많은 도구들이 이것의 탄생에 영감을 주었습니다.
저는 여러 해 동안 새로운 프레임워크를 만드는 것을 피하고 있었습니다. 처음에는 **FastAPI**가 다루는 모든 기능을 여러 다른 프레임워크, 플러그인, 도구들을 사용해 해결하려고 했습니다.
하지만 어느 시점에는, 이전 도구들의 최고의 아이디어를 가져와 가능한 한 최선의 방식으로 조합하고, 이전에는 존재하지 않았던 언어 기능(Python 3.6+ type hints)을 사용해 이 모든 기능을 제공하는 무언가를 만드는 것 외에는 다른 선택지가 없었습니다.
## 조사 { #investigation }
이전의 모든 대안을 사용해 보면서, 각 도구로부터 배울 기회를 얻었고, 아이디어를 가져와 제가 일해온 개발 팀들과 저 자신에게 가장 적합하다고 찾은 방식으로 조합할 수 있었습니다.
예를 들어, 이상적으로는 표준 Python 타입 힌트에 기반해야 한다는 점이 분명했습니다.
또한, 가장 좋은 접근법은 이미 존재하는 표준을 사용하는 것이었습니다.
그래서 **FastAPI**의 코딩을 시작하기도 전에, OpenAPI, JSON Schema, OAuth2 등과 같은 명세를 몇 달 동안 공부했습니다. 이들의 관계, 겹치는 부분, 차이점을 이해하기 위해서였습니다.
## 디자인 { #design }
그 다음에는 (FastAPI를 사용하는 개발자로서) 사용자로서 갖고 싶었던 개발자 "API"를 디자인하는 데 시간을 썼습니다.
가장 인기 있는 Python 편집기들: PyCharm, VS Code, Jedi 기반 편집기에서 여러 아이디어를 테스트했습니다.
약 80%의 사용자를 포함하는 최근 [Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools)에 따르면 그렇습니다.
즉, **FastAPI**는 Python 개발자의 80%가 사용하는 편집기들로 특별히 테스트되었습니다. 그리고 대부분의 다른 편집기도 유사하게 동작하는 경향이 있으므로, 그 모든 이점은 사실상 모든 편집기에서 동작해야 합니다.
그렇게 해서 코드 중복을 가능한 한 많이 줄이고, 어디서나 자동 완성, 타입 및 에러 검사 등을 제공하는 최선의 방법을 찾을 수 있었습니다.
모든 개발자에게 최고의 개발 경험을 제공하는 방식으로 말입니다.
## 필요조건 { #requirements }
여러 대안을 테스트한 후, 장점 때문에 [**Pydantic**](https://docs.pydantic.dev/)을 사용하기로 결정했습니다.
그 후, JSON Schema를 완전히 준수하도록 하고, 제약 조건 선언을 정의하는 다양한 방식을 지원하며, 여러 편집기에서의 테스트를 바탕으로 편집기 지원(타입 검사, 자동 완성)을 개선하기 위해 기여했습니다.
개발 과정에서, 또 다른 핵심 필요조건인 [**Starlette**](https://www.starlette.dev/)에도 기여했습니다.
## 개발 { #development }
**FastAPI** 자체를 만들기 시작했을 때쯤에는, 대부분의 조각들이 이미 갖춰져 있었고, 디자인은 정의되어 있었며, 필요조건과 도구는 준비되어 있었고, 표준과 명세에 대한 지식도 명확하고 최신 상태였습니다.
## 미래 { #future }
이 시점에는, **FastAPI**가 그 아이디어와 함께 많은 사람들에게 유용하다는 것이 이미 분명합니다.
많은 사용 사례에 더 잘 맞기 때문에 이전 대안들보다 선택되고 있습니다.
많은 개발자와 팀이 이미 자신의 프로젝트를 위해 **FastAPI**에 의존하고 있습니다(저와 제 팀도 포함해서요).
하지만 여전히, 앞으로 나올 개선 사항과 기능들이 많이 있습니다.
**FastAPI**의 미래는 밝습니다.
그리고 [여러분의 도움](help-fastapi.md)은 큰 힘이 됩니다.
================================================
FILE: docs/ko/docs/how-to/authentication-error-status-code.md
================================================
# 이전 403 인증 오류 상태 코드 사용하기 { #use-old-403-authentication-error-status-codes }
FastAPI 버전 `0.122.0` 이전에는, 통합 보안 유틸리티가 인증 실패 후 클라이언트에 오류를 반환할 때 HTTP 상태 코드 `403 Forbidden`을 사용했습니다.
FastAPI 버전 `0.122.0`부터는 더 적절한 HTTP 상태 코드 `401 Unauthorized`를 사용하며, HTTP 명세인 [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized)를 따라 응답에 합리적인 `WWW-Authenticate` 헤더를 반환합니다.
하지만 어떤 이유로든 클라이언트가 이전 동작에 의존하고 있다면, 보안 클래스에서 `make_not_authenticated_error` 메서드를 오버라이드하여 이전 동작으로 되돌릴 수 있습니다.
예를 들어, 기본값인 `401 Unauthorized` 오류 대신 `403 Forbidden` 오류를 반환하는 `HTTPBearer`의 서브클래스를 만들 수 있습니다:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | 팁
함수는 예외를 `return`하는 것이지 `raise`하지 않는다는 점에 유의하세요. 예외를 발생시키는(`raise`) 작업은 내부 코드의 나머지 부분에서 수행됩니다.
///
================================================
FILE: docs/ko/docs/how-to/conditional-openapi.md
================================================
# 조건부 OpenAPI { #conditional-openapi }
필요한 경우, 설정 및 환경 변수를 사용하여 환경에 따라 OpenAPI를 조건부로 구성하고 완전히 비활성화할 수도 있습니다.
## 보안, API 및 docs에 대해서 { #about-security-apis-and-docs }
프로덕션에서, 문서화된 사용자 인터페이스(UI)를 숨기는 것이 API를 보호하는 방법이 되어서는 안 됩니다.
이는 API에 추가적인 보안을 제공하지 않으며, *경로 처리*는 여전히 동일한 위치에서 사용 할 수 있습니다.
코드에 보안 결함이 있다면, 그 결함은 여전히 존재할 것입니다.
문서를 숨기는 것은 API와 상호작용하는 방법을 이해하기 어렵게 만들며, 프로덕션에서 디버깅을 더 어렵게 만들 수 있습니다. 이는 단순히 [은폐를 통한 보안](https://en.wikipedia.org/wiki/Security_through_obscurity)의 한 형태로 간주될 수 있습니다.
API를 보호하고 싶다면, 예를 들어 다음과 같은 더 나은 방법들이 있습니다:
* 요청 본문과 응답에 대해 잘 정의된 Pydantic 모델이 있는지 확인하세요.
* 종속성을 사용하여 필요한 권한과 역할을 구성하세요.
* 평문 비밀번호를 절대 저장하지 말고, 비밀번호 해시만 저장하세요.
* pwdlib와 JWT 토큰 등과 같은 잘 알려진 암호화 도구들을 구현하고 사용하세요.
* 필요한 곳에 OAuth2 범위를 사용하여 더 세분화된 권한 제어를 추가하세요.
* ...등등.
그럼에도 불구하고, 특정 환경(예: 프로덕션)에서 또는 환경 변수의 설정에 따라 API docs를 비활성화해야 하는 매우 특정한 사용 사례가 있을 수 있습니다.
## 설정 및 환경변수의 조건부 OpenAPI { #conditional-openapi-from-settings-and-env-vars }
동일한 Pydantic 설정을 사용하여 생성된 OpenAPI 및 docs UI를 쉽게 구성할 수 있습니다.
예를 들어:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
여기서 `openapi_url` 설정을 기본값인 `"/openapi.json"`으로 선언합니다.
그런 뒤, 우리는 `FastAPI` 앱을 만들 때 그것을 사용합니다.
그런 다음 환경 변수 `OPENAPI_URL`을 빈 문자열로 설정하여 OpenAPI(UI docs 포함)를 비활성화할 수도 있습니다. 예를 들어:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
그리고 `/openapi.json`, `/docs` 또는 `/redoc`의 URL로 이동하면 `404 Not Found`라는 오류가 다음과 같이 표시됩니다:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/ko/docs/how-to/configure-swagger-ui.md
================================================
# Swagger UI 구성 { #configure-swagger-ui }
추가적인 [Swagger UI 매개변수](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/)를 구성할 수 있습니다.
구성을 하려면, `FastAPI()` 앱 객체를 생성할 때 또는 `get_swagger_ui_html()` 함수에 `swagger_ui_parameters` 인수를 전달하십시오.
`swagger_ui_parameters`는 Swagger UI에 직접 전달된 구성을 포함하는 딕셔너리를 받습니다.
FastAPI는 이 구성을 **JSON** 형식으로 변환하여 JavaScript와 호환되도록 합니다. 이는 Swagger UI에서 필요로 하는 형식입니다.
## 구문 강조 비활성화 { #disable-syntax-highlighting }
예를 들어, Swagger UI에서 구문 강조 기능을 비활성화할 수 있습니다.
설정을 변경하지 않으면, 기본적으로 구문 강조 기능이 활성화되어 있습니다:
그러나 `syntaxHighlight`를 `False`로 설정하여 구문 강조 기능을 비활성화할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...그럼 Swagger UI에서 더 이상 구문 강조 기능이 표시되지 않습니다:
## 테마 변경 { #change-the-theme }
동일한 방식으로 `"syntaxHighlight.theme"` 키를 사용하여 구문 강조 테마를 설정할 수 있습니다 (중간에 점이 포함된 것을 참고하십시오).
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
이 설정은 구문 강조 색상 테마를 변경합니다:
## 기본 Swagger UI 매개변수 변경 { #change-default-swagger-ui-parameters }
FastAPI는 대부분의 사용 사례에 적합한 몇 가지 기본 구성 매개변수를 포함하고 있습니다.
기본 구성에는 다음이 포함됩니다:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
`swagger_ui_parameters` 인수에 다른 값을 설정하여 이러한 기본값 중 어느 것이든 재정의할 수 있습니다.
예를 들어, `deepLinking`을 비활성화하려면 `swagger_ui_parameters`에 다음 설정을 전달할 수 있습니다:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## 기타 Swagger UI 매개변수 { #other-swagger-ui-parameters }
사용할 수 있는 다른 모든 구성 옵션을 확인하려면, 공식 [Swagger UI 매개변수 문서](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/)를 참조하십시오.
## JavaScript 전용 설정 { #javascript-only-settings }
Swagger UI는 **JavaScript 전용** 객체(예: JavaScript 함수)로 다른 구성을 허용하기도 합니다.
FastAPI는 이러한 JavaScript 전용 `presets` 설정을 포함하고 있습니다:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
이들은 문자열이 아닌 **JavaScript** 객체이므로 Python 코드에서 직접 전달할 수 없습니다.
이와 같은 JavaScript 전용 구성을 사용해야 하는 경우, 위의 방법 중 하나를 사용할 수 있습니다. Swagger UI *경로 처리*를 모두 재정의하고 필요한 JavaScript를 수동으로 작성하세요.
================================================
FILE: docs/ko/docs/how-to/custom-docs-ui-assets.md
================================================
# 커스텀 Docs UI 정적 에셋(자체 호스팅) { #custom-docs-ui-static-assets-self-hosting }
API 문서는 **Swagger UI**와 **ReDoc**을 사용하며, 각각 JavaScript와 CSS 파일이 필요합니다.
기본적으로 이러한 파일은 CDN에서 제공됩니다.
하지만 이를 커스터마이징할 수 있으며, 특정 CDN을 지정하거나 파일을 직접 제공할 수도 있습니다.
## JavaScript와 CSS용 커스텀 CDN { #custom-cdn-for-javascript-and-css }
예를 들어 다른 CDN을 사용하고 싶다고 해봅시다. 예를 들면 `https://unpkg.com/`을 사용하려는 경우입니다.
이는 예를 들어 특정 국가에서 일부 URL을 제한하는 경우에 유용할 수 있습니다.
### 자동 문서 비활성화하기 { #disable-the-automatic-docs }
첫 번째 단계는 자동 문서를 비활성화하는 것입니다. 기본적으로 자동 문서는 기본 CDN을 사용하기 때문입니다.
비활성화하려면 `FastAPI` 앱을 생성할 때 해당 URL을 `None`으로 설정하세요:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### 커스텀 문서 포함하기 { #include-the-custom-docs }
이제 커스텀 문서를 위한 *경로 처리*를 만들 수 있습니다.
FastAPI 내부 함수를 재사용해 문서용 HTML 페이지를 생성하고, 필요한 인자를 전달할 수 있습니다:
* `openapi_url`: 문서 HTML 페이지가 API의 OpenAPI 스키마를 가져올 수 있는 URL입니다. 여기서는 `app.openapi_url` 속성을 사용할 수 있습니다.
* `title`: API의 제목입니다.
* `oauth2_redirect_url`: 기본값을 사용하려면 여기서 `app.swagger_ui_oauth2_redirect_url`을 사용할 수 있습니다.
* `swagger_js_url`: Swagger UI 문서의 HTML이 **JavaScript** 파일을 가져올 수 있는 URL입니다. 커스텀 CDN URL입니다.
* `swagger_css_url`: Swagger UI 문서의 HTML이 **CSS** 파일을 가져올 수 있는 URL입니다. 커스텀 CDN URL입니다.
ReDoc도 마찬가지입니다...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | 팁
`swagger_ui_redirect`에 대한 *경로 처리*는 OAuth2를 사용할 때 도움이 되는 헬퍼입니다.
API를 OAuth2 provider와 통합하면 인증을 수행한 뒤 획득한 자격 증명으로 API 문서로 다시 돌아올 수 있습니다. 그리고 실제 OAuth2 인증을 사용해 API와 상호작용할 수 있습니다.
Swagger UI가 이 과정을 백그라운드에서 처리해 주지만, 이를 위해 이 "redirect" 헬퍼가 필요합니다.
///
### 테스트용 *경로 처리* 만들기 { #create-a-path-operation-to-test-it }
이제 모든 것이 제대로 동작하는지 테스트할 수 있도록 *경로 처리*를 하나 만드세요:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### 테스트하기 { #test-it }
이제 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)에서 문서에 접속한 뒤 페이지를 새로고침하면, 새 CDN에서 에셋을 불러오는 것을 확인할 수 있습니다.
## 문서용 JavaScript와 CSS 자체 호스팅하기 { #self-hosting-javascript-and-css-for-docs }
JavaScript와 CSS를 자체 호스팅하는 것은 예를 들어, 오프라인 상태이거나 외부 인터넷에 접근할 수 없는 환경, 또는 로컬 네트워크에서도 앱이 계속 동작해야 할 때 유용할 수 있습니다.
여기서는 동일한 FastAPI 앱에서 해당 파일을 직접 제공하고, 문서가 이를 사용하도록 설정하는 방법을 살펴봅니다.
### 프로젝트 파일 구조 { #project-file-structure }
프로젝트 파일 구조가 다음과 같다고 해봅시다:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
이제 해당 정적 파일을 저장할 디렉터리를 만드세요.
새 파일 구조는 다음과 같을 수 있습니다:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### 파일 다운로드하기 { #download-the-files }
문서에 필요한 정적 파일을 다운로드해서 `static/` 디렉터리에 넣으세요.
각 링크를 우클릭한 뒤 "링크를 다른 이름으로 저장..."과 비슷한 옵션을 선택하면 될 것입니다.
**Swagger UI**는 다음 파일을 사용합니다:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
**ReDoc**은 다음 파일을 사용합니다:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
이후 파일 구조는 다음과 같을 수 있습니다:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### 정적 파일 제공하기 { #serve-the-static-files }
* `StaticFiles`를 import합니다.
* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트(mount)"합니다.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### 정적 파일 테스트하기 { #test-the-static-files }
애플리케이션을 시작하고 [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js)로 이동하세요.
**ReDoc**용 매우 긴 JavaScript 파일이 보일 것입니다.
예를 들어 다음과 같이 시작할 수 있습니다:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
이는 앱에서 정적 파일을 제공할 수 있고, 문서용 정적 파일을 올바른 위치에 배치했다는 것을 확인해 줍니다.
이제 문서가 이 정적 파일을 사용하도록 앱을 설정할 수 있습니다.
### 정적 파일을 위한 자동 문서 비활성화하기 { #disable-the-automatic-docs-for-static-files }
커스텀 CDN을 사용할 때와 마찬가지로, 첫 단계는 자동 문서를 비활성화하는 것입니다. 자동 문서는 기본적으로 CDN을 사용합니다.
비활성화하려면 `FastAPI` 앱을 생성할 때 해당 URL을 `None`으로 설정하세요:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### 정적 파일을 위한 커스텀 문서 포함하기 { #include-the-custom-docs-for-static-files }
그리고 커스텀 CDN을 사용할 때와 동일한 방식으로, 이제 커스텀 문서를 위한 *경로 처리*를 만들 수 있습니다.
다시 한 번, FastAPI 내부 함수를 재사용해 문서용 HTML 페이지를 생성하고, 필요한 인자를 전달할 수 있습니다:
* `openapi_url`: 문서 HTML 페이지가 API의 OpenAPI 스키마를 가져올 수 있는 URL입니다. 여기서는 `app.openapi_url` 속성을 사용할 수 있습니다.
* `title`: API의 제목입니다.
* `oauth2_redirect_url`: 기본값을 사용하려면 여기서 `app.swagger_ui_oauth2_redirect_url`을 사용할 수 있습니다.
* `swagger_js_url`: Swagger UI 문서의 HTML이 **JavaScript** 파일을 가져올 수 있는 URL입니다. **이제는 여러분의 앱이 직접 제공하는 파일입니다**.
* `swagger_css_url`: Swagger UI 문서의 HTML이 **CSS** 파일을 가져올 수 있는 URL입니다. **이제는 여러분의 앱이 직접 제공하는 파일입니다**.
ReDoc도 마찬가지입니다...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | 팁
`swagger_ui_redirect`에 대한 *경로 처리*는 OAuth2를 사용할 때 도움이 되는 헬퍼입니다.
API를 OAuth2 provider와 통합하면 인증을 수행한 뒤 획득한 자격 증명으로 API 문서로 다시 돌아올 수 있습니다. 그리고 실제 OAuth2 인증을 사용해 API와 상호작용할 수 있습니다.
Swagger UI가 이 과정을 백그라운드에서 처리해 주지만, 이를 위해 이 "redirect" 헬퍼가 필요합니다.
///
### 정적 파일 테스트용 *경로 처리* 만들기 { #create-a-path-operation-to-test-static-files }
이제 모든 것이 제대로 동작하는지 테스트할 수 있도록 *경로 처리*를 하나 만드세요:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### 정적 파일 UI 테스트하기 { #test-static-files-ui }
이제 WiFi 연결을 끊고 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)에서 문서에 접속한 뒤 페이지를 새로고침해 보세요.
인터넷이 없어도 API 문서를 보고, API와 상호작용할 수 있을 것입니다.
================================================
FILE: docs/ko/docs/how-to/custom-request-and-route.md
================================================
# 커스텀 Request 및 APIRoute 클래스 { #custom-request-and-apiroute-class }
일부 경우에는 `Request`와 `APIRoute` 클래스에서 사용되는 로직을 오버라이드하고 싶을 수 있습니다.
특히, 이는 middleware에 있는 로직의 좋은 대안이 될 수 있습니다.
예를 들어, 애플리케이션에서 처리되기 전에 요청 바디를 읽거나 조작하고 싶을 때가 그렇습니다.
/// danger | 위험
이 기능은 "고급" 기능입니다.
**FastAPI**를 이제 막 시작했다면 이 섹션은 건너뛰는 것이 좋습니다.
///
## 사용 사례 { #use-cases }
사용 사례에는 다음이 포함됩니다:
* JSON이 아닌 요청 바디를 JSON으로 변환하기(예: [`msgpack`](https://msgpack.org/index.html)).
* gzip으로 압축된 요청 바디 압축 해제하기.
* 모든 요청 바디를 자동으로 로깅하기.
## 커스텀 요청 바디 인코딩 처리하기 { #handling-custom-request-body-encodings }
커스텀 `Request` 서브클래스를 사용해 gzip 요청의 압축을 해제하는 방법을 살펴보겠습니다.
그리고 그 커스텀 요청 클래스를 사용하기 위한 `APIRoute` 서브클래스도 함께 보겠습니다.
### 커스텀 `GzipRequest` 클래스 만들기 { #create-a-custom-gziprequest-class }
/// tip | 팁
이 예시는 동작 방식 시연을 위한 장난감 예제입니다. Gzip 지원이 필요하다면 제공되는 [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware)를 사용할 수 있습니다.
///
먼저, `GzipRequest` 클래스를 만듭니다. 이 클래스는 `Request.body()` 메서드를 덮어써서, 적절한 헤더가 있는 경우 바디를 압축 해제합니다.
헤더에 `gzip`이 없으면 바디를 압축 해제하려고 시도하지 않습니다.
이렇게 하면 동일한 route 클래스가 gzip으로 압축된 요청과 압축되지 않은 요청을 모두 처리할 수 있습니다.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### 커스텀 `GzipRoute` 클래스 만들기 { #create-a-custom-gziproute-class }
다음으로, `GzipRequest`를 활용하는 `fastapi.routing.APIRoute`의 커스텀 서브클래스를 만듭니다.
이번에는 `APIRoute.get_route_handler()` 메서드를 오버라이드합니다.
이 메서드는 함수를 반환합니다. 그리고 그 함수가 요청을 받아 응답을 반환합니다.
여기서는 원본 요청으로부터 `GzipRequest`를 만들기 위해 이를 사용합니다.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | 기술 세부사항
`Request`에는 `request.scope` 속성이 있는데, 이는 요청과 관련된 메타데이터를 담고 있는 Python `dict`입니다.
`Request`에는 또한 `request.receive`가 있는데, 이는 요청의 바디를 "받기(receive)" 위한 함수입니다.
`scope` `dict`와 `receive` 함수는 모두 ASGI 명세의 일부입니다.
그리고 이 두 가지, `scope`와 `receive`가 새로운 `Request` 인스턴스를 만드는 데 필요한 것들입니다.
`Request`에 대해 더 알아보려면 [Starlette의 Requests 문서](https://www.starlette.dev/requests/)를 확인하세요.
///
`GzipRequest.get_route_handler`가 반환하는 함수가 다르게 하는 유일한 것은 `Request`를 `GzipRequest`로 변환하는 것입니다.
이렇게 하면, 우리의 `GzipRequest`가 *경로 처리*로 전달하기 전에(필요하다면) 데이터의 압축 해제를 담당하게 됩니다.
그 이후의 모든 처리 로직은 동일합니다.
하지만 `GzipRequest.body`에서 변경을 했기 때문에, 필요할 때 **FastAPI**가 로드하는 시점에 요청 바디는 자동으로 압축 해제됩니다.
## 예외 핸들러에서 요청 바디 접근하기 { #accessing-the-request-body-in-an-exception-handler }
/// tip | 팁
같은 문제를 해결하려면 `RequestValidationError`에 대한 커스텀 핸들러에서 `body`를 사용하는 편이 아마 훨씬 더 쉽습니다([오류 처리하기](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
하지만 이 예시도 여전히 유효하며, 내부 컴포넌트와 상호작용하는 방법을 보여줍니다.
///
같은 접근 방식을 사용해 예외 핸들러에서 요청 바디에 접근할 수도 있습니다.
필요한 것은 `try`/`except` 블록 안에서 요청을 처리하는 것뿐입니다:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
예외가 발생하더라도 `Request` 인스턴스는 여전히 스코프 안에 남아 있으므로, 오류를 처리할 때 요청 바디를 읽고 활용할 수 있습니다:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## 라우터에서의 커스텀 `APIRoute` 클래스 { #custom-apiroute-class-in-a-router }
`APIRouter`의 `route_class` 파라미터를 설정할 수도 있습니다:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
이 예시에서는 `router` 아래의 *경로 처리*들이 커스텀 `TimedRoute` 클래스를 사용하며, 응답을 생성하는 데 걸린 시간을 담은 추가 `X-Response-Time` 헤더가 응답에 포함됩니다:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/ko/docs/how-to/extending-openapi.md
================================================
# OpenAPI 확장하기 { #extending-openapi }
생성된 OpenAPI 스키마를 수정해야 하는 경우가 있습니다.
이 섹션에서 그 방법을 살펴보겠습니다.
## 일반적인 과정 { #the-normal-process }
일반적인(기본) 과정은 다음과 같습니다.
`FastAPI` 애플리케이션(인스턴스)에는 OpenAPI 스키마를 반환해야 하는 `.openapi()` 메서드가 있습니다.
애플리케이션 객체를 생성하는 과정에서 `/openapi.json`(또는 `openapi_url`에 설정한 경로)용 *경로 처리*가 등록됩니다.
이 경로 처리는 애플리케이션의 `.openapi()` 메서드 결과를 JSON 응답으로 반환할 뿐입니다.
기본적으로 `.openapi()` 메서드는 프로퍼티 `.openapi_schema`에 내용이 있는지 확인하고, 있으면 그 내용을 반환합니다.
없으면 `fastapi.openapi.utils.get_openapi`에 있는 유틸리티 함수를 사용해 생성합니다.
그리고 `get_openapi()` 함수는 다음을 파라미터로 받습니다:
* `title`: 문서에 표시되는 OpenAPI 제목.
* `version`: API 버전. 예: `2.5.0`.
* `openapi_version`: 사용되는 OpenAPI 스펙 버전. 기본값은 최신인 `3.1.0`.
* `summary`: API에 대한 짧은 요약.
* `description`: API 설명. markdown을 포함할 수 있으며 문서에 표시됩니다.
* `routes`: 라우트 목록. 각각 등록된 *경로 처리*입니다. `app.routes`에서 가져옵니다.
/// info | 정보
`summary` 파라미터는 OpenAPI 3.1.0 이상에서 사용할 수 있으며, FastAPI 0.99.0 이상에서 지원됩니다.
///
## 기본값 덮어쓰기 { #overriding-the-defaults }
위 정보를 바탕으로, 동일한 유틸리티 함수를 사용해 OpenAPI 스키마를 생성하고 필요한 각 부분을 덮어쓸 수 있습니다.
예를 들어, [커스텀 로고를 포함하기 위한 ReDoc의 OpenAPI 확장](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo)을 추가해 보겠습니다.
### 일반적인 **FastAPI** { #normal-fastapi }
먼저, 평소처럼 **FastAPI** 애플리케이션을 모두 작성합니다:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### OpenAPI 스키마 생성하기 { #generate-the-openapi-schema }
그다음 `custom_openapi()` 함수 안에서, 동일한 유틸리티 함수를 사용해 OpenAPI 스키마를 생성합니다:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### OpenAPI 스키마 수정하기 { #modify-the-openapi-schema }
이제 OpenAPI 스키마의 `info` "object"에 커스텀 `x-logo`를 추가하여 ReDoc 확장을 더할 수 있습니다:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### OpenAPI 스키마 캐시하기 { #cache-the-openapi-schema }
생성한 스키마를 저장하기 위한 "cache"로 `.openapi_schema` 프로퍼티를 사용할 수 있습니다.
이렇게 하면 사용자가 API 문서를 열 때마다 애플리케이션이 스키마를 매번 생성하지 않아도 됩니다.
스키마는 한 번만 생성되고, 이후 요청에서는 같은 캐시된 스키마가 사용됩니다.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### 메서드 오버라이드하기 { #override-the-method }
이제 `.openapi()` 메서드를 새 함수로 교체할 수 있습니다.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### 확인하기 { #check-it }
[http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)로 이동하면 커스텀 로고(이 예시에서는 **FastAPI** 로고)를 사용하는 것을 확인할 수 있습니다:
================================================
FILE: docs/ko/docs/how-to/general.md
================================================
# 일반 - 사용 방법 - 레시피 { #general-how-to-recipes }
일반적이거나 자주 나오는 질문에 대해, 문서의 다른 위치로 안내하는 몇 가지 포인터를 소개합니다.
## 데이터 필터링 - 보안 { #filter-data-security }
반환하면 안 되는 데이터를 과도하게 반환하지 않도록 하려면, [튜토리얼 - 응답 모델 - 반환 타입](../tutorial/response-model.md) 문서를 읽어보세요.
## 응답 성능 최적화 - 응답 모델 - 반환 타입 { #optimize-response-performance-response-model-return-type }
JSON 데이터를 반환할 때 성능을 최적화하려면 반환 타입 또는 응답 모델을 사용하세요. 그러면 Pydantic이 Python을 거치지 않고 Rust 측에서 JSON 직렬화를 처리합니다. 자세한 내용은 [튜토리얼 - 응답 모델 - 반환 타입](../tutorial/response-model.md) 문서를 참고하세요.
## 문서화 태그 - OpenAPI { #documentation-tags-openapi }
*경로 처리*에 태그를 추가하고, 문서 UI에서 이를 그룹화하려면 [튜토리얼 - 경로 처리 구성 - 태그](../tutorial/path-operation-configuration.md#tags) 문서를 읽어보세요.
## 문서화 요약 및 설명 - OpenAPI { #documentation-summary-and-description-openapi }
*경로 처리*에 요약과 설명을 추가하고, 문서 UI에 표시하려면 [튜토리얼 - 경로 처리 구성 - 요약 및 설명](../tutorial/path-operation-configuration.md#summary-and-description) 문서를 읽어보세요.
## 문서화 응답 설명 - OpenAPI { #documentation-response-description-openapi }
문서 UI에 표시되는 응답의 설명을 정의하려면 [튜토리얼 - 경로 처리 구성 - 응답 설명](../tutorial/path-operation-configuration.md#response-description) 문서를 읽어보세요.
## 문서화 *경로 처리* 지원 중단하기 - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
*경로 처리*를 지원 중단(deprecate)으로 표시하고, 문서 UI에 보여주려면 [튜토리얼 - 경로 처리 구성 - 지원 중단](../tutorial/path-operation-configuration.md#deprecate-a-path-operation) 문서를 읽어보세요.
## 어떤 데이터든 JSON 호환으로 변환하기 { #convert-any-data-to-json-compatible }
어떤 데이터든 JSON 호환 형식으로 변환하려면 [튜토리얼 - JSON 호환 인코더](../tutorial/encoder.md) 문서를 읽어보세요.
## OpenAPI 메타데이터 - 문서 { #openapi-metadata-docs }
라이선스, 버전, 연락처 등의 정보를 포함해 OpenAPI 스키마에 메타데이터를 추가하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md) 문서를 읽어보세요.
## OpenAPI 사용자 정의 URL { #openapi-custom-url }
OpenAPI URL을 커스터마이즈(또는 제거)하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md#openapi-url) 문서를 읽어보세요.
## OpenAPI 문서 URL { #openapi-docs-urls }
자동으로 생성되는 문서 사용자 인터페이스에서 사용하는 URL을 업데이트하려면 [튜토리얼 - 메타데이터와 문서 URL](../tutorial/metadata.md#docs-urls) 문서를 읽어보세요.
================================================
FILE: docs/ko/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
**FastAPI**는 **ASGI** 표준을 기반으로 하므로, ASGI와도 호환되는 어떤 **GraphQL** 라이브러리든 매우 쉽게 통합할 수 있습니다.
같은 애플리케이션에서 일반 FastAPI **경로 처리**와 GraphQL을 함께 조합할 수 있습니다.
/// tip | 팁
**GraphQL**은 몇 가지 매우 특정한 사용 사례를 해결합니다.
일반적인 **web API**와 비교했을 때 **장점**과 **단점**이 있습니다.
여러분의 사용 사례에서 **이점**이 **단점**을 상쇄하는지 꼭 평가해 보세요. 🤓
///
## GraphQL 라이브러리 { #graphql-libraries }
다음은 **ASGI** 지원이 있는 **GraphQL** 라이브러리들입니다. **FastAPI**와 함께 사용할 수 있습니다:
* [Strawberry](https://strawberry.rocks/) 🍓
* [FastAPI용 문서](https://strawberry.rocks/docs/integrations/fastapi) 제공
* [Ariadne](https://ariadnegraphql.org/)
* [FastAPI용 문서](https://ariadnegraphql.org/docs/fastapi-integration) 제공
* [Tartiflette](https://tartiflette.io/)
* ASGI 통합을 제공하기 위해 [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) 사용
* [Graphene](https://graphene-python.org/)
* [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3) 사용
## Strawberry로 GraphQL 사용하기 { #graphql-with-strawberry }
**GraphQL**로 작업해야 하거나 작업하고 싶다면, [**Strawberry**](https://strawberry.rocks/)를 **권장**합니다. **FastAPI**의 설계와 가장 가깝고, 모든 것이 **type annotations**에 기반해 있기 때문입니다.
사용 사례에 따라 다른 라이브러리를 선호할 수도 있지만, 제게 묻는다면 아마 **Strawberry**를 먼저 시도해 보라고 제안할 것입니다.
다음은 Strawberry를 FastAPI와 통합하는 방법에 대한 간단한 미리보기입니다:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
[Strawberry 문서](https://strawberry.rocks/)에서 Strawberry에 대해 더 알아볼 수 있습니다.
또한 [FastAPI에서 Strawberry 사용](https://strawberry.rocks/docs/integrations/fastapi)에 대한 문서도 확인해 보세요.
## Starlette의 예전 `GraphQLApp` { #older-graphqlapp-from-starlette }
이전 버전의 Starlette에는 [Graphene](https://graphene-python.org/)과 통합하기 위한 `GraphQLApp` 클래스가 포함되어 있었습니다.
이것은 Starlette에서 deprecated 되었지만, 이를 사용하던 코드가 있다면 같은 사용 사례를 다루고 **거의 동일한 인터페이스**를 가진 [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)로 쉽게 **마이그레이션**할 수 있습니다.
/// tip | 팁
GraphQL이 필요하다면, 커스텀 클래스와 타입 대신 type annotations에 기반한 [Strawberry](https://strawberry.rocks/)를 여전히 확인해 보시길 권장합니다.
///
## 더 알아보기 { #learn-more }
[공식 GraphQL 문서](https://graphql.org/)에서 **GraphQL**에 대해 더 알아볼 수 있습니다.
또한 위에서 설명한 각 라이브러리에 대해서도 해당 링크에서 더 자세히 읽어볼 수 있습니다.
================================================
FILE: docs/ko/docs/how-to/index.md
================================================
# How To - 레시피 { #how-to-recipes }
여기에서는 **여러 주제**에 대한 다양한 레시피(“how to” 가이드)를 볼 수 있습니다.
대부분의 아이디어는 어느 정도 **서로 독립적**이며, 대부분의 경우 **여러분의 프로젝트**에 직접 적용되는 경우에만 학습하면 됩니다.
프로젝트에 흥미롭고 유용해 보이는 것이 있다면 확인해 보세요. 그렇지 않다면 아마 건너뛰어도 됩니다.
/// tip | 팁
**FastAPI를 구조적으로 학습**하고 싶다면(권장), 대신 [튜토리얼 - 사용자 가이드](../tutorial/index.md)를 장별로 읽어보세요.
///
================================================
FILE: docs/ko/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Pydantic v1에서 Pydantic v2로 마이그레이션하기 { #migrate-from-pydantic-v1-to-pydantic-v2 }
오래된 FastAPI 앱이 있다면 Pydantic 버전 1을 사용하고 있을 수 있습니다.
FastAPI 0.100.0 버전은 Pydantic v1 또는 v2 중 하나를 지원했습니다. 설치되어 있는 쪽을 사용했습니다.
FastAPI 0.119.0 버전에서는 v2로의 마이그레이션을 쉽게 하기 위해, Pydantic v2 내부에서 Pydantic v1을(`pydantic.v1`로) 부분적으로 지원하기 시작했습니다.
FastAPI 0.126.0 버전에서는 Pydantic v1 지원을 중단했지만, `pydantic.v1`은 잠시 동안 계속 지원했습니다.
/// warning | 경고
Pydantic 팀은 최신 Python 버전에서 Pydantic v1 지원을 중단했으며, 시작 버전은 **Python 3.14**입니다.
여기에는 `pydantic.v1`도 포함되며, Python 3.14 이상에서는 더 이상 지원되지 않습니다.
Python의 최신 기능을 사용하려면 Pydantic v2를 사용하고 있는지 확인해야 합니다.
///
Pydantic v1을 사용하는 오래된 FastAPI 앱이 있다면, 여기서는 이를 Pydantic v2로 마이그레이션하는 방법과 점진적 마이그레이션을 돕는 **FastAPI 0.119.0의 기능**을 소개하겠습니다.
## 공식 가이드 { #official-guide }
Pydantic에는 v1에서 v2로의 공식 [마이그레이션 가이드](https://docs.pydantic.dev/latest/migration/)가 있습니다.
여기에는 무엇이 바뀌었는지, 검증이 이제 어떻게 더 정확하고 엄격해졌는지, 가능한 주의사항 등도 포함되어 있습니다.
변경된 내용을 더 잘 이해하기 위해 읽어보면 좋습니다.
## 테스트 { #tests }
앱에 대한 [테스트](../tutorial/testing.md)가 있는지 확인하고, 지속적 통합(CI)에서 테스트를 실행하세요.
이렇게 하면 업그레이드를 진행하면서도 모든 것이 기대한 대로 계속 동작하는지 확인할 수 있습니다.
## `bump-pydantic` { #bump-pydantic }
많은 경우, 커스터마이징 없이 일반적인 Pydantic 모델을 사용하고 있다면 Pydantic v1에서 Pydantic v2로의 마이그레이션 과정 대부분을 자동화할 수 있습니다.
같은 Pydantic 팀이 제공하는 [`bump-pydantic`](https://github.com/pydantic/bump-pydantic)를 사용할 수 있습니다.
이 도구는 변경해야 하는 코드의 대부분을 자동으로 바꾸는 데 도움을 줍니다.
그 다음 테스트를 실행해서 모든 것이 동작하는지 확인하면 됩니다. 잘 된다면 끝입니다. 😎
## v2 안의 Pydantic v1 { #pydantic-v1-in-v2 }
Pydantic v2는 Pydantic v1의 모든 것을 서브모듈 `pydantic.v1`로 포함합니다. 하지만 이는 Python 3.13보다 높은 버전에서는 더 이상 지원되지 않습니다.
즉, Pydantic v2의 최신 버전을 설치한 뒤, 이 서브모듈에서 예전 Pydantic v1 구성 요소를 import하여 예전 Pydantic v1을 설치한 것처럼 사용할 수 있습니다.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### v2 안의 Pydantic v1에 대한 FastAPI 지원 { #fastapi-support-for-pydantic-v1-in-v2 }
FastAPI 0.119.0부터는 v2로의 마이그레이션을 쉽게 하기 위해, Pydantic v2 내부의 Pydantic v1에 대해서도 부분적인 지원이 있습니다.
따라서 Pydantic을 최신 v2로 업그레이드하고, import를 `pydantic.v1` 서브모듈을 사용하도록 바꾸면, 많은 경우 그대로 동작합니다.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | 경고
Pydantic 팀이 Python 3.14부터 최신 Python 버전에서 Pydantic v1을 더 이상 지원하지 않으므로, `pydantic.v1`을 사용하는 것 역시 Python 3.14 이상에서는 지원되지 않는다는 점을 염두에 두세요.
///
### 같은 앱에서 Pydantic v1과 v2 함께 사용하기 { #pydantic-v1-and-v2-on-the-same-app }
Pydantic에서는 Pydantic v2 모델의 필드를 Pydantic v1 모델로 정의하거나 그 반대로 하는 것을 **지원하지 않습니다**.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...하지만 같은 앱에서 Pydantic v1과 v2를 사용하되, 모델을 분리해서 둘 수는 있습니다.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
어떤 경우에는 FastAPI 앱의 같은 **경로 처리**에서 Pydantic v1과 v2 모델을 함께 사용하는 것도 가능합니다:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
위 예제에서 입력 모델은 Pydantic v1 모델이고, 출력 모델(`response_model=ItemV2`로 정의됨)은 Pydantic v2 모델입니다.
### Pydantic v1 파라미터 { #pydantic-v1-parameters }
Pydantic v1 모델과 함께 `Body`, `Query`, `Form` 등 파라미터용 FastAPI 전용 도구 일부를 사용해야 한다면, Pydantic v2로의 마이그레이션을 마칠 때까지 `fastapi.temp_pydantic_v1_params`에서 import할 수 있습니다:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### 단계적으로 마이그레이션하기 { #migrate-in-steps }
/// tip | 팁
먼저 `bump-pydantic`로 시도해 보세요. 테스트가 통과하고 잘 동작한다면, 한 번의 명령으로 끝입니다. ✨
///
`bump-pydantic`가 여러분의 사용 사례에 맞지 않는다면, 같은 앱에서 Pydantic v1과 v2 모델을 모두 지원하는 기능을 이용해 Pydantic v2로 점진적으로 마이그레이션할 수 있습니다.
먼저 Pydantic을 최신 v2로 업그레이드하고, 모든 모델의 import를 `pydantic.v1`을 사용하도록 바꿀 수 있습니다.
그 다음 Pydantic v1에서 v2로 모델을 그룹 단위로, 점진적인 단계로 마이그레이션을 시작하면 됩니다. 🚶
================================================
FILE: docs/ko/docs/how-to/separate-openapi-schemas.md
================================================
# 입력과 출력에 대해 OpenAPI 스키마를 분리할지 여부 { #separate-openapi-schemas-for-input-and-output-or-not }
**Pydantic v2**가 릴리스된 이후, 생성되는 OpenAPI는 이전보다 조금 더 정확하고 **올바르게** 만들어집니다. 😎
실제로 어떤 경우에는, 같은 Pydantic 모델에 대해 OpenAPI 안에 **두 개의 JSON Schema**가 생기기도 합니다. **기본값(default value)**이 있는지 여부에 따라, 입력용과 출력용으로 나뉩니다.
이것이 어떻게 동작하는지, 그리고 필요하다면 어떻게 변경할 수 있는지 살펴보겠습니다.
## 입력과 출력을 위한 Pydantic 모델 { #pydantic-models-for-input-and-output }
예를 들어, 다음처럼 기본값이 있는 Pydantic 모델이 있다고 해보겠습니다:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### 입력용 모델 { #model-for-input }
이 모델을 다음처럼 입력으로 사용하면:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
...`description` 필드는 **필수가 아닙니다**. `None`이라는 기본값이 있기 때문입니다.
### 문서에서의 입력 모델 { #input-model-in-docs }
문서에서 `description` 필드에 **빨간 별표**가 없고, 필수로 표시되지 않는 것을 확인할 수 있습니다:
### 출력용 모델 { #model-for-output }
하지만 같은 모델을 다음처럼 출력으로 사용하면:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
...`description`에 기본값이 있기 때문에, 그 필드에 대해 **아무것도 반환하지 않더라도** 여전히 그 **기본값**이 들어가게 됩니다.
### 출력 응답 데이터용 모델 { #model-for-output-response-data }
문서에서 직접 동작시켜 응답을 확인해 보면, 코드가 `description` 필드 중 하나에 아무것도 추가하지 않았더라도 JSON 응답에는 기본값(`null`)이 포함되어 있습니다:
이는 해당 필드가 **항상 값을 가진다는 것**을 의미합니다. 다만 그 값이 때로는 `None`(JSON에서는 `null`)일 수 있습니다.
즉, API를 사용하는 클라이언트는 값이 존재하는지 여부를 확인할 필요가 없고, **필드가 항상 존재한다고 가정**할 수 있습니다. 다만 어떤 경우에는 기본값 `None`이 들어갑니다.
이를 OpenAPI에서 표현하는 방법은, 그 필드를 **required**로 표시하는 것입니다. 항상 존재하기 때문입니다.
이 때문에, 하나의 모델이라도 **입력용인지 출력용인지**에 따라 JSON Schema가 달라질 수 있습니다:
* **입력**에서는 `description`이 **필수가 아님**
* **출력**에서는 **필수임** (그리고 값은 `None`일 수도 있으며, JSON 용어로는 `null`)
### 문서에서의 출력용 모델 { #model-for-output-in-docs }
문서에서 출력 모델을 확인해 보면, `name`과 `description` **둘 다** **빨간 별표**로 **필수**로 표시되어 있습니다:
### 문서에서의 입력과 출력 모델 { #model-for-input-and-output-in-docs }
또 OpenAPI에서 사용 가능한 모든 Schemas(JSON Schemas)를 확인해 보면, `Item-Input` 하나와 `Item-Output` 하나, 이렇게 두 개가 있는 것을 볼 수 있습니다.
`Item-Input`에서는 `description`이 **필수가 아니며**, 빨간 별표가 없습니다.
하지만 `Item-Output`에서는 `description`이 **필수이며**, 빨간 별표가 있습니다.
**Pydantic v2**의 이 기능 덕분에 API 문서는 더 **정밀**해지고, 자동 생성된 클라이언트와 SDK가 있다면 그것들도 더 정밀해져서 더 나은 **developer experience**와 일관성을 제공할 수 있습니다. 🎉
## 스키마를 분리하지 않기 { #do-not-separate-schemas }
이제 어떤 경우에는 **입력과 출력에 대해 같은 스키마를 사용**하고 싶을 수도 있습니다.
가장 대표적인 경우는, 이미 자동 생성된 클라이언트 코드/SDK가 있고, 아직은 그 자동 생성된 클라이언트 코드/SDK들을 전부 업데이트하고 싶지 않은 경우입니다. 언젠가는 업데이트해야 할 가능성이 높지만, 지금 당장은 아닐 수도 있습니다.
그런 경우에는, **FastAPI**에서 `separate_input_output_schemas=False` 파라미터로 이 기능을 비활성화할 수 있습니다.
/// info | 정보
`separate_input_output_schemas` 지원은 FastAPI `0.102.0`에 추가되었습니다. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### 문서에서 입력과 출력 모델에 같은 스키마 사용 { #same-schema-for-input-and-output-models-in-docs }
이제 모델에 대해 입력과 출력 모두에 사용되는 단일 스키마(오직 `Item`만)가 생성되며, `description`은 **필수가 아닌 것**으로 표시됩니다:
================================================
FILE: docs/ko/docs/how-to/testing-database.md
================================================
# 데이터베이스 테스트하기 { #testing-a-database }
데이터베이스, SQL, SQLModel에 대해서는 [SQLModel 문서](https://sqlmodel.tiangolo.com/)에서 학습할 수 있습니다. 🤓
[FastAPI에서 SQLModel을 사용하는 방법에 대한 미니 튜토리얼](https://sqlmodel.tiangolo.com/tutorial/fastapi/)도 있습니다. ✨
해당 튜토리얼에는 [SQL 데이터베이스 테스트](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/)에 대한 섹션도 포함되어 있습니다. 😎
================================================
FILE: docs/ko/docs/index.md
================================================
# FastAPI { #fastapi }
FastAPI 프레임워크, 고성능, 간편한 학습, 빠른 코드 작성, 준비된 프로덕션
---
**문서**: [https://fastapi.tiangolo.com/ko](https://fastapi.tiangolo.com/ko)
**소스 코드**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트에 기초한 Python의 API를 빌드하기 위한 웹 프레임워크입니다.
주요 특징으로:
* **빠름**: (Starlette과 Pydantic 덕분에) **NodeJS** 및 **Go**와 대등할 정도로 매우 높은 성능. [사용 가능한 가장 빠른 파이썬 프레임워크 중 하나](#performance).
* **빠른 코드 작성**: 약 200%에서 300%까지 기능 개발 속도 증가. *
* **적은 버그**: 사람(개발자)에 의한 에러 약 40% 감소. *
* **직관적**: 훌륭한 편집기 지원. 자동완성이 모든 곳에서 동작. 적은 디버깅 시간.
* **쉬움**: 쉽게 사용하고 배우도록 설계. 적은 문서 읽기 시간.
* **짧음**: 코드 중복 최소화. 각 매개변수 선언의 여러 기능. 적은 버그.
* **견고함**: 준비된 프로덕션 용 코드를 얻으십시오. 자동 대화형 문서와 함께.
* **표준 기반**: API에 대한 (완전히 호환되는) 개방형 표준 기반: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (이전에 Swagger로 알려졌던) 및 [JSON Schema](https://json-schema.org/).
* 내부 개발팀의 프로덕션 애플리케이션을 빌드한 테스트에 근거한 측정
## 스폰서 { #sponsors }
### 키스톤 스폰서 { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### 골드 및 실버 스폰서 { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[다른 스폰서](https://fastapi.tiangolo.com/ko/fastapi-people/#sponsors)
## 의견들 { #opinions }
"_[...] 저는 요즘 **FastAPI**를 많이 사용하고 있습니다. [...] 사실 우리 팀의 **마이크로소프트 ML 서비스** 전부를 바꿀 계획입니다. 그중 일부는 핵심 **Windows**와 몇몇의 **Office** 제품들이 통합되고 있습니다._"
---
"_REST API를 만들기 위해 **현대적인 프레임워크**를 찾고 있다면 **FastAPI**를 확인해 보십시오. [...] 빠르고, 쓰기 쉽고, 배우기도 쉽습니다 [...]_"
"_우리 **API**를 **FastAPI**로 바꿨습니다 [...] 아마 여러분도 좋아하실 것입니다 [...]_"
---
"_프로덕션 Python API를 만들고자 한다면, 저는 **FastAPI**를 강력히 추천합니다. **아름답게 설계**되었고, **사용이 간단**하며, **확장성이 매우 뛰어나**고, 우리의 API 우선 개발 전략에서 **핵심 구성 요소**가 되었으며 Virtual TAC Engineer 같은 많은 자동화와 서비스를 이끌고 있습니다._"
---
## FastAPI 미니 다큐멘터리 { #fastapi-mini-documentary }
2025년 말에 공개된 [FastAPI 미니 다큐멘터리](https://www.youtube.com/watch?v=mpR8ngthqiE)가 있습니다. 온라인에서 시청할 수 있습니다:
## **Typer**, CLI를 위한 FastAPI { #typer-the-fastapi-of-clis }
웹 API 대신 터미널에서 사용할 CLI 앱을 만들고 있다면, [**Typer**](https://typer.tiangolo.com/)를 확인해 보십시오.
**Typer**는 FastAPI의 동생입니다. 그리고 **CLI를 위한 FastAPI**가 되기 위해 생겼습니다. ⌨️ 🚀
## 요구사항 { #requirements }
FastAPI는 거인들의 어깨 위에 서 있습니다:
* [Starlette](https://www.starlette.dev/) — 웹 부분을 담당합니다.
* [Pydantic](https://docs.pydantic.dev/) — 데이터 부분을 담당합니다.
## 설치 { #installation }
[가상 환경](https://fastapi.tiangolo.com/ko/virtual-environments/)을 생성하고 활성화한 다음 FastAPI를 설치하세요:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
fastapi dev 명령에 관하여...
`fastapi dev` 명령은 여러분의 `main.py` 파일을 자동으로 읽고, 그 안의 **FastAPI** 앱을 감지한 다음, [Uvicorn](https://www.uvicorn.dev)을 사용해 서버를 시작합니다.
기본적으로 `fastapi dev`는 로컬 개발을 위해 auto-reload가 활성화된 상태로 시작됩니다.
자세한 내용은 [FastAPI CLI 문서](https://fastapi.tiangolo.com/ko/fastapi-cli/)에서 확인할 수 있습니다.
### 확인하기 { #check-it }
브라우저로 [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery)를 열어보십시오.
아래의 JSON 응답을 볼 수 있습니다:
```JSON
{"item_id": 5, "q": "somequery"}
```
여러분은 벌써 API를 만들었습니다:
* _경로_ `/` 및 `/items/{item_id}`에서 HTTP 요청 받기.
* 두 _경로_ 모두 `GET` 연산(HTTP _메소드_ 로 알려진)을 받습니다.
* _경로_ `/items/{item_id}`는 `int`형 이어야 하는 _경로 매개변수_ `item_id`를 가지고 있습니다.
* _경로_ `/items/{item_id}`는 선택적인 `str`형 _쿼리 매개변수_ `q`를 가지고 있습니다.
### 대화형 API 문서 { #interactive-api-docs }
이제 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)로 가보십시오.
자동 대화형 API 문서를 볼 수 있습니다 ([Swagger UI](https://github.com/swagger-api/swagger-ui) 제공):

### 대안 API 문서 { #alternative-api-docs }
그리고 이제 [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)로 가봅시다.
다른 자동 문서를 볼 수 있습니다([ReDoc](https://github.com/Rebilly/ReDoc) 제공):

## 예제 업그레이드 { #example-upgrade }
이제 `PUT` 요청에서 본문을 받기 위해 `main.py` 파일을 수정해봅시다.
Pydantic 덕분에 표준 Python 타입을 사용해 본문을 선언합니다.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
`fastapi dev` 서버는 자동으로 리로딩되어야 합니다.
### 대화형 API 문서 업그레이드 { #interactive-api-docs-upgrade }
이제 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)로 이동합니다.
* 대화형 API 문서는 새 본문을 포함해 자동으로 업데이트됩니다:

* "Try it out" 버튼을 클릭하면, 매개변수를 채우고 API와 직접 상호작용할 수 있습니다:

* 그런 다음 "Execute" 버튼을 클릭하면, 사용자 인터페이스가 API와 통신하고 매개변수를 전송한 뒤 결과를 받아 화면에 표시합니다:

### 대안 API 문서 업그레이드 { #alternative-api-docs-upgrade }
그리고 이제, [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)로 이동합니다.
* 대안 문서 역시 새 쿼리 매개변수와 본문을 반영합니다:

### 요약 { #recap }
요약하면, 여러분은 매개변수의 타입, 본문 등을 함수 매개변수로서 **한번에** 선언했습니다.
여러분은 현대 표준 파이썬 타입으로 이를 행했습니다.
새로운 문법, 특정 라이브러리의 메소드나 클래스 등을 배울 필요가 없습니다.
그저 표준 **Python** 입니다.
예를 들어, `int`에 대해선:
```Python
item_id: int
```
또는 좀 더 복잡한 `Item` 모델에 대해선:
```Python
item: Item
```
...그리고 단 하나의 선언으로 여러분이 얻는 것은:
* 다음을 포함한 편집기 지원:
* 자동완성.
* 타입 검사.
* 데이터 검증:
* 데이터가 유효하지 않을 때 자동으로 생성하는 명확한 에러.
* 깊이 중첩된 JSON 객체에 대한 유효성 검사.
* 입력 데이터 변환: 네트워크에서 파이썬 데이터 및 타입으로 전송. 읽을 수 있는 것들:
* JSON.
* 경로 매개변수.
* 쿼리 매개변수.
* 쿠키.
* 헤더.
* 폼(Forms).
* 파일.
* 출력 데이터 변환: 파이썬 데이터 및 타입을 네트워크 데이터로 전환(JSON 형식으로):
* 파이썬 타입 변환 (`str`, `int`, `float`, `bool`, `list`, 등).
* `datetime` 객체.
* `UUID` 객체.
* 데이터베이스 모델.
* ...더 많은 것들.
* 대안가능한 사용자 인터페이스를 2개 포함한 자동 대화형 API 문서:
* Swagger UI.
* ReDoc.
---
이전 코드 예제로 돌아가서, **FastAPI**는 다음처럼 처리합니다:
* `GET` 및 `PUT` 요청에 `item_id`가 경로에 있는지 검증.
* `GET` 및 `PUT` 요청에 `item_id`가 `int` 타입인지 검증.
* 그렇지 않다면 클라이언트는 유용하고 명확한 에러를 볼 수 있습니다.
* `GET` 요청에 `q`라는 선택적인 쿼리 매개변수가 있는지 검사(`http://127.0.0.1:8000/items/foo?q=somequery`처럼).
* `q` 매개변수는 `= None`으로 선언되었기 때문에 선택사항입니다.
* `None`이 없다면 필수사항입니다(`PUT`의 경우와 마찬가지로).
* `/items/{item_id}`으로의 `PUT` 요청은 본문을 JSON으로 읽음:
* `name`을 필수 속성으로 갖고 `str` 형인지 검사.
* `price`를 필수 속성으로 갖고 `float` 형이어야 하는지 검사.
* 만약 주어진다면, `is_offer`를 선택 속성으로 갖고 `bool` 형이어야 하는지 검사.
* 이 모든 것은 깊이 중첩된 JSON 객체에도 적용됩니다.
* JSON을 변환하거나 JSON으로 변환하는 것을 자동화.
* 다음에서 사용할 수 있는 모든 것을 OpenAPI로 문서화:
* 대화형 문서 시스템.
* 여러 언어들에 대한 자동 클라이언트 코드 생성 시스템.
* 2개의 대화형 문서 웹 인터페이스를 직접 제공.
---
우리는 그저 수박 겉 핥기만 했을 뿐인데 여러분은 벌써 어떻게 작동하는지 알고 있습니다.
다음 줄을 바꿔보십시오:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...에서:
```Python
... "item_name": item.name ...
```
...으로:
```Python
... "item_price": item.price ...
```
...그러고 나서 여러분의 편집기가 속성과 타입을 알고 자동 완성하는지 보십시오:

더 많은 기능을 포함한 보다 완전한 예제의 경우, 튜토리얼 - 사용자 가이드를 보십시오.
**스포일러 주의**: 튜토리얼 - 사용자 가이드는:
* 서로 다른 장소에서 **매개변수** 선언: **헤더**, **쿠키**, **폼 필드** 그리고 **파일**.
* `maximum_length` 또는 `regex`처럼 **유효성 제약**하는 방법.
* 강력하고 사용하기 쉬운 **의존성 주입** 시스템.
* **OAuth2** 지원을 포함한 **JWT tokens** 및 **HTTP Basic**을 갖는 보안과 인증.
* (Pydantic 덕분에) **깊은 중첩 JSON 모델**을 선언하는데 더 진보한 (하지만 마찬가지로 쉬운) 기술.
* [Strawberry](https://strawberry.rocks) 및 기타 라이브러리와의 **GraphQL** 통합.
* (Starlette 덕분에) 많은 추가 기능:
* **웹 소켓**
* HTTPX 및 `pytest`에 기반한 극히 쉬운 테스트
* **CORS**
* **쿠키 세션**
* ...기타 등등.
### 앱 배포하기(선택 사항) { #deploy-your-app-optional }
선택적으로 FastAPI 앱을 [FastAPI Cloud](https://fastapicloud.com)에 배포할 수 있습니다. 아직이라면 대기자 명단에 등록해 보세요. 🚀
이미 **FastAPI Cloud** 계정이 있다면(대기자 명단에서 초대해 드렸습니다 😉), 한 번의 명령으로 애플리케이션을 배포할 수 있습니다.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
#### FastAPI Cloud 소개 { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**는 **FastAPI** 뒤에 있는 동일한 작성자와 팀이 만들었습니다.
최소한의 노력으로 API를 **빌드**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**하는 데까지 확장해 줍니다. 🎉
FastAPI Cloud는 *FastAPI and friends* 오픈 소스 프로젝트의 주요 스폰서이자 자금 제공자입니다. ✨
#### 다른 클라우드 제공자에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 선택한 어떤 클라우드 제공자에도 FastAPI 앱을 배포할 수 있습니다.
클라우드 제공자의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 성능 { #performance }
독립된 TechEmpower 벤치마크에서 Uvicorn에서 작동하는 **FastAPI** 애플리케이션이 [사용 가능한 가장 빠른 Python 프레임워크 중 하나](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7)로 Starlette와 Uvicorn(FastAPI에서 내부적으로 사용)에만 밑돌고 있습니다. (*)
자세한 내용은 [벤치마크](https://fastapi.tiangolo.com/ko/benchmarks/) 섹션을 보십시오.
## 의존성 { #dependencies }
FastAPI는 Pydantic과 Starlette에 의존합니다.
### `standard` 의존성 { #standard-dependencies }
`pip install "fastapi[standard]"`로 FastAPI를 설치하면 `standard` 그룹의 선택적 의존성이 함께 설치됩니다.
Pydantic이 사용하는:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - 이메일 유효성 검사.
Starlette이 사용하는:
* [`httpx`](https://www.python-httpx.org) - `TestClient`를 사용하려면 필요.
* [`jinja2`](https://jinja.palletsprojects.com) - 기본 템플릿 설정을 사용하려면 필요.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - `request.form()`과 함께 form "파싱" 지원을 원하면 필요.
FastAPI가 사용하는:
* [`uvicorn`](https://www.uvicorn.dev) - 애플리케이션을 로드하고 제공하는 서버를 위한 것입니다. 여기에는 고성능 서빙에 필요한 일부 의존성(예: `uvloop`)이 포함된 `uvicorn[standard]`가 포함됩니다.
* `fastapi-cli[standard]` - `fastapi` 명령을 제공하기 위한 것입니다.
* 여기에는 [FastAPI Cloud](https://fastapicloud.com)에 FastAPI 애플리케이션을 배포할 수 있게 해주는 `fastapi-cloud-cli`가 포함됩니다.
### `standard` 의존성 없이 { #without-standard-dependencies }
`standard` 선택적 의존성을 포함하고 싶지 않다면, `pip install "fastapi[standard]"` 대신 `pip install fastapi`로 설치할 수 있습니다.
### `fastapi-cloud-cli` 없이 { #without-fastapi-cloud-cli }
표준 의존성과 함께 FastAPI를 설치하되 `fastapi-cloud-cli` 없이 설치하고 싶다면, `pip install "fastapi[standard-no-fastapi-cloud-cli]"`로 설치할 수 있습니다.
### 추가 선택적 의존성 { #additional-optional-dependencies }
추가로 설치하고 싶을 수 있는 의존성도 있습니다.
추가 선택적 Pydantic 의존성:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - 설정 관리를 위한 것입니다.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - Pydantic에서 사용할 추가 타입을 위한 것입니다.
추가 선택적 FastAPI 의존성:
* [`orjson`](https://github.com/ijl/orjson) - `ORJSONResponse`를 사용하려면 필요.
* [`ujson`](https://github.com/esnme/ultrajson) - `UJSONResponse`를 사용하려면 필요.
## 라이센스 { #license }
이 프로젝트는 MIT 라이센스 조약에 따라 라이센스가 부여됩니다.
================================================
FILE: docs/ko/docs/learn/index.md
================================================
# 배우기 { #learn }
여기 **FastAPI**를 배우기 위한 입문 섹션과 자습서가 있습니다.
여러분은 이것을 FastAPI를 배우기 위한 **책**, **강의**, **공식**이자 권장되는 방법으로 생각할 수 있습니다. 😎
================================================
FILE: docs/ko/docs/project-generation.md
================================================
# Full Stack FastAPI 템플릿 { #full-stack-fastapi-template }
템플릿은 일반적으로 특정 설정과 함께 제공되지만, 유연하고 커스터마이징이 가능하게 디자인 되었습니다. 이 특성들은 여러분이 프로젝트의 요구사항에 맞춰 수정, 적용을 할 수 있게 해주고, 템플릿이 완벽한 시작점이 되게 해줍니다. 🏁
많은 초기 설정, 보안, 데이터베이스 및 일부 API 엔드포인트가 이미 준비되어 있으므로, 여러분은 이 템플릿을 시작하는 데 사용할 수 있습니다.
GitHub 저장소: [Full Stack FastAPI 템플릿](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI 템플릿 - 기술 스택과 기능들 { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ Python 백엔드 API를 위한 [**FastAPI**](https://fastapi.tiangolo.com/ko).
- 🧰 Python SQL 데이터베이스 상호작용을 위한 [SQLModel](https://sqlmodel.tiangolo.com) (ORM).
- 🔍 FastAPI에 의해 사용되는, 데이터 검증과 설정 관리를 위한 [Pydantic](https://docs.pydantic.dev).
- 💾 SQL 데이터베이스로서의 [PostgreSQL](https://www.postgresql.org).
- 🚀 프론트엔드를 위한 [React](https://react.dev).
- 💃 TypeScript, hooks, Vite 및 기타 현대적인 프론트엔드 스택을 사용.
- 🎨 프론트엔드 컴포넌트를 위한 [Tailwind CSS](https://tailwindcss.com) 및 [shadcn/ui](https://ui.shadcn.com).
- 🤖 자동으로 생성된 프론트엔드 클라이언트.
- 🧪 End-to-End 테스트를 위한 [Playwright](https://playwright.dev).
- 🦇 다크 모드 지원.
- 🐋 개발 환경과 프로덕션(운영)을 위한 [Docker Compose](https://www.docker.com).
- 🔒 기본으로 지원되는 안전한 비밀번호 해싱.
- 🔑 JWT (JSON Web Token) 인증.
- 📫 이메일 기반 비밀번호 복구.
- ✅ [Pytest](https://pytest.org)를 이용한 테스트.
- 📞 리버스 프록시 / 로드 밸런서로서의 [Traefik](https://traefik.io).
- 🚢 Docker Compose를 이용한 배포 지침: 자동 HTTPS 인증서를 처리하기 위한 프론트엔드 Traefik 프록시 설정 방법을 포함.
- 🏭 GitHub Actions를 기반으로 CI (지속적인 통합) 및 CD (지속적인 배포).
================================================
FILE: docs/ko/docs/python-types.md
================================================
# 파이썬 타입 소개 { #python-types-intro }
파이썬은 선택적으로 "타입 힌트(type hints)"(“type annotations”라고도 함)를 지원합니다.
이러한 **"타입 힌트"** 또는 애너테이션은 변수의 타입을 선언할 수 있게 해주는 특수한 구문입니다.
변수의 타입을 선언하면 에디터와 도구가 더 나은 지원을 제공할 수 있습니다.
이 문서는 파이썬 타입 힌트에 대한 **빠른 자습서 / 내용 환기**입니다. **FastAPI**와 함께 사용하기 위해 필요한 최소한만 다룹니다... 실제로는 아주 조금만 있으면 됩니다.
**FastAPI**는 모두 이러한 타입 힌트에 기반을 두고 있으며, 이는 많은 장점과 이점을 제공합니다.
하지만 **FastAPI**를 전혀 사용하지 않더라도, 타입 힌트를 조금만 배워도 도움이 됩니다.
/// note | 참고
파이썬에 능숙하고 타입 힌트에 대해 이미 모두 알고 있다면, 다음 장으로 건너뛰세요.
///
## 동기 부여 { #motivation }
간단한 예제로 시작해봅시다:
{* ../../docs_src/python_types/tutorial001_py310.py *}
이 프로그램을 호출하면 다음이 출력됩니다:
```
John Doe
```
이 함수는 다음을 수행합니다:
* `first_name`과 `last_name`를 받습니다.
* `title()`로 각각의 첫 글자를 대문자로 변환합니다.
* 가운데에 공백을 두고 연결합니다.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### 수정하기 { #edit-it }
매우 간단한 프로그램입니다.
하지만 이제, 이것을 처음부터 작성한다고 상상해봅시다.
어느 시점엔 함수를 정의하기 시작했고, 매개변수도 준비해두었을 겁니다...
그런데 "첫 글자를 대문자로 변환하는 그 메서드"를 호출해야 합니다.
`upper`였나요? `uppercase`였나요? `first_uppercase`? `capitalize`?
그 다음, 개발자들의 오랜 친구인 에디터 자동완성을 시도합니다.
함수의 첫 번째 매개변수인 `first_name`을 입력하고, 점(`.`)을 찍은 다음, 완성을 트리거하기 위해 `Ctrl+Space`를 누릅니다.
하지만, 슬프게도 쓸만한 게 아무것도 없습니다:
### 타입 추가하기 { #add-types }
이전 버전에서 한 줄만 수정해봅시다.
함수의 매개변수인 정확히 이 부분을:
```Python
first_name, last_name
```
에서:
```Python
first_name: str, last_name: str
```
로 바꾸겠습니다.
이게 다입니다.
이것들이 "타입 힌트"입니다:
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
이것은 다음처럼 기본값을 선언하는 것과는 다릅니다:
```Python
first_name="john", last_name="doe"
```
다른 것입니다.
등호(`=`)가 아니라 콜론(`:`)을 사용합니다.
그리고 보통 타입 힌트를 추가해도, 타입 힌트 없이 일어나는 일과 비교해 특별히 달라지는 것은 없습니다.
하지만 이제, 타입 힌트를 포함해 그 함수를 다시 만드는 중이라고 상상해봅시다.
같은 지점에서 `Ctrl+Space`로 자동완성을 트리거하면 다음이 보입니다:
그러면 스크롤하며 옵션을 보다가, "기억나는" 것을 찾을 수 있습니다:
## 더 큰 동기부여 { #more-motivation }
이 함수를 확인해보세요. 이미 타입 힌트가 있습니다:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
에디터가 변수의 타입을 알고 있기 때문에, 자동완성만 되는 게 아니라 오류 검사도 할 수 있습니다:
이제 고쳐야 한다는 것을 알고, `age`를 `str(age)`로 문자열로 바꿉니다:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## 타입 선언 { #declaring-types }
방금 타입 힌트를 선언하는 주요 위치를 보았습니다. 함수 매개변수입니다.
이것은 **FastAPI**와 함께 사용할 때도 주요 위치입니다.
### Simple 타입 { #simple-types }
`str`뿐 아니라 모든 파이썬 표준 타입을 선언할 수 있습니다.
예를 들어 다음을 사용할 수 있습니다:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### `typing` 모듈 { #typing-module }
몇 가지 추가적인 사용 사례에서는 표준 라이브러리의 `typing` 모듈에서 무언가를 import해야 할 수 있습니다. 예를 들어 어떤 값이 "아무 타입"일 수 있다고 선언하려면, `typing`의 `Any`를 사용할 수 있습니다:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic(제네릭) 타입 { #generic-types }
일부 타입은 대괄호 안에 "타입 매개변수"를 받아 내부 타입을 정의할 수 있습니다. 예를 들어 "문자열의 리스트"는 `list[str]`로 선언합니다.
이렇게 타입 매개변수를 받을 수 있는 타입을 **Generic types** 또는 **Generics**라고 부릅니다.
대괄호와 내부 타입을 사용해 동일한 내장 타입들을 제네릭으로 사용할 수 있습니다:
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
예를 들어, `str`의 `list`인 변수를 정의해봅시다.
같은 콜론(`:`) 문법으로 변수를 선언합니다.
타입으로 `list`를 넣습니다.
`list`는 내부 타입을 포함하는 타입이므로, 그 타입들을 대괄호 안에 넣습니다:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | 정보
대괄호 안의 내부 타입은 "type parameters"라고 부릅니다.
이 경우 `str`이 `list`에 전달된 타입 매개변수입니다.
///
이는 "변수 `items`는 `list`이고, 이 `list`의 각 아이템은 `str`이다"라는 뜻입니다.
이렇게 하면, 에디터는 리스트의 아이템을 처리하는 동안에도 지원을 제공할 수 있습니다:
타입이 없으면, 이는 거의 불가능합니다.
변수 `item`이 리스트 `items`의 요소 중 하나라는 점에 주목하세요.
그리고 에디터는 여전히 이것이 `str`임을 알고, 그에 대한 지원을 제공합니다.
#### Tuple과 Set { #tuple-and-set }
`tuple`과 `set`도 동일하게 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `items_t`는 3개의 아이템을 가진 `tuple`이며, `int`, 또 다른 `int`, 그리고 `str`입니다.
* 변수 `items_s`는 `set`이며, 각 아이템의 타입은 `bytes`입니다.
#### Dict { #dict }
`dict`를 정의하려면, 쉼표로 구분된 2개의 타입 매개변수를 전달합니다.
첫 번째 타입 매개변수는 `dict`의 키를 위한 것입니다.
두 번째 타입 매개변수는 `dict`의 값을 위한 것입니다:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
이는 다음을 의미합니다:
* 변수 `prices`는 `dict`입니다:
* 이 `dict`의 키는 `str` 타입입니다(예: 각 아이템의 이름).
* 이 `dict`의 값은 `float` 타입입니다(예: 각 아이템의 가격).
#### Union { #union }
변수가 **여러 타입 중 어떤 것이든** 될 수 있다고 선언할 수 있습니다. 예를 들어 `int` 또는 `str`입니다.
이를 정의하려면 두 타입을 세로 막대(`|`)로 구분해 사용합니다.
이는 두 타입 집합의 합집합(union) 안의 어느 것이든 될 수 있다는 뜻이므로 "유니온"이라고 부릅니다.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
이는 `item`이 `int` 또는 `str`일 수 있다는 뜻입니다.
#### `None`일 수도 있음 { #possibly-none }
값이 `str` 같은 타입일 수도 있지만, `None`일 수도 있다고 선언할 수 있습니다.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
그냥 `str` 대신 `str | None`을 사용하면, 값이 항상 `str`이라고 가정하고 있지만 실제로는 `None`일 수도 있는 상황에서 에디터가 오류를 감지하도록 도와줍니다.
### 타입으로서의 클래스 { #classes-as-types }
변수의 타입으로 클래스를 선언할 수도 있습니다.
이름을 가진 `Person` 클래스가 있다고 해봅시다:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
그러면 `Person` 타입의 변수를 선언할 수 있습니다:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
그리고 다시, 에디터의 모든 지원을 받을 수 있습니다:
이는 "`one_person`은 `Person` 클래스의 **인스턴스**"라는 뜻입니다.
"`one_person`은 `Person`이라는 **클래스**다"라는 뜻이 아닙니다.
## Pydantic 모델 { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/)은 데이터 검증을 수행하는 파이썬 라이브러리입니다.
속성을 가진 클래스 형태로 데이터의 "모양(shape)"을 선언합니다.
그리고 각 속성은 타입을 가집니다.
그 다음 그 클래스의 인스턴스를 몇 가지 값으로 생성하면, 값들을 검증하고, (그런 경우라면) 적절한 타입으로 변환한 뒤, 모든 데이터를 가진 객체를 제공합니다.
그리고 그 결과 객체에 대해 에디터의 모든 지원을 받을 수 있습니다.
Pydantic 공식 문서의 예시:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | 정보
Pydantic에 대해 더 알아보려면 [문서를 확인하세요](https://docs.pydantic.dev/).
///
**FastAPI**는 모두 Pydantic에 기반을 두고 있습니다.
이 모든 것은 [자습서 - 사용자 안내서](tutorial/index.md)에서 실제로 많이 보게 될 것입니다.
## 메타데이터 애너테이션이 있는 타입 힌트 { #type-hints-with-metadata-annotations }
파이썬에는 `Annotated`를 사용해 이러한 타입 힌트에 **추가 메타데이터**를 넣을 수 있는 기능도 있습니다.
`Annotated`는 `typing`에서 import할 수 있습니다.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
파이썬 자체는 이 `Annotated`로 아무것도 하지 않습니다. 그리고 에디터와 다른 도구들에게는 타입이 여전히 `str`입니다.
하지만 `Annotated`의 이 공간을 사용해, 애플리케이션이 어떻게 동작하길 원하는지에 대한 추가 메타데이터를 **FastAPI**에 제공할 수 있습니다.
기억해야 할 중요한 점은 `Annotated`에 전달하는 **첫 번째 *타입 매개변수***가 **실제 타입**이라는 것입니다. 나머지는 다른 도구를 위한 메타데이터일 뿐입니다.
지금은 `Annotated`가 존재하며, 표준 파이썬이라는 것만 알면 됩니다. 😎
나중에 이것이 얼마나 **강력**할 수 있는지 보게 될 것입니다.
/// tip | 팁
이것이 **표준 파이썬**이라는 사실은, 에디터에서 가능한 **최고의 개발자 경험**을 계속 얻을 수 있다는 뜻이기도 합니다. 사용하는 도구로 코드를 분석하고 리팩터링하는 등에서도요. ✨
또한 코드가 많은 다른 파이썬 도구 및 라이브러리와 매우 호환된다는 뜻이기도 합니다. 🚀
///
## **FastAPI**에서의 타입 힌트 { #type-hints-in-fastapi }
**FastAPI**는 이러한 타입 힌트를 활용해 여러 가지를 합니다.
**FastAPI**에서는 타입 힌트로 매개변수를 선언하면 다음을 얻습니다:
* **에디터 도움**.
* **타입 확인**.
...그리고 **FastAPI**는 같은 선언을 다음에도 사용합니다:
* **요구사항 정의**: 요청 경로 매개변수, 쿼리 매개변수, 헤더, 바디, 의존성 등에서.
* **데이터 변환**: 요청에서 필요한 타입으로.
* **데이터 검증**: 각 요청에서:
* 데이터가 유효하지 않을 때 클라이언트에 반환되는 **자동 오류**를 생성합니다.
* OpenAPI를 사용해 API를 **문서화**:
* 자동 상호작용 문서 UI에서 사용됩니다.
이 모든 것이 다소 추상적으로 들릴 수도 있습니다. 걱정하지 마세요. [자습서 - 사용자 안내서](tutorial/index.md)에서 실제로 확인하게 될 것입니다.
가장 중요한 점은 표준 파이썬 타입을 한 곳에서 사용함으로써(더 많은 클래스, 데코레이터 등을 추가하는 대신) **FastAPI**가 여러분을 위해 많은 일을 해준다는 사실입니다.
/// info | 정보
자습서를 모두 끝내고 타입에 대해 더 알아보기 위해 다시 돌아왔다면, 좋은 자료로 [`mypy`의 "cheat sheet"](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html)가 있습니다.
///
================================================
FILE: docs/ko/docs/resources/index.md
================================================
# 리소스 { #resources }
추가 리소스, 외부 링크, 그리고 더 많은 자료. ✈️
================================================
FILE: docs/ko/docs/translation-banner.md
================================================
/// details | 🌐 AI와 사람이 함께한 번역
이 번역은 사람의 안내를 받아 AI가 만들었습니다. 🤝
원문의 의미를 오해하거나 부자연스러워 보이는 등 오류가 있을 수 있습니다. 🤖
[AI LLM을 더 잘 안내하는 데 도움을 주세요](https://fastapi.tiangolo.com/ko/contributing/#translations).
[영문 버전](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/ko/docs/tutorial/background-tasks.md
================================================
# 백그라운드 작업 { #background-tasks }
FastAPI에서는 응답을 반환한 *후에* 실행할 백그라운드 작업을 정의할 수 있습니다.
백그라운드 작업은 요청 후에 발생해야 하지만, 클라이언트가 응답을 받기 전에 작업이 완료될 때까지 기다릴 필요가 없는 작업에 유용합니다.
예를 들면 다음과 같습니다.
* 작업을 수행한 후 전송되는 이메일 알림:
* 이메일 서버에 연결하고 이메일을 전송하는 것은 (몇 초 정도) "느린" 경향이 있으므로, 응답은 즉시 반환하고 이메일 알림은 백그라운드에서 전송할 수 있습니다.
* 데이터 처리:
* 예를 들어 처리에 오랜 시간이 걸리는 프로세스를 거쳐야 하는 파일을 받았다면, "Accepted"(HTTP 202) 응답을 반환하고 백그라운드에서 파일을 처리할 수 있습니다.
## `BackgroundTasks` 사용 { #using-backgroundtasks }
먼저 `BackgroundTasks`를 임포트하고, `BackgroundTasks` 타입 선언으로 *경로 처리 함수*에 매개변수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI**가 `BackgroundTasks` 타입의 객체를 생성하고 해당 매개변수로 전달합니다.
## 작업 함수 생성 { #create-a-task-function }
백그라운드 작업으로 실행할 함수를 생성합니다.
이는 매개변수를 받을 수 있는 표준 함수일 뿐입니다.
`async def` 함수일 수도, 일반 `def` 함수일 수도 있으며, **FastAPI**가 이를 올바르게 처리하는 방법을 알고 있습니다.
이 경우 작업 함수는 파일에 쓰기를 수행합니다(이메일 전송을 시뮬레이션).
그리고 쓰기 작업은 `async`와 `await`를 사용하지 않으므로, 일반 `def`로 함수를 정의합니다:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## 백그라운드 작업 추가 { #add-the-background-task }
*경로 처리 함수* 내부에서 `.add_task()` 메서드로 작업 함수를 *백그라운드 작업* 객체에 전달합니다:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()`는 다음 인자를 받습니다:
* 백그라운드에서 실행될 작업 함수(`write_notification`).
* 작업 함수에 순서대로 전달되어야 하는 인자 시퀀스(`email`).
* 작업 함수에 전달되어야 하는 키워드 인자(`message="some notification"`).
## 의존성 주입 { #dependency-injection }
`BackgroundTasks`는 의존성 주입 시스템에서도 동작하며, *경로 처리 함수*, 의존성(dependable), 하위 의존성 등 여러 수준에서 `BackgroundTasks` 타입의 매개변수를 선언할 수 있습니다.
**FastAPI**는 각 경우에 무엇을 해야 하는지와 동일한 객체를 어떻게 재사용해야 하는지를 알고 있으므로, 모든 백그라운드 작업이 함께 병합되어 이후 백그라운드에서 실행됩니다:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
이 예제에서는 응답이 전송된 *후에* 메시지가 `log.txt` 파일에 작성됩니다.
요청에 쿼리가 있었다면, 백그라운드 작업으로 로그에 작성됩니다.
그 다음 *경로 처리 함수*에서 생성된 또 다른 백그라운드 작업이 `email` 경로 매개변수를 사용해 메시지를 작성합니다.
## 기술적 세부사항 { #technical-details }
`BackgroundTasks` 클래스는 [`starlette.background`](https://www.starlette.dev/background/)에서 직접 가져옵니다.
FastAPI에 직접 임포트/포함되어 있으므로 `fastapi`에서 임포트할 수 있고, 실수로 `starlette.background`에서 대안인 `BackgroundTask`(끝에 `s`가 없음)를 임포트하는 것을 피할 수 있습니다.
`BackgroundTask`가 아닌 `BackgroundTasks`만 사용하면, 이를 *경로 처리 함수*의 매개변수로 사용할 수 있고 나머지는 **FastAPI**가 `Request` 객체를 직접 사용할 때처럼 대신 처리해 줍니다.
FastAPI에서 `BackgroundTask`만 단독으로 사용하는 것도 가능하지만, 코드에서 객체를 생성하고 이를 포함하는 Starlette `Response`를 반환해야 합니다.
더 자세한 내용은 [Starlette의 Background Tasks 공식 문서](https://www.starlette.dev/background/)에서 확인할 수 있습니다.
## 주의사항 { #caveat }
무거운 백그라운드 계산을 수행해야 하고, 반드시 동일한 프로세스에서 실행할 필요가 없다면(예: 메모리, 변수 등을 공유할 필요가 없음) [Celery](https://docs.celeryq.dev) 같은 더 큰 도구를 사용하는 것이 도움이 될 수 있습니다.
이들은 RabbitMQ나 Redis 같은 메시지/작업 큐 관리자 등 더 복잡한 설정을 필요로 하는 경향이 있지만, 여러 프로세스에서, 특히 여러 서버에서 백그라운드 작업을 실행할 수 있습니다.
하지만 동일한 **FastAPI** 앱의 변수와 객체에 접근해야 하거나, 작은 백그라운드 작업(예: 이메일 알림 전송)을 수행해야 한다면, `BackgroundTasks`를 간단히 사용하면 됩니다.
## 요약 { #recap }
*경로 처리 함수*와 의존성에서 매개변수로 `BackgroundTasks`를 임포트해 사용하여 백그라운드 작업을 추가합니다.
================================================
FILE: docs/ko/docs/tutorial/bigger-applications.md
================================================
# 더 큰 애플리케이션 - 여러 파일 { #bigger-applications-multiple-files }
애플리케이션이나 웹 API를 만들 때, 모든 것을 하나의 파일에 담을 수 있는 경우는 드뭅니다.
**FastAPI**는 모든 유연성을 유지하면서도 애플리케이션을 구조화할 수 있게 해주는 편리한 도구를 제공합니다.
/// info | 정보
Flask를 사용해 보셨다면, 이는 Flask의 Blueprints에 해당하는 개념입니다.
///
## 예시 파일 구조 { #an-example-file-structure }
다음과 같은 파일 구조가 있다고 해봅시다:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | 팁
`__init__.py` 파일이 여러 개 있습니다: 각 디렉터리 또는 하위 디렉터리에 하나씩 있습니다.
이 파일들이 한 파일의 코드를 다른 파일로 import할 수 있게 해줍니다.
예를 들어 `app/main.py`에는 다음과 같은 줄이 있을 수 있습니다:
```
from app.routers import items
```
///
* `app` 디렉터리에는 모든 것이 들어 있습니다. 그리고 비어 있는 파일 `app/__init__.py`가 있어 "Python package"(“Python modules”의 모음)인 `app`이 됩니다.
* `app/main.py` 파일이 있습니다. Python package(`__init__.py` 파일이 있는 디렉터리) 안에 있으므로, 이 package의 "module"입니다: `app.main`.
* `app/dependencies.py` 파일도 있습니다. `app/main.py`와 마찬가지로 "module"입니다: `app.dependencies`.
* `app/routers/` 하위 디렉터리가 있고, 여기에 또 `__init__.py` 파일이 있으므로 "Python subpackage"입니다: `app.routers`.
* `app/routers/items.py` 파일은 `app/routers/` package 안에 있으므로, submodule입니다: `app.routers.items`.
* `app/routers/users.py`도 동일하게 또 다른 submodule입니다: `app.routers.users`.
* `app/internal/` 하위 디렉터리도 있고 여기에 `__init__.py`가 있으므로 또 다른 "Python subpackage"입니다: `app.internal`.
* 그리고 `app/internal/admin.py` 파일은 또 다른 submodule입니다: `app.internal.admin`.
같은 파일 구조에 주석을 추가하면 다음과 같습니다:
```bash
.
├── app # 'app'은 Python 패키지입니다
│ ├── __init__.py # 이 파일로 'app'이 'Python 패키지'가 됩니다
│ ├── main.py # 'main' 모듈, 예: import app.main
│ ├── dependencies.py # 'dependencies' 모듈, 예: import app.dependencies
│ └── routers # 'routers'는 'Python 하위 패키지'입니다
│ │ ├── __init__.py # 이 파일로 'routers'가 'Python 하위 패키지'가 됩니다
│ │ ├── items.py # 'items' 서브모듈, 예: import app.routers.items
│ │ └── users.py # 'users' 서브모듈, 예: import app.routers.users
│ └── internal # 'internal'은 'Python 하위 패키지'입니다
│ ├── __init__.py # 이 파일로 'internal'이 'Python 하위 패키지'가 됩니다
│ └── admin.py # 'admin' 서브모듈, 예: import app.internal.admin
```
## `APIRouter` { #apirouter }
사용자만 처리하는 전용 파일이 `/app/routers/users.py`의 submodule이라고 해봅시다.
코드를 정리하기 위해 사용자와 관련된 *path operations*를 나머지 코드와 분리해 두고 싶을 것입니다.
하지만 이것은 여전히 같은 **FastAPI** 애플리케이션/웹 API의 일부입니다(같은 "Python Package"의 일부입니다).
`APIRouter`를 사용해 해당 모듈의 *path operations*를 만들 수 있습니다.
### `APIRouter` import하기 { #import-apirouter }
`FastAPI` 클래스와 동일한 방식으로 import하고 "instance"를 생성합니다:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### `APIRouter`로 *path operations* 만들기 { #path-operations-with-apirouter }
그 다음 이를 사용해 *path operations*를 선언합니다.
`FastAPI` 클래스를 사용할 때와 동일한 방식으로 사용합니다:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
`APIRouter`는 "미니 `FastAPI`" 클래스라고 생각할 수 있습니다.
동일한 옵션들이 모두 지원됩니다.
동일한 `parameters`, `responses`, `dependencies`, `tags` 등등.
/// tip | 팁
이 예시에서는 변수 이름이 `router`이지만, 원하는 이름으로 지어도 됩니다.
///
이제 이 `APIRouter`를 메인 `FastAPI` 앱에 포함(include)할 것이지만, 먼저 dependencies와 다른 `APIRouter` 하나를 확인해 보겠습니다.
## Dependencies { #dependencies }
애플리케이션의 여러 위치에서 사용되는 dependencies가 일부 필요하다는 것을 알 수 있습니다.
그래서 이를 별도의 `dependencies` 모듈(`app/dependencies.py`)에 둡니다.
이제 간단한 dependency를 사용해 커스텀 `X-Token` 헤더를 읽어 보겠습니다:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | 팁
이 예시를 단순화하기 위해 임의로 만든 헤더를 사용하고 있습니다.
하지만 실제 상황에서는 통합된 [Security 유틸리티](security/index.md)를 사용하는 것이 더 좋은 결과를 얻을 수 있습니다.
///
## `APIRouter`가 있는 또 다른 모듈 { #another-module-with-apirouter }
애플리케이션의 "items"를 처리하는 전용 endpoint들도 `app/routers/items.py` 모듈에 있다고 해봅시다.
여기에는 다음에 대한 *path operations*가 있습니다:
* `/items/`
* `/items/{item_id}`
구조는 `app/routers/users.py`와 완전히 동일합니다.
하지만 우리는 조금 더 똑똑하게, 코드를 약간 단순화하고 싶습니다.
이 모듈의 모든 *path operations*에는 다음이 동일하게 적용됩니다:
* 경로 `prefix`: `/items`.
* `tags`: (태그 하나: `items`).
* 추가 `responses`.
* `dependencies`: 모두 우리가 만든 `X-Token` dependency가 필요합니다.
따라서 각 *path operation*마다 매번 모두 추가하는 대신, `APIRouter`에 한 번에 추가할 수 있습니다.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
각 *path operation*의 경로는 다음처럼 `/`로 시작해야 하므로:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...prefix에는 마지막 `/`가 포함되면 안 됩니다.
따라서 이 경우 prefix는 `/items`입니다.
또한 이 router에 포함된 모든 *path operations*에 적용될 `tags` 목록과 추가 `responses`도 넣을 수 있습니다.
그리고 router의 모든 *path operations*에 추가될 `dependencies` 목록도 추가할 수 있으며, 해당 경로들로 들어오는 각 요청마다 실행/해결됩니다.
/// tip | 팁
[*path operation decorator의 dependencies*](dependencies/dependencies-in-path-operation-decorators.md)와 마찬가지로, *path operation function*에 어떤 값도 전달되지 않습니다.
///
최종적으로 item 경로는 다음과 같습니다:
* `/items/`
* `/items/{item_id}`
...의도한 그대로입니다.
* 단일 문자열 `"items"`를 포함하는 태그 목록으로 표시됩니다.
* 이 "tags"는 자동 대화형 문서 시스템(OpenAPI 사용)에 특히 유용합니다.
* 모두 미리 정의된 `responses`를 포함합니다.
* 이 모든 *path operations*는 실행되기 전에 `dependencies` 목록이 평가/실행됩니다.
* 특정 *path operation*에 dependencies를 추가로 선언하면 **그것들도 실행됩니다**.
* router dependencies가 먼저 실행되고, 그 다음에 [decorator의 `dependencies`](dependencies/dependencies-in-path-operation-decorators.md), 그리고 일반 파라미터 dependencies가 실행됩니다.
* [`scopes`가 있는 `Security` dependencies](../advanced/security/oauth2-scopes.md)도 추가할 수 있습니다.
/// tip | 팁
`APIRouter`에 `dependencies`를 두는 것은 예를 들어 전체 *path operations* 그룹에 인증을 요구할 때 사용할 수 있습니다. 각 경로 처리에 개별적으로 dependencies를 추가하지 않아도 됩니다.
///
/// check | 확인
`prefix`, `tags`, `responses`, `dependencies` 파라미터는 (다른 많은 경우와 마찬가지로) 코드 중복을 피하도록 도와주는 **FastAPI**의 기능입니다.
///
### dependencies import하기 { #import-the-dependencies }
이 코드는 모듈 `app.routers.items`, 파일 `app/routers/items.py`에 있습니다.
그리고 dependency 함수는 모듈 `app.dependencies`, 파일 `app/dependencies.py`에서 가져와야 합니다.
그래서 dependencies에 대해 `..`를 사용하는 상대 import를 사용합니다:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### 상대 import가 동작하는 방식 { #how-relative-imports-work }
/// tip | 팁
import가 동작하는 방식을 완벽히 알고 있다면, 아래 다음 섹션으로 넘어가세요.
///
다음과 같이 점 하나 `.`를 쓰면:
```Python
from .dependencies import get_token_header
```
의미는 다음과 같습니다:
* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서...
* `dependencies` 모듈(가상의 파일 `app/routers/dependencies.py`)을 찾고...
* 그 안에서 함수 `get_token_header`를 import합니다.
하지만 그 파일은 존재하지 않습니다. dependencies는 `app/dependencies.py` 파일에 있습니다.
우리 앱/파일 구조를 다시 떠올려 보세요:
---
다음처럼 점 두 개 `..`를 쓰면:
```Python
from ..dependencies import get_token_header
```
의미는 다음과 같습니다:
* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서...
* 상위 package(디렉터리 `app/`)로 올라가고...
* 그 안에서 `dependencies` 모듈(파일 `app/dependencies.py`)을 찾고...
* 그 안에서 함수 `get_token_header`를 import합니다.
이렇게 하면 제대로 동작합니다! 🎉
---
같은 방식으로 점 세 개 `...`를 사용했다면:
```Python
from ...dependencies import get_token_header
```
의미는 다음과 같습니다:
* 이 모듈(파일 `app/routers/items.py`)이 속한 같은 package(디렉터리 `app/routers/`)에서 시작해서...
* 상위 package(디렉터리 `app/`)로 올라가고...
* 그 package의 상위로 또 올라가는데(상위 package가 없습니다, `app`이 최상위입니다 😱)...
* 그 안에서 `dependencies` 모듈(파일 `app/dependencies.py`)을 찾고...
* 그 안에서 함수 `get_token_header`를 import합니다.
이는 `app/` 위쪽의 어떤 package(자신의 `__init__.py` 파일 등을 가진)에 대한 참조가 됩니다. 하지만 우리는 그런 것이 없습니다. 그래서 이 예시에서는 에러가 발생합니다. 🚨
이제 어떻게 동작하는지 알았으니, 앱이 얼마나 복잡하든 상대 import를 사용할 수 있습니다. 🤓
### 커스텀 `tags`, `responses`, `dependencies` 추가하기 { #add-some-custom-tags-responses-and-dependencies }
`APIRouter`에 이미 prefix `/items`와 `tags=["items"]`를 추가했기 때문에 각 *path operation*에 이를 추가하지 않습니다.
하지만 특정 *path operation*에만 적용될 _추가_ `tags`를 더할 수도 있고, 그 *path operation* 전용의 추가 `responses`도 넣을 수 있습니다:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | 팁
이 마지막 경로 처리는 `["items", "custom"]` 태그 조합을 갖게 됩니다.
그리고 문서에는 `404`용 응답과 `403`용 응답, 두 가지 모두가 표시됩니다.
///
## 메인 `FastAPI` { #the-main-fastapi }
이제 `app/main.py` 모듈을 봅시다.
여기에서 `FastAPI` 클래스를 import하고 사용합니다.
이 파일은 모든 것을 하나로 엮는 애플리케이션의 메인 파일이 될 것입니다.
그리고 대부분의 로직이 각자의 특정 모듈로 분리되어 있으므로, 메인 파일은 꽤 단순해집니다.
### `FastAPI` import하기 { #import-fastapi }
평소처럼 `FastAPI` 클래스를 import하고 생성합니다.
또한 각 `APIRouter`의 dependencies와 결합될 [global dependencies](dependencies/global-dependencies.md)도 선언할 수 있습니다:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### `APIRouter` import하기 { #import-the-apirouter }
이제 `APIRouter`가 있는 다른 submodule들을 import합니다:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
`app/routers/users.py`와 `app/routers/items.py` 파일은 같은 Python package `app`에 속한 submodule들이므로, 점 하나 `.`를 사용해 "상대 import"로 가져올 수 있습니다.
### import가 동작하는 방식 { #how-the-importing-works }
다음 구문은:
```Python
from .routers import items, users
```
의미는 다음과 같습니다:
* 이 모듈(파일 `app/main.py`)이 속한 같은 package(디렉터리 `app/`)에서 시작해서...
* subpackage `routers`(디렉터리 `app/routers/`)를 찾고...
* 그 안에서 submodule `items`(파일 `app/routers/items.py`)와 `users`(파일 `app/routers/users.py`)를 import합니다...
`items` 모듈에는 `router` 변수(`items.router`)가 있습니다. 이는 `app/routers/items.py` 파일에서 만든 것과 동일하며 `APIRouter` 객체입니다.
그리고 `users` 모듈도 같은 방식입니다.
다음처럼 import할 수도 있습니다:
```Python
from app.routers import items, users
```
/// info | 정보
첫 번째 버전은 "상대 import"입니다:
```Python
from .routers import items, users
```
두 번째 버전은 "절대 import"입니다:
```Python
from app.routers import items, users
```
Python Packages와 Modules에 대해 더 알아보려면 [Modules에 대한 Python 공식 문서](https://docs.python.org/3/tutorial/modules.html)를 읽어보세요.
///
### 이름 충돌 피하기 { #avoid-name-collisions }
submodule `items`를 직접 import하고, 그 안의 `router` 변수만 import하지는 않습니다.
이는 submodule `users`에도 `router`라는 이름의 변수가 있기 때문입니다.
만약 다음처럼 순서대로 import했다면:
```Python
from .routers.items import router
from .routers.users import router
```
`users`의 `router`가 `items`의 `router`를 덮어써서 동시에 사용할 수 없게 됩니다.
따라서 같은 파일에서 둘 다 사용할 수 있도록 submodule들을 직접 import합니다:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### `users`와 `items`용 `APIRouter` 포함하기 { #include-the-apirouters-for-users-and-items }
이제 submodule `users`와 `items`의 `router`를 포함해 봅시다:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | 정보
`users.router`는 `app/routers/users.py` 파일 안의 `APIRouter`를 담고 있습니다.
`items.router`는 `app/routers/items.py` 파일 안의 `APIRouter`를 담고 있습니다.
///
`app.include_router()`로 각 `APIRouter`를 메인 `FastAPI` 애플리케이션에 추가할 수 있습니다.
그 router의 모든 route가 애플리케이션의 일부로 포함됩니다.
/// note | 기술 세부사항
내부적으로는 `APIRouter`에 선언된 각 *path operation*마다 *path operation*을 실제로 생성합니다.
즉, 내부적으로는 모든 것이 동일한 하나의 앱인 것처럼 동작합니다.
///
/// check | 확인
router를 포함(include)할 때 성능을 걱정할 필요는 없습니다.
이 작업은 마이크로초 단위이며 시작 시에만 발생합니다.
따라서 성능에 영향을 주지 않습니다. ⚡
///
### 커스텀 `prefix`, `tags`, `responses`, `dependencies`로 `APIRouter` 포함하기 { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
이제 조직에서 `app/internal/admin.py` 파일을 받았다고 가정해 봅시다.
여기에는 조직에서 여러 프로젝트 간에 공유하는 관리자용 *path operations*가 있는 `APIRouter`가 들어 있습니다.
이 예시에서는 매우 단순하게 만들겠습니다. 하지만 조직 내 다른 프로젝트와 공유되기 때문에, 이를 수정할 수 없어 `prefix`, `dependencies`, `tags` 등을 `APIRouter`에 직접 추가할 수 없다고 해봅시다:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
하지만 `APIRouter`를 포함할 때 커스텀 `prefix`를 지정해 모든 *path operations*가 `/admin`으로 시작하게 하고, 이 프로젝트에서 이미 가진 `dependencies`로 보호하고, `tags`와 `responses`도 포함하고 싶습니다.
원래 `APIRouter`를 수정하지 않고도 `app.include_router()`에 파라미터를 전달해서 이를 선언할 수 있습니다:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
이렇게 하면 원래 `APIRouter`는 수정되지 않으므로, 조직 내 다른 프로젝트에서도 동일한 `app/internal/admin.py` 파일을 계속 공유할 수 있습니다.
결과적으로 우리 앱에서 `admin` 모듈의 각 *path operations*는 다음을 갖게 됩니다:
* prefix `/admin`.
* tag `admin`.
* dependency `get_token_header`.
* 응답 `418`. 🍵
하지만 이는 우리 앱에서 그 `APIRouter`에만 영향을 주며, 이를 사용하는 다른 코드에는 영향을 주지 않습니다.
따라서 다른 프로젝트들은 같은 `APIRouter`를 다른 인증 방식으로 사용할 수도 있습니다.
### *path operation* 포함하기 { #include-a-path-operation }
*path operations*를 `FastAPI` 앱에 직접 추가할 수도 있습니다.
여기서는 가능하다는 것을 보여주기 위해... 그냥 해봅니다 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
그리고 `app.include_router()`로 추가한 다른 모든 *path operations*와 함께 올바르게 동작합니다.
/// info | 매우 기술적인 세부사항
**참고**: 이는 매우 기술적인 세부사항이라 아마 **그냥 건너뛰어도 됩니다**.
---
`APIRouter`는 "mount"되는 것이 아니며, 애플리케이션의 나머지 부분과 격리되어 있지 않습니다.
이는 OpenAPI 스키마와 사용자 인터페이스에 그들의 *path operations*를 포함시키고 싶기 때문입니다.
나머지와 독립적으로 격리해 "mount"할 수 없으므로, *path operations*는 직접 포함되는 것이 아니라 "clone"(재생성)됩니다.
///
## `pyproject.toml`에서 `entrypoint` 구성하기 { #configure-the-entrypoint-in-pyproject-toml }
FastAPI `app` 객체가 `app/main.py`에 있으므로 `pyproject.toml` 파일에서 `entrypoint`를 다음과 같이 구성할 수 있습니다:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
이는 다음과 같이 import하는 것과 동일합니다:
```python
from app.main import app
```
이렇게 하면 `fastapi` 명령어가 여러분의 앱이 어디에 있는지 알 수 있습니다.
/// Note | 참고
명령어에 경로를 직접 전달할 수도 있습니다:
```console
$ fastapi dev app/main.py
```
하지만 `fastapi` 명령어를 실행할 때마다 올바른 경로를 기억해 전달해야 합니다.
또한 [VS Code 확장](../editor-support.md)이나 [FastAPI Cloud](https://fastapicloud.com) 같은 다른 도구들이 이를 찾지 못할 수도 있으므로, `pyproject.toml`의 `entrypoint`를 사용하는 것을 권장합니다.
///
## 자동 API 문서 확인하기 { #check-the-automatic-api-docs }
이제 앱을 실행하세요:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
그리고 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)에서 문서를 여세요.
올바른 경로(및 prefix)와 올바른 태그를 사용해, 모든 submodule의 경로를 포함한 자동 API 문서를 볼 수 있습니다:
## 같은 router를 다른 `prefix`로 여러 번 포함하기 { #include-the-same-router-multiple-times-with-different-prefix }
`.include_router()`를 사용해 *같은* router를 서로 다른 prefix로 여러 번 포함할 수도 있습니다.
예를 들어 `/api/v1`과 `/api/latest`처럼 서로 다른 prefix로 동일한 API를 노출할 때 유용할 수 있습니다.
이는 고급 사용 방식이라 실제로 필요하지 않을 수도 있지만, 필요할 때를 위해 제공됩니다.
## `APIRouter`에 다른 `APIRouter` 포함하기 { #include-an-apirouter-in-another }
`APIRouter`를 `FastAPI` 애플리케이션에 포함할 수 있는 것과 같은 방식으로, 다음을 사용해 `APIRouter`를 다른 `APIRouter`에 포함할 수 있습니다:
```Python
router.include_router(other_router)
```
`FastAPI` 앱에 `router`를 포함하기 전에 수행해야 하며, 그래야 `other_router`의 *path operations*도 함께 포함됩니다.
================================================
FILE: docs/ko/docs/tutorial/body-fields.md
================================================
# 본문 - 필드 { #body-fields }
`Query`, `Path`와 `Body`를 사용해 *경로 처리 함수* 매개변수 내에서 추가적인 검증이나 메타데이터를 선언한 것처럼 Pydantic의 `Field`를 사용하여 모델 내에서 검증과 메타데이터를 선언할 수 있습니다.
## `Field` 임포트 { #import-field }
먼저 이를 임포트해야 합니다:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | 경고
`Field`는 다른 것들처럼 (`Query`, `Path`, `Body` 등) `fastapi`에서가 아닌 `pydantic`에서 바로 임포트 되는 점에 주의하세요.
///
## 모델 어트리뷰트 선언 { #declare-model-attributes }
그 다음 모델 어트리뷰트와 함께 `Field`를 사용할 수 있습니다:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field`는 `Query`, `Path`와 `Body`와 같은 방식으로 동작하며, 모두 같은 매개변수들 등을 가집니다.
/// note | 기술 세부사항
실제로 `Query`, `Path`등, 여러분이 앞으로 볼 다른 것들은 공통 클래스인 `Param` 클래스의 서브클래스 객체를 만드는데, 그 자체로 Pydantic의 `FieldInfo` 클래스의 서브클래스입니다.
그리고 Pydantic의 `Field` 또한 `FieldInfo`의 인스턴스를 반환합니다.
`Body` 또한 `FieldInfo`의 서브클래스 객체를 직접적으로 반환합니다. 그리고 `Body` 클래스의 서브클래스인 것들도 여러분이 나중에 보게될 것입니다.
`Query`, `Path`와 그 외 것들을 `fastapi`에서 임포트할 때, 이는 실제로 특별한 클래스를 반환하는 함수인 것을 기억해 주세요.
///
/// tip | 팁
주목할 점은 타입, 기본 값 및 `Field`로 이루어진 각 모델 어트리뷰트가 `Path`, `Query`와 `Body`대신 `Field`를 사용하는 *경로 처리 함수*의 매개변수와 같은 구조를 가진다는 점 입니다.
///
## 별도 정보 추가 { #add-extra-information }
`Field`, `Query`, `Body`, 그 외 안에 별도 정보를 선언할 수 있습니다. 이는 생성된 JSON 스키마에 포함됩니다.
여러분이 예제를 선언할 때 나중에 이 공식 문서에서 별도 정보를 추가하는 방법을 배울 것입니다.
/// warning | 경고
별도 키가 전달된 `Field` 또한 여러분의 애플리케이션의 OpenAPI 스키마에 나타날 것입니다.
이런 키가 OpenAPI 명세서의 일부가 아닐 수도 있으므로, [OpenAPI validator](https://validator.swagger.io/) 같은 몇몇 OpenAPI 도구들은 여러분이 생성한 스키마와 호환되지 않을 수 있습니다.
///
## 요약 { #recap }
모델 어트리뷰트를 위한 추가 검증과 메타데이터 선언하기 위해 Pydantic의 `Field` 를 사용할 수 있습니다.
또한 추가적인 JSON 스키마 메타데이터를 전달하기 위한 별도의 키워드 인자를 사용할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/body-multiple-params.md
================================================
# 본문 - 다중 매개변수 { #body-multiple-parameters }
이제 `Path`와 `Query`를 어떻게 사용하는지 확인했으니, 요청 본문 선언에 대한 더 고급 사용법을 살펴보겠습니다.
## `Path`, `Query` 및 본문 매개변수 혼합 { #mix-path-query-and-body-parameters }
먼저, 물론 `Path`, `Query` 및 요청 본문 매개변수 선언을 자유롭게 혼합해서 사용할 수 있고, **FastAPI**는 어떤 동작을 할지 압니다.
또한 기본 값을 `None`으로 설정해 본문 매개변수를 선택사항으로 선언할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | 참고
이 경우에는 본문에서 가져올 `item`이 선택사항이라는 점을 유의하세요. 기본값이 `None`이기 때문입니다.
///
## 다중 본문 매개변수 { #multiple-body-parameters }
이전 예제에서, *경로 처리*는 아래처럼 `Item`의 속성을 가진 JSON 본문을 예상합니다:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
하지만, 다중 본문 매개변수 역시 선언할 수 있습니다. 예. `item`과 `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
이 경우에, **FastAPI**는 이 함수에 본문 매개변수가 1개보다 많다는 것을 알아챌 것입니다(두 매개변수가 Pydantic 모델입니다).
그래서, 본문에서 매개변수 이름을 키(필드 이름)로 사용하고, 다음과 같은 본문을 예상합니다:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | 참고
`item`이 이전과 같은 방식으로 선언되었더라도, 이제는 본문에서 `item` 키 안에 있을 것으로 예상된다는 점을 유의하세요.
///
**FastAPI**는 요청에서 자동으로 변환을 수행하여, 매개변수 `item`이 해당하는 내용을 받고 `user`도 마찬가지로 받도록 합니다.
복합 데이터의 검증을 수행하고, OpenAPI 스키마 및 자동 문서에도 그에 맞게 문서화합니다.
## 본문 내의 단일 값 { #singular-values-in-body }
쿼리 및 경로 매개변수에 대한 추가 데이터를 정의하는 `Query`와 `Path`가 있는 것과 같은 방식으로, **FastAPI**는 동등한 `Body`를 제공합니다.
예를 들어 이전 모델을 확장해서, `item`과 `user` 외에도 같은 본문에 `importance`라는 다른 키를 두고 싶을 수 있습니다.
단일 값이므로 그대로 선언하면, **FastAPI**는 이를 쿼리 매개변수라고 가정할 것입니다.
하지만 `Body`를 사용하여 다른 본문 키로 처리하도록 **FastAPI**에 지시할 수 있습니다:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
이 경우에는 **FastAPI**가 다음과 같은 본문을 예상할 것입니다:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
다시 말해, 데이터 타입을 변환하고, 검증하고, 문서화하는 등의 작업을 수행합니다.
## 다중 본문 매개변수와 쿼리 { #multiple-body-params-and-query }
물론, 필요할 때마다 어떤 본문 매개변수에 추가로 쿼리 매개변수도 선언할 수 있습니다.
기본적으로 단일 값은 쿼리 매개변수로 해석되므로, 명시적으로 `Query`를 추가할 필요 없이 이렇게 하면 됩니다:
```Python
q: str | None = None
```
예를 들어:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | 정보
`Body` 또한 `Query`, `Path` 그리고 이후에 볼 다른 것들과 마찬가지로 동일한 추가 검증과 메타데이터 매개변수를 모두 갖고 있습니다.
///
## 단일 본문 매개변수 삽입하기 { #embed-a-single-body-parameter }
Pydantic 모델 `Item`에서 가져온 단일 `item` 본문 매개변수만 있다고 하겠습니다.
기본적으로 **FastAPI**는 그 본문을 직접 예상합니다.
하지만 추가 본문 매개변수를 선언할 때처럼, `item` 키를 가지고 그 안에 모델 내용이 들어 있는 JSON을 예상하게 하려면, `Body`의 특별한 매개변수 `embed`를 사용할 수 있습니다:
```Python
item: Item = Body(embed=True)
```
다음과 같이요:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
이 경우 **FastAPI**는 다음과 같은 본문을 예상합니다:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
다음 대신에:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## 정리 { #recap }
요청은 본문을 하나만 가질 수 있지만, *경로 처리 함수*에 다중 본문 매개변수를 추가할 수 있습니다.
하지만 **FastAPI**는 이를 처리하고, 함수에 올바른 데이터를 제공하며, *경로 처리*에서 올바른 스키마를 검증하고 문서화합니다.
또한 단일 값을 본문의 일부로 받도록 선언할 수 있습니다.
그리고 단 하나의 매개변수만 선언되어 있더라도, **FastAPI**에 본문을 키 안에 삽입하도록 지시할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/body-nested-models.md
================================================
# 본문 - 중첩 모델 { #body-nested-models }
**FastAPI**를 사용하면 (Pydantic 덕분에) 임의로 깊게 중첩된 모델을 정의, 검증, 문서화하고 사용할 수 있습니다.
## 리스트 필드 { #list-fields }
어트리뷰트를 서브타입으로 정의할 수 있습니다. 예를 들어 파이썬 `list`는:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
이는 `tags`를 리스트로 만들지만, 리스트 요소의 타입을 선언하지는 않습니다.
## 타입 매개변수가 있는 리스트 필드 { #list-fields-with-type-parameter }
하지만 파이썬에는 내부 타입, 즉 "타입 매개변수"를 사용해 리스트를 선언하는 특정한 방법이 있습니다:
### 타입 매개변수로 `list` 선언 { #declare-a-list-with-a-type-parameter }
`list`, `dict`, `tuple`처럼 타입 매개변수(내부 타입)를 갖는 타입을 선언하려면,
대괄호 `[` 및 `]`를 사용해 내부 타입(들)을 "타입 매개변수"로 전달하세요.
```Python
my_list: list[str]
```
이 모든 것은 타입 선언을 위한 표준 파이썬 문법입니다.
내부 타입을 갖는 모델 어트리뷰트에 대해 동일한 표준 문법을 사용하세요.
마찬가지로 예제에서 `tags`를 구체적으로 "문자열의 리스트"로 만들 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## 집합 타입 { #set-types }
그런데 생각해보니 태그는 반복되면 안 되고, 아마 고유한 문자열이어야 할 것입니다.
그리고 파이썬에는 고유한 항목들의 집합을 위한 특별한 데이터 타입 `set`이 있습니다.
그렇다면 `tags`를 문자열의 집합으로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
이렇게 하면 중복 데이터가 있는 요청을 받더라도 고유한 항목들의 집합으로 변환됩니다.
그리고 해당 데이터를 출력할 때마다, 소스에 중복이 있더라도 고유한 항목들의 집합으로 출력됩니다.
또한 그에 따라 주석이 생기고 문서화됩니다.
## 중첩 모델 { #nested-models }
Pydantic 모델의 각 어트리뷰트는 타입을 갖습니다.
그런데 그 타입 자체가 또 다른 Pydantic 모델일 수 있습니다.
따라서 특정한 어트리뷰트 이름, 타입, 검증을 사용하여 깊게 중첩된 JSON "객체"를 선언할 수 있습니다.
모든 것이 임의의 깊이로 중첩됩니다.
### 서브모델 정의 { #define-a-submodel }
예를 들어, `Image` 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### 서브모델을 타입으로 사용 { #use-the-submodel-as-a-type }
그리고 이를 어트리뷰트의 타입으로 사용할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
이는 **FastAPI**가 다음과 유사한 본문을 기대한다는 것을 의미합니다:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
다시 한번, **FastAPI**로 그 선언만 해도 얻는 것은:
* 중첩 모델도 편집기 지원(자동완성 등)
* 데이터 변환
* 데이터 검증
* 자동 문서화
## 특별한 타입과 검증 { #special-types-and-validation }
`str`, `int`, `float` 등과 같은 일반적인 단일 타입과는 별개로, `str`을 상속하는 더 복잡한 단일 타입을 사용할 수 있습니다.
사용할 수 있는 모든 옵션을 보려면 [Pydantic의 Type Overview](https://docs.pydantic.dev/latest/concepts/types/)를 확인하세요. 다음 장에서 몇 가지 예제를 볼 수 있습니다.
예를 들어 `Image` 모델에는 `url` 필드가 있으므로, 이를 `str` 대신 Pydantic의 `HttpUrl` 인스턴스로 선언할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
이 문자열은 유효한 URL인지 검사되며, JSON Schema / OpenAPI에도 그에 맞게 문서화됩니다.
## 서브모델 리스트를 갖는 어트리뷰트 { #attributes-with-lists-of-submodels }
`list`, `set` 등의 서브타입으로 Pydantic 모델을 사용할 수도 있습니다:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
아래와 같은 JSON 본문을 예상(변환, 검증, 문서화 등)합니다:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | 정보
`images` 키가 이제 이미지 객체 리스트를 갖는지 주목하세요.
///
## 깊게 중첩된 모델 { #deeply-nested-models }
임의로 깊게 중첩된 모델을 정의할 수 있습니다:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | 정보
`Offer`가 `Item`의 리스트를 가지고, 그 `Item`이 다시 선택 사항인 `Image` 리스트를 갖는지 주목하세요
///
## 순수 리스트의 본문 { #bodies-of-pure-lists }
예상되는 JSON 본문의 최상위 값이 JSON `array`(파이썬 `list`)라면, Pydantic 모델에서와 마찬가지로 함수의 매개변수에서 타입을 선언할 수 있습니다:
```Python
images: list[Image]
```
이를 아래처럼:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## 어디서나 편집기 지원 { #editor-support-everywhere }
그리고 어디서나 편집기 지원을 받을 수 있습니다.
리스트 내부 항목의 경우에도:
Pydantic 모델 대신 `dict`로 직접 작업한다면 이런 종류의 편집기 지원을 받을 수 없습니다.
하지만 그 부분에 대해서도 걱정할 필요는 없습니다. 들어오는 dict는 자동으로 변환되고, 출력도 자동으로 JSON으로 변환됩니다.
## 임의의 `dict` 본문 { #bodies-of-arbitrary-dicts }
또한 키는 어떤 타입이고 값은 다른 타입인 `dict`로 본문을 선언할 수 있습니다.
이렇게 하면 (Pydantic 모델을 사용하는 경우처럼) 유효한 필드/어트리뷰트 이름이 무엇인지 미리 알 필요가 없습니다.
아직 모르는 키를 받으려는 경우에 유용합니다.
---
또 다른 유용한 경우는 다른 타입(예: `int`)의 키를 갖고 싶을 때입니다.
여기서 그 경우를 볼 것입니다.
이 경우, `int` 키와 `float` 값을 가진 한 어떤 `dict`든 받아들입니다:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | 팁
JSON은 키로 `str`만 지원한다는 것을 염두에 두세요.
하지만 Pydantic은 자동 데이터 변환 기능이 있습니다.
즉, API 클라이언트는 키로 문자열만 보낼 수 있더라도, 해당 문자열이 순수한 정수를 포함하기만 하면 Pydantic이 이를 변환하고 검증합니다.
그리고 `weights`로 받는 `dict`는 실제로 `int` 키와 `float` 값을 갖게 됩니다.
///
## 요약 { #recap }
**FastAPI**를 사용하면 Pydantic 모델이 제공하는 최대 유연성을 확보하면서 코드를 간단하고 짧고 우아하게 유지할 수 있습니다.
하지만 아래의 모든 이점도 있습니다:
* 편집기 지원(어디서나 자동완성!)
* 데이터 변환(일명 파싱/직렬화)
* 데이터 검증
* 스키마 문서화
* 자동 문서
================================================
FILE: docs/ko/docs/tutorial/body-updates.md
================================================
# Body - 업데이트 { #body-updates }
## `PUT`으로 교체 업데이트하기 { #update-replacing-with-put }
항목을 업데이트하려면 [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) 작업을 사용할 수 있습니다.
`jsonable_encoder`를 사용해 입력 데이터를 JSON으로 저장할 수 있는 데이터로 변환할 수 있습니다(예: NoSQL 데이터베이스 사용 시). 예를 들어 `datetime`을 `str`로 변환하는 경우입니다.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT`은 기존 데이터를 **대체**해야 하는 데이터를 받는 데 사용합니다.
### 대체 시 주의사항 { #warning-about-replacing }
즉, `PUT`으로 항목 `bar`를 업데이트하면서 다음과 같은 body를 보낸다면:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
이미 저장된 속성 `"tax": 20.2`가 포함되어 있지 않기 때문에, 입력 모델은 `"tax": 10.5`라는 기본값을 사용하게 됩니다.
그리고 데이터는 그 “새로운” `tax` 값 `10.5`로 저장됩니다.
## `PATCH`로 부분 업데이트하기 { #partial-updates-with-patch }
[HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) 작업을 사용해 데이터를 *부분적으로* 업데이트할 수도 있습니다.
이는 업데이트하려는 데이터만 보내고, 나머지는 그대로 두는 것을 의미합니다.
/// note | 참고
`PATCH`는 `PUT`보다 덜 일반적으로 사용되고 덜 알려져 있습니다.
그리고 많은 팀이 부분 업데이트에도 `PUT`만 사용합니다.
여러분은 원하는 방식으로 **자유롭게** 사용할 수 있으며, **FastAPI**는 어떤 제한도 강제하지 않습니다.
다만 이 가이드는 의도된 사용 방식이 대략 어떻게 되는지를 보여줍니다.
///
### Pydantic의 `exclude_unset` 파라미터 사용하기 { #using-pydantics-exclude-unset-parameter }
부분 업데이트를 받으려면 Pydantic 모델의 `.model_dump()`에서 `exclude_unset` 파라미터를 사용하는 것이 매우 유용합니다.
예: `item.model_dump(exclude_unset=True)`.
이는 `item` 모델을 만들 때 실제로 설정된 데이터만 포함하는 `dict`를 생성하고, 기본값은 제외합니다.
그 다음 이를 사용해 (요청에서 전송되어) 설정된 데이터만 포함하고 기본값은 생략한 `dict`를 만들 수 있습니다:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Pydantic의 `update` 파라미터 사용하기 { #using-pydantics-update-parameter }
이제 `.model_copy()`를 사용해 기존 모델의 복사본을 만들고, 업데이트할 데이터가 들어있는 `dict`를 `update` 파라미터로 전달할 수 있습니다.
예: `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### 부분 업데이트 요약 { #partial-updates-recap }
정리하면, 부분 업데이트를 적용하려면 다음을 수행합니다:
* (선택 사항) `PUT` 대신 `PATCH`를 사용합니다.
* 저장된 데이터를 조회합니다.
* 그 데이터를 Pydantic 모델에 넣습니다.
* 입력 모델에서 기본값이 제외된 `dict`를 생성합니다(`exclude_unset` 사용).
* 이렇게 하면 모델의 기본값으로 이미 저장된 값을 덮어쓰지 않고, 사용자가 실제로 설정한 값만 업데이트할 수 있습니다.
* 저장된 모델의 복사본을 만들고, 받은 부분 업데이트로 해당 속성들을 갱신합니다(`update` 파라미터 사용).
* 복사한 모델을 DB에 저장할 수 있는 형태로 변환합니다(예: `jsonable_encoder` 사용).
* 이는 모델의 `.model_dump()` 메서드를 다시 사용하는 것과 비슷하지만, JSON으로 변환 가능한 데이터 타입으로 값이 확실히 변환되도록 보장합니다(예: `datetime` → `str`).
* 데이터를 DB에 저장합니다.
* 업데이트된 모델을 반환합니다.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | 팁
동일한 기법을 HTTP `PUT` 작업에서도 실제로 사용할 수 있습니다.
하지만 여기의 예시는 이런 사용 사례를 위해 만들어진 `PATCH`를 사용합니다.
///
/// note | 참고
입력 모델은 여전히 검증된다는 점에 유의하세요.
따라서 모든 속성을 생략할 수 있는 부분 업데이트를 받으려면, 모든 속성이 optional로 표시된(기본값을 가지거나 `None`을 기본값으로 가지는) 모델이 필요합니다.
**업데이트**를 위한 “모든 값이 optional인” 모델과, **생성**을 위한 “필수 값이 있는” 모델을 구분하려면 [추가 모델](extra-models.md)에 설명된 아이디어를 사용할 수 있습니다.
///
================================================
FILE: docs/ko/docs/tutorial/body.md
================================================
# 요청 본문 { #request-body }
클라이언트(브라우저라고 해봅시다)로부터 여러분의 API로 데이터를 보내야 할 때, **요청 본문**으로 보냅니다.
**요청** 본문은 클라이언트에서 API로 보내지는 데이터입니다. **응답** 본문은 API가 클라이언트로 보내는 데이터입니다.
여러분의 API는 대부분의 경우 **응답** 본문을 보내야 합니다. 하지만 클라이언트는 항상 **요청 본문**을 보낼 필요는 없고, 때로는 (쿼리 매개변수와 함께) 어떤 경로만 요청하고 본문은 보내지 않을 수도 있습니다.
**요청** 본문을 선언하기 위해서 모든 강력함과 이점을 갖춘 [Pydantic](https://docs.pydantic.dev/) 모델을 사용합니다.
/// info | 정보
데이터를 보내기 위해, (좀 더 보편적인) `POST`, `PUT`, `DELETE` 혹은 `PATCH` 중에 하나를 사용하는 것이 좋습니다.
`GET` 요청에 본문을 담아 보내는 것은 명세서에 정의되지 않은 행동입니다. 그럼에도 불구하고, 이 방식은 아주 복잡한/극한의 사용 상황에서만 FastAPI에 의해 지원됩니다.
`GET` 요청에 본문을 담는 것은 권장되지 않기에, Swagger UI같은 대화형 문서에서는 `GET` 사용시 담기는 본문에 대한 문서를 표시하지 않으며, 중간에 있는 프록시는 이를 지원하지 않을 수도 있습니다.
///
## Pydantic의 `BaseModel` 임포트 { #import-pydantics-basemodel }
먼저 `pydantic`에서 `BaseModel`를 임포트해야 합니다:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## 여러분의 데이터 모델 만들기 { #create-your-data-model }
`BaseModel`를 상속받은 클래스로 여러분의 데이터 모델을 선언합니다.
모든 어트리뷰트에 대해 표준 파이썬 타입을 사용합니다:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
쿼리 매개변수를 선언할 때와 같이, 모델 어트리뷰트가 기본 값을 가지고 있어도 이는 필수가 아닙니다. 그외에는 필수입니다. 그저 `None`을 사용하여 선택적으로 만들 수 있습니다.
예를 들면, 위의 이 모델은 JSON "`object`" (혹은 파이썬 `dict`)을 다음과 같이 선언합니다:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...`description`과 `tax`는 (기본 값이 `None`으로 되어 있어) 선택적이기 때문에, 이 JSON "`object`"는 다음과 같은 상황에서도 유효합니다:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## 매개변수로서 선언하기 { #declare-it-as-a-parameter }
여러분의 *경로 처리*에 추가하기 위해, 경로 매개변수 그리고 쿼리 매개변수에서 선언했던 것과 같은 방식으로 선언하면 됩니다.
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...그리고 만들어낸 모델인 `Item`으로 타입을 선언합니다.
## 결과 { #results }
위에서의 단순한 파이썬 타입 선언으로, **FastAPI**는 다음과 같이 동작합니다:
* 요청의 본문을 JSON으로 읽어 들입니다.
* (필요하다면) 대응되는 타입으로 변환합니다.
* 데이터를 검증합니다.
* 만약 데이터가 유효하지 않다면, 정확히 어떤 것이 그리고 어디에서 데이터가 잘 못 되었는지 지시하는 친절하고 명료한 에러를 반환할 것입니다.
* 매개변수 `item`에 포함된 수신 데이터를 제공합니다.
* 함수 내에서 매개변수를 `Item` 타입으로 선언했기 때문에, 모든 어트리뷰트와 그에 대한 타입에 대한 편집기 지원(완성 등)을 또한 받을 수 있습니다.
* 여러분의 모델을 위한 [JSON Schema](https://json-schema.org) 정의를 생성합니다. 여러분의 프로젝트에 적합하다면 여러분이 사용하고 싶은 곳 어디에서나 사용할 수 있습니다.
* 이러한 스키마는, 생성된 OpenAPI 스키마 일부가 될 것이며, 자동 문서화 UIs에 사용됩니다.
## 자동 문서화 { #automatic-docs }
모델의 JSON 스키마는 생성된 OpenAPI 스키마에 포함되며 대화형 API 문서에 표시됩니다:
이를 필요로 하는 각각의 *경로 처리* 내부의 API 문서에도 사용됩니다:
## 편집기 지원 { #editor-support }
편집기에서, 함수 내에서 타입 힌트와 완성을 어디서나 (만약 Pydantic model 대신에 `dict`을 받을 경우 나타나지 않을 수 있습니다) 받을 수 있습니다:
잘못된 타입 연산에 대한 에러 확인도 받을 수 있습니다:
단순한 우연이 아닙니다. 프레임워크 전체가 이러한 디자인을 중심으로 설계되었습니다.
그 어떤 구현 전에, 모든 편집기에서 작동할 수 있도록 보장하기 위해 설계 단계에서 혹독하게 테스트되었습니다.
이를 지원하기 위해 Pydantic 자체에서 몇몇 변경점이 있었습니다.
이전 스크린샷은 [Visual Studio Code](https://code.visualstudio.com)로 찍은 것입니다.
하지만 똑같은 편집기 지원을 [PyCharm](https://www.jetbrains.com/pycharm/)와 대부분의 다른 파이썬 편집기에서도 받을 수 있습니다:
/// tip | 팁
만약 [PyCharm](https://www.jetbrains.com/pycharm/)를 편집기로 사용한다면, [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/)을 사용할 수 있습니다.
다음 사항을 포함해 Pydantic 모델에 대한 편집기 지원을 향상시킵니다:
* auto-completion
* type checks
* refactoring
* searching
* inspections
///
## 모델 사용하기 { #use-the-model }
함수 안에서 모델 객체의 모든 어트리뷰트에 직접 접근 가능합니다:
{* ../../docs_src/body/tutorial002_py310.py *}
## 요청 본문 + 경로 매개변수 { #request-body-path-parameters }
경로 매개변수와 요청 본문을 동시에 선언할 수 있습니다.
**FastAPI**는 경로 매개변수와 일치하는 함수 매개변수가 **경로에서 가져와야 한다**는 것을 인지하며, Pydantic 모델로 선언된 그 함수 매개변수는 **요청 본문에서 가져와야 한다**는 것을 인지할 것입니다.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## 요청 본문 + 경로 + 쿼리 매개변수 { #request-body-path-query-parameters }
**본문**, **경로** 그리고 **쿼리** 매개변수 모두 동시에 선언할 수도 있습니다.
**FastAPI**는 각각을 인지하고 데이터를 올바른 위치에 가져올 것입니다.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
함수 매개변수는 다음을 따라서 인지하게 됩니다:
* 만약 매개변수가 **경로**에도 선언되어 있다면, 이는 경로 매개변수로 사용될 것입니다.
* 만약 매개변수가 (`int`, `float`, `str`, `bool` 등과 같은) **유일한 타입**으로 되어있으면, **쿼리** 매개변수로 해석될 것입니다.
* 만약 매개변수가 **Pydantic 모델** 타입으로 선언되어 있으면, 요청 **본문**으로 해석될 것입니다.
/// note | 참고
FastAPI는 `q`의 값이 필요없음을 기본 값 `= None` 때문에 알게 됩니다.
`str | None`은 FastAPI가 값이 필수인지 아닌지를 판단하기 위해 사용하지 않습니다. 기본 값이 `= None`이므로 필수가 아님을 알게 됩니다.
하지만 타입 어노테이션을 추가하면 편집기가 더 나은 지원을 제공하고 오류를 감지할 수 있습니다.
///
## Pydantic없이 { #without-pydantic }
만약 Pydantic 모델을 사용하고 싶지 않다면, **Body** 매개변수를 사용할 수도 있습니다. [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body) 문서를 확인하세요.
================================================
FILE: docs/ko/docs/tutorial/cookie-param-models.md
================================================
# 쿠키 매개변수 모델 { #cookie-parameter-models }
관련있는 **쿠키**들의 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다. 🍪
이를 통해 **여러 위치**에서 **모델을 재사용** 할 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😎
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
///
/// tip | 팁
동일한 기술이 `Query`, `Cookie`, 그리고 `Header`에 적용됩니다. 😎
///
## Pydantic 모델을 사용한 쿠키 { #cookies-with-a-pydantic-model }
**Pydantic 모델**에 필요한 **쿠키** 매개변수를 선언한 다음, 해당 매개변수를 `Cookie`로 선언합니다:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI**는 요청에서 받은 **쿠키**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 정의한 쿠키를 볼 수 있습니다:
/// info | 정보
명심하세요, 내부적으로 **브라우저는 쿠키를 특별한 방식으로 처리**하기 때문에 **자바스크립트**가 쉽게 쿠키를 건드릴 수 **없습니다**.
`/docs`에서 **API 문서 UI**로 이동하면 *경로 처리*에 대한 쿠키의 **문서**를 볼 수 있습니다.
하지만 아무리 **데이터를 입력**하고 "실행(Execute)"을 클릭해도, 문서 UI는 **자바스크립트**로 작동하기 때문에 쿠키는 전송되지 않고, 아무 값도 쓰지 않은 것처럼 **오류** 메시지를 보게 됩니다.
///
## 추가 쿠키 금지하기 { #forbid-extra-cookies }
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 쿠키를 **제한**할 수 있습니다.
이제 API는 자신의 쿠키 동의를 제어할 수 있는 권한을 갖게 되었습니다. 🤪🍪
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
클라이언트가 **추가 쿠키**를 보내려고 시도하면, **오류** 응답을 받게 됩니다.
동의를 얻기 위해 애쓰는 불쌍한 쿠키 배너(팝업)들, API가 거부하는데도. 🍪
예를 들어, 클라이언트가 `good-list-please` 값으로 `santa_tracker` 쿠키를 보내려고 하면 클라이언트는 `santa_tracker` 쿠키가 허용되지 않는다는 **오류** 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## 요약 { #summary }
**Pydantic 모델**을 사용하여 **FastAPI**에서 **쿠키**를 선언할 수 있습니다. 😎
================================================
FILE: docs/ko/docs/tutorial/cookie-params.md
================================================
# 쿠키 매개변수 { #cookie-parameters }
쿠키 매개변수를 `Query`와 `Path` 매개변수들과 같은 방식으로 정의할 수 있습니다.
## `Cookie` 임포트 { #import-cookie }
먼저 `Cookie`를 임포트합니다:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## `Cookie` 매개변수 선언 { #declare-cookie-parameters }
그런 다음, `Path`와 `Query`처럼 동일한 구조를 사용하는 쿠키 매개변수를 선언합니다.
첫 번째 값은 기본값이며, 추가 검증이나 어노테이션 매개변수 모두 전달할 수 있습니다:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | 기술 세부사항
`Cookie`는 `Path` 및 `Query`의 "자매"클래스입니다. 이 역시 동일한 공통 `Param` 클래스를 상속합니다.
하지만 `fastapi`에서 `Query`, `Path`, `Cookie` 그리고 다른 것들을 임포트할 때, 실제로는 특별한 클래스를 반환하는 함수임을 기억하세요.
///
/// info
쿠키를 선언하기 위해서는 `Cookie`를 사용해야 합니다. 그렇지 않으면 해당 매개변수를 쿼리 매개변수로 해석하기 때문입니다.
///
/// info
**브라우저는 쿠키를** 내부적으로 특별한 방식으로 처리하기 때문에, **JavaScript**가 쉽게 쿠키를 다루도록 허용하지 않는다는 점을 염두에 두세요.
`/docs`의 **API docs UI**로 이동하면 *경로 처리*에 대한 쿠키 **문서**를 확인할 수 있습니다.
하지만 **데이터를 채우고** "Execute"를 클릭하더라도, docs UI는 **JavaScript**로 동작하기 때문에 쿠키가 전송되지 않고, 아무 값도 입력하지 않은 것처럼 **오류** 메시지를 보게 될 것입니다.
///
## 요약 { #recap }
`Query`와 `Path`에서 사용하는 것과 동일한 공통 패턴으로, `Cookie`를 사용해 쿠키를 선언합니다.
================================================
FILE: docs/ko/docs/tutorial/cors.md
================================================
# CORS (교차-출처 리소스 공유) { #cors-cross-origin-resource-sharing }
[CORS 또는 "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)란, 브라우저에서 실행되는 프론트엔드에 백엔드와 통신하는 JavaScript 코드가 있고, 백엔드가 프론트엔드와 다른 "출처(origin)"에 있는 상황을 의미합니다.
## 출처 { #origin }
출처란 프로토콜(`http`, `https`), 도메인(`myapp.com`, `localhost`, `localhost.tiangolo.com`), 그리고 포트(`80`, `443`, `8080`)의 조합을 의미합니다.
따라서, 아래는 모두 상이한 출처입니다:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
모두 `localhost`에 있더라도, 서로 다른 프로토콜이나 포트를 사용하므로 서로 다른 "출처"입니다.
## 단계 { #steps }
그러면 브라우저에서 `http://localhost:8080`으로 실행되는 프론트엔드가 있고, 그 JavaScript가 `http://localhost`에서 실행되는 백엔드와 통신하려고 한다고 해봅시다(포트를 명시하지 않았기 때문에, 브라우저는 기본 포트 `80`을 가정합니다).
그러면 브라우저는 `:80`-백엔드에 HTTP `OPTIONS` 요청을 보내고, 백엔드가 이 다른 출처(`http://localhost:8080`)로부터의 통신을 허가하는 적절한 헤더를 보내면, `:8080`-브라우저는 프론트엔드의 JavaScript가 `:80`-백엔드에 요청을 보낼 수 있도록 합니다.
이를 위해, `:80`-백엔드는 "허용된 출처(allowed origins)" 목록을 가지고 있어야 합니다.
이 경우, `:8080`-프론트엔드가 올바르게 동작하려면 목록에 `http://localhost:8080`이 포함되어야 합니다.
## 와일드카드 { #wildcards }
또한 목록을 `"*"`("와일드카드")로 선언해 모두 허용된다고 말할 수도 있습니다.
하지만 그러면 자격 증명(credentials)이 포함된 모든 것을 제외하고 특정 유형의 통신만 허용하게 됩니다. 예: 쿠키, Bearer Token에 사용되는 것과 같은 Authorization 헤더 등.
따라서 모든 것이 올바르게 동작하게 하려면, 허용된 출처를 명시적으로 지정하는 것이 더 좋습니다.
## `CORSMiddleware` 사용 { #use-corsmiddleware }
`CORSMiddleware`를 사용하여 **FastAPI** 애플리케이션에서 이를 설정할 수 있습니다.
* `CORSMiddleware`를 임포트합니다.
* 허용된 출처(문자열)의 리스트를 생성합니다.
* **FastAPI** 애플리케이션에 "미들웨어(middleware)"로 추가합니다.
또한 백엔드가 다음을 허용할지 여부도 지정할 수 있습니다:
* 자격 증명(Authorization 헤더, 쿠키 등).
* 특정 HTTP 메서드(`POST`, `PUT`) 또는 와일드카드 `"*"`를 사용한 모든 메서드.
* 특정 HTTP 헤더 또는 와일드카드 `"*"`를 사용한 모든 헤더.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
`CORSMiddleware` 구현에서 사용하는 기본 매개변수는 기본적으로 제한적이므로, 브라우저가 Cross-Domain 컨텍스트에서 특정 출처, 메서드 또는 헤더를 사용할 수 있도록 하려면 이를 명시적으로 활성화해야 합니다.
다음 인자들이 지원됩니다:
* `allow_origins` - 교차-출처 요청을 보낼 수 있도록 허용해야 하는 출처의 리스트입니다. 예: `['https://example.org', 'https://www.example.org']`. 모든 출처를 허용하려면 `['*']`를 사용할 수 있습니다.
* `allow_origin_regex` - 교차-출처 요청을 보낼 수 있도록 허용해야 하는 출처와 매칭할 정규표현식 문자열입니다. 예: `'https://.*\.example\.org'`.
* `allow_methods` - 교차-출처 요청에 허용되어야 하는 HTTP 메서드의 리스트입니다. 기본값은 `['GET']`입니다. 모든 표준 메서드를 허용하려면 `['*']`를 사용할 수 있습니다.
* `allow_headers` - 교차-출처 요청에 대해 지원되어야 하는 HTTP 요청 헤더의 리스트입니다. 기본값은 `[]`입니다. 모든 헤더를 허용하려면 `['*']`를 사용할 수 있습니다. `Accept`, `Accept-Language`, `Content-Language`, `Content-Type` 헤더는 [단순 CORS 요청](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)에 대해 항상 허용됩니다.
* `allow_credentials` - 교차-출처 요청에 대해 쿠키를 지원해야 함을 나타냅니다. 기본값은 `False`입니다.
`allow_credentials`가 `True`로 설정된 경우 `allow_origins`, `allow_methods`, `allow_headers` 중 어느 것도 `['*']`로 설정할 수 없습니다. 모두 [명시적으로 지정](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards)되어야 합니다.
* `expose_headers` - 브라우저에서 접근 가능해야 하는 모든 응답 헤더를 나타냅니다. 기본값은 `[]`입니다.
* `max_age` - 브라우저가 CORS 응답을 캐시하는 최대 시간을 초 단위로 설정합니다. 기본값은 `600`입니다.
미들웨어는 두 가지 특정한 종류의 HTTP 요청에 응답합니다...
### CORS 사전 요청 { #cors-preflight-requests }
`Origin` 및 `Access-Control-Request-Method` 헤더가 있는 모든 `OPTIONS` 요청입니다.
이 경우 미들웨어는 들어오는 요청을 가로채 적절한 CORS 헤더와 함께, 정보 제공 목적으로 `200` 또는 `400` 응답을 반환합니다.
### 단순한 요청 { #simple-requests }
`Origin` 헤더가 있는 모든 요청입니다. 이 경우 미들웨어는 요청을 정상적으로 통과시키지만, 응답에 적절한 CORS 헤더를 포함합니다.
## 더 많은 정보 { #more-info }
CORS에 대한 더 많은 정보는 [Mozilla CORS 문서](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)를 참고하세요.
/// note | 기술 세부사항
`from starlette.middleware.cors import CORSMiddleware`도 사용할 수 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `fastapi.middleware`에 여러 미들웨어를 제공합니다. 하지만 사용 가능한 미들웨어 대부분은 Starlette에서 직접 제공됩니다.
///
================================================
FILE: docs/ko/docs/tutorial/debugging.md
================================================
# 디버깅 { #debugging }
예를 들면 Visual Studio Code 또는 PyCharm을 사용하여 편집기에서 디버거를 연결할 수 있습니다.
## `uvicorn` 호출 { #call-uvicorn }
FastAPI 애플리케이션에서 `uvicorn`을 직접 임포트하여 실행합니다:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### `__name__ == "__main__"` 에 대하여 { #about-name-main }
`__name__ == "__main__"`의 주요 목적은 다음과 같이 파일이 호출될 때 실행되는 일부 코드를 갖는 것입니다.
```console
$ python myapp.py
```
그러나 다음과 같이 다른 파일을 가져올 때는 호출되지 않습니다.
```Python
from myapp import app
```
#### 추가 세부사항 { #more-details }
파일 이름이 `myapp.py`라고 가정해 보겠습니다.
다음과 같이 실행하면
```console
$ python myapp.py
```
Python에 의해 자동으로 생성된 파일의 내부 변수 `__name__`은 문자열 `"__main__"`을 값으로 갖게 됩니다.
따라서 섹션
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
이 실행됩니다.
---
해당 모듈(파일)을 가져오면 이런 일이 발생하지 않습니다
그래서 다음과 같은 다른 파일 `importer.py`가 있는 경우:
```Python
from myapp import app
# Some more code
```
이 경우 `myapp.py` 내부의 자동 변수 `__name__`에는 값이 `"__main__"`이 들어가지 않습니다.
따라서 다음 행
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
은 실행되지 않습니다.
/// info | 정보
자세한 내용은 [공식 Python 문서](https://docs.python.org/3/library/__main__.html)를 확인하세요.
///
## 디버거로 코드 실행 { #run-your-code-with-your-debugger }
코드에서 직접 Uvicorn 서버를 실행하고 있기 때문에 디버거에서 직접 Python 프로그램(FastAPI 애플리케이션)을 호출할 수 있습니다.
---
예를 들어 Visual Studio Code에서 다음을 수행할 수 있습니다.
* "Debug" 패널로 이동합니다.
* "Add configuration...".
* "Python"을 선택합니다.
* "`Python: Current File (Integrated Terminal)`" 옵션으로 디버거를 실행합니다.
그런 다음 **FastAPI** 코드로 서버를 시작하고 중단점 등에서 중지합니다.
다음과 같이 표시됩니다.
---
Pycharm을 사용하는 경우 다음을 수행할 수 있습니다
* "Run" 메뉴를 엽니다.
* "Debug..." 옵션을 선택합니다.
* 그러면 상황에 맞는 메뉴가 나타납니다.
* 디버그할 파일을 선택합니다(이 경우 `main.py`).
그런 다음 **FastAPI** 코드로 서버를 시작하고 중단점 등에서 중지합니다.
다음과 같이 표시됩니다.
================================================
FILE: docs/ko/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# 의존성으로서의 클래스 { #classes-as-dependencies }
**의존성 주입** 시스템에 대해 더 깊이 살펴보기 전에, 이전 예제를 업그레이드해 보겠습니다.
## 이전 예제의 `dict` { #a-dict-from-the-previous-example }
이전 예제에서는 의존성("dependable")에서 `dict`를 반환하고 있었습니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
하지만 그러면 *경로 처리 함수*의 매개변수 `commons`에서 `dict`를 받게 됩니다.
그리고 에디터는 `dict`의 키와 값 타입을 알 수 없기 때문에 `dict`에 대해서는 (완성 기능 같은) 많은 지원을 제공할 수 없다는 것을 알고 있습니다.
더 나은 방법이 있습니다...
## 의존성이 되기 위한 조건 { #what-makes-a-dependency }
지금까지는 함수로 선언된 의존성을 봤습니다.
하지만 그것만이 의존성을 선언하는 유일한 방법은 아닙니다(아마도 더 일반적이긴 하겠지만요).
핵심 요소는 의존성이 "호출 가능(callable)"해야 한다는 것입니다.
파이썬에서 "**호출 가능(callable)**"이란 파이썬이 함수처럼 "호출"할 수 있는 모든 것입니다.
따라서 `something`(함수가 _아닐_ 수도 있습니다)이라는 객체가 있고, 다음처럼 "호출"(실행)할 수 있다면:
```Python
something()
```
또는
```Python
something(some_argument, some_keyword_argument="foo")
```
그것은 "호출 가능(callable)"입니다.
## 의존성으로서의 클래스 { #classes-as-dependencies_1 }
파이썬 클래스의 인스턴스를 만들 때도 같은 문법을 사용한다는 것을 알 수 있을 겁니다.
예를 들어:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
이 경우 `fluffy`는 클래스 `Cat`의 인스턴스입니다.
그리고 `fluffy`를 만들기 위해 `Cat`을 "호출"하고 있습니다.
따라서 파이썬 클래스도 **호출 가능(callable)**합니다.
그러면 **FastAPI**에서는 파이썬 클래스를 의존성으로 사용할 수 있습니다.
FastAPI가 실제로 확인하는 것은 그것이 "호출 가능(callable)"(함수, 클래스, 또는 다른 무엇이든)한지와 정의된 매개변수들입니다.
**FastAPI**에서 "호출 가능(callable)"한 것을 의존성으로 넘기면, 그 "호출 가능(callable)"한 것의 매개변수들을 분석하고 *경로 처리 함수*의 매개변수와 동일한 방식으로 처리합니다. 하위 의존성도 포함해서요.
이것은 매개변수가 전혀 없는 callable에도 적용됩니다. 매개변수가 없는 *경로 처리 함수*에 적용되는 것과 동일합니다.
그러면 위의 의존성("dependable") `common_parameters`를 클래스 `CommonQueryParams`로 바꿀 수 있습니다:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
클래스의 인스턴스를 만들 때 사용하는 `__init__` 메서드에 주목하세요:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...이전의 `common_parameters`와 동일한 매개변수를 가지고 있습니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
이 매개변수들이 **FastAPI**가 의존성을 "해결"하는 데 사용할 것들입니다.
두 경우 모두 다음을 갖게 됩니다:
* `str`인 선택적 쿼리 매개변수 `q`.
* 기본값이 `0`인 `int` 쿼리 매개변수 `skip`.
* 기본값이 `100`인 `int` 쿼리 매개변수 `limit`.
두 경우 모두 데이터는 변환되고, 검증되며, OpenAPI 스키마에 문서화되는 등 여러 처리가 적용됩니다.
## 사용하기 { #use-it }
이제 이 클래스를 사용해 의존성을 선언할 수 있습니다.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI**는 `CommonQueryParams` 클래스를 호출합니다. 그러면 해당 클래스의 "인스턴스"가 생성되고, 그 인스턴스가 함수의 매개변수 `commons`로 전달됩니다.
## 타입 어노테이션 vs `Depends` { #type-annotation-vs-depends }
위 코드에서 `CommonQueryParams`를 두 번 작성하는 방식에 주목하세요:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
마지막 `CommonQueryParams`는, 다음에서:
```Python
... Depends(CommonQueryParams)
```
...**FastAPI**가 실제로 무엇이 의존성인지 알기 위해 사용하는 것입니다.
FastAPI는 여기에서 선언된 매개변수들을 추출하고, 실제로 이것을 호출합니다.
---
이 경우 첫 번째 `CommonQueryParams`는 다음에서:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams ...
```
////
...**FastAPI**에 특별한 의미가 없습니다. FastAPI는 이를 데이터 변환, 검증 등에 사용하지 않습니다(그런 용도로는 `Depends(CommonQueryParams)`를 사용하고 있기 때문입니다).
실제로는 이렇게만 작성해도 됩니다:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...다음과 같이요:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
하지만 타입을 선언하는 것을 권장합니다. 그러면 에디터가 매개변수 `commons`에 무엇이 전달되는지 알 수 있고, 코드 완성, 타입 체크 등에서 도움을 받을 수 있습니다:
## 단축 { #shortcut }
하지만 `CommonQueryParams`를 두 번 작성하는 코드 반복이 있다는 것을 볼 수 있습니다:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI**는 이런 경우를 위한 단축 방법을 제공합니다. 이때 의존성은 *특히* **FastAPI**가 "호출"해서 클래스 자체의 인스턴스를 만들도록 하는 클래스입니다.
이 특정한 경우에는 다음과 같이 할 수 있습니다:
다음처럼 작성하는 대신:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...이렇게 작성합니다:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ Annotated 미사용
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python
commons: CommonQueryParams = Depends()
```
////
의존성을 매개변수의 타입으로 선언하고, `Depends(CommonQueryParams)` 안에 클래스 전체를 *다시* 작성하는 대신 매개변수 없이 `Depends()`를 사용합니다.
그러면 같은 예제는 다음처럼 보일 겁니다:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...그리고 **FastAPI**는 무엇을 해야 하는지 알게 됩니다.
/// tip | 팁
도움이 되기보다 더 헷갈린다면, 무시하세요. 이건 *필수*가 아닙니다.
그저 단축 방법일 뿐입니다. **FastAPI**는 코드 반복을 최소화하도록 도와주는 것을 중요하게 생각하기 때문입니다.
///
================================================
FILE: docs/ko/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# 경로 처리 데코레이터에서의 의존성 { #dependencies-in-path-operation-decorators }
몇몇 경우에는, *경로 처리 함수* 안에서 의존성의 반환 값이 필요하지 않습니다.
또는 의존성이 값을 반환하지 않습니다.
그러나 여전히 실행/해결될 필요가 있습니다.
그런 경우에, `Depends`를 사용하여 *경로 처리 함수*의 매개변수로 선언하는 대신 *경로 처리 데코레이터*에 `dependencies`의 `list`를 추가할 수 있습니다.
## *경로 처리 데코레이터*에 `dependencies` 추가하기 { #add-dependencies-to-the-path-operation-decorator }
*경로 처리 데코레이터*는 선택적인 인자 `dependencies`를 받습니다.
`Depends()`로 된 `list`이어야 합니다:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
이러한 의존성들은 기존 의존성들과 같은 방식으로 실행/해결됩니다. 그러나 값은 (무엇이든 반환한다면) *경로 처리 함수*에 제공되지 않습니다.
/// tip | 팁
일부 편집기에서는 사용되지 않는 함수 매개변수를 검사하고 오류로 표시합니다.
*경로 처리 데코레이터*에서 이러한 `dependencies`를 사용하면 편집기/도구 오류를 피하면서도 실행되도록 할 수 있습니다.
또한 코드에서 사용되지 않는 매개변수를 보고 불필요하다고 생각할 수 있는 새로운 개발자의 혼란을 방지하는데 도움이 될 수 있습니다.
///
/// info | 정보
이 예시에서 `X-Key`와 `X-Token`이라는 커스텀 헤더를 만들어 사용했습니다.
그러나 실제로 보안을 구현할 때는 통합된 [보안 유틸리티 (다음 챕터)](../security/index.md)를 사용하는 것이 더 많은 이점을 얻을 수 있습니다.
///
## 의존성 오류와 값 반환하기 { #dependencies-errors-and-return-values }
평소에 사용하던대로 같은 의존성 *함수*를 사용할 수 있습니다.
### 의존성 요구사항 { #dependency-requirements }
(헤더같은) 요청 요구사항이나 하위-의존성을 선언할 수 있습니다:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### 오류 발생시키기 { #raise-exceptions }
다음 의존성은 기존 의존성과 동일하게 예외를 `raise`할 수 있습니다:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### 값 반환하기 { #return-values }
값을 반환하거나, 그러지 않을 수 있으며 값은 사용되지 않습니다.
그래서 이미 다른 곳에서 사용된 (값을 반환하는) 일반적인 의존성을 재사용할 수 있고, 비록 값은 사용되지 않지만 의존성은 실행될 것입니다:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## *경로 처리* 모음에 대한 의존성 { #dependencies-for-a-group-of-path-operations }
나중에 여러 파일을 가지고 있을 수 있는 더 큰 애플리케이션을 구조화하는 법([더 큰 애플리케이션 - 여러 파일들](../../tutorial/bigger-applications.md))을 읽을 때, *경로 처리* 모음에 대한 단일 `dependencies` 매개변수를 선언하는 법에 대해서 배우게 될 것입니다.
## 전역 의존성 { #global-dependencies }
다음으로 각 *경로 처리*에 적용되도록 `FastAPI` 애플리케이션 전체에 의존성을 추가하는 법을 볼 것입니다.
================================================
FILE: docs/ko/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# `yield`를 사용하는 의존성 { #dependencies-with-yield }
FastAPI는 작업 완료 후 추가 단계를 수행하는 의존성을 지원합니다.
이를 구현하려면 `return` 대신 `yield`를 사용하고, 추가로 실행할 단계 (코드)를 그 뒤에 작성하세요.
/// tip
각 의존성마다 `yield`는 한 번만 사용해야 합니다.
///
/// note | 기술 세부사항
다음과 함께 사용할 수 있는 모든 함수:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) 또는
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
는 **FastAPI**의 의존성으로 사용할 수 있습니다.
사실, FastAPI는 내부적으로 이 두 데코레이터를 사용합니다.
///
## `yield`를 사용하는 데이터베이스 의존성 { #a-database-dependency-with-yield }
예를 들어, 이 기능을 사용하면 데이터베이스 세션을 생성하고 작업이 끝난 후에 세션을 종료할 수 있습니다.
응답을 생성하기 전에는 `yield`문을 포함하여 그 이전의 코드만이 실행됩니다:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
yield된 값은 *경로 처리* 및 다른 의존성들에 주입되는 값 입니다:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
`yield`문 다음의 코드는 응답을 생성한 후 실행됩니다:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip
`async` 함수와 일반 함수 모두 사용할 수 있습니다.
**FastAPI**는 일반 의존성과 마찬가지로 각각의 함수를 올바르게 처리할 것입니다.
///
## `yield`와 `try`를 사용하는 의존성 { #a-dependency-with-yield-and-try }
`yield`를 사용하는 의존성에서 `try` 블록을 사용한다면, 의존성을 사용하는 도중 발생한 모든 예외를 받을 수 있습니다.
예를 들어, 다른 의존성이나 *경로 처리*의 중간에 데이터베이스 트랜잭션 "롤백"이 발생하거나 다른 오류가 발생한다면, 해당 예외를 의존성에서 받을 수 있습니다.
따라서, 의존성 내에서 `except SomeException`을 사용하여 특정 예외를 처리할 수 있습니다.
마찬가지로, `finally`를 사용하여 예외 발생 여부와 관계 없이 종료 단계까 실행되도록 할 수 있습니다.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## `yield`를 사용하는 하위 의존성 { #sub-dependencies-with-yield }
모든 크기와 형태의 하위 의존성과 하위 의존성의 "트리"도 가질 수 있으며, 이들 모두가 `yield`를 사용할 수 있습니다.
**FastAPI**는 `yield`를 사용하는 각 의존성의 "종료 코드"가 올바른 순서로 실행되도록 보장합니다.
예를 들어, `dependency_c`는 `dependency_b`에 의존할 수 있고, `dependency_b`는 `dependency_a`에 의존할 수 있습니다.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
이들 모두는 `yield`를 사용할 수 있습니다.
이 경우 `dependency_c`는 종료 코드를 실행하기 위해, `dependency_b`의 값 (여기서는 `dep_b`로 명명)이 여전히 사용 가능해야 합니다.
그리고, `dependency_b`는 종료 코드를 위해 `dependency_a`의 값 (여기서는 `dep_a`로 명명) 이 사용 가능해야 합니다.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
같은 방식으로, `yield`를 사용하는 의존성과 `return`을 사용하는 의존성을 함께 사용할 수 있으며, 이들 중 일부가 다른 것들에 의존할 수 있습니다.
그리고 `yield`를 사용하는 다른 여러 의존성을 필요로 하는 단일 의존성을 가질 수도 있습니다.
원하는 의존성을 원하는 대로 조합할 수 있습니다.
**FastAPI**는 모든 것이 올바른 순서로 실행되도록 보장합니다.
/// note | 기술 세부사항
파이썬의 [Context Managers](https://docs.python.org/3/library/contextlib.html) 덕분에 이 기능이 작동합니다.
**FastAPI**는 이를 내부적으로 사용하여 이를 달성합니다.
///
## `yield`와 `HTTPException`를 사용하는 의존성 { #dependencies-with-yield-and-httpexception }
`yield`를 사용하는 의존성에서 `try` 블록을 사용해 코드를 실행하고, 그 다음 `finally` 뒤에 종료 코드를 실행할 수 있다는 것을 보았습니다.
또한 `except`를 사용해 발생한 예외를 잡고 그에 대해 무언가를 할 수도 있습니다.
예를 들어, `HTTPException` 같은 다른 예외를 발생시킬 수 있습니다.
/// tip
이는 다소 고급 기술이며, 대부분의 경우 실제로는 필요하지 않을 것입니다. 예를 들어, *경로 처리 함수* 등 나머지 애플리케이션 코드 내부에서 예외 (`HTTPException` 포함)를 발생시킬 수 있기 때문입니다.
하지만 필요한 경우 사용할 수 있습니다. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
예외를 잡고 그에 기반해 사용자 정의 응답을 생성하려면, [사용자 정의 예외 처리기](../handling-errors.md#install-custom-exception-handlers)를 생성하세요.
## `yield`와 `except`를 사용하는 의존성 { #dependencies-with-yield-and-except }
`yield`를 사용하는 의존성에서 `except`를 사용하여 예외를 포착하고 예외를 다시 발생시키지 않거나 (또는 새 예외를 발생시키지 않으면), FastAPI는 일반적인 Python에서와 마찬가지로 예외가 있었다는 것을 알아차릴 수 없습니다:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
이 경우, `HTTPException`이나 유사한 예외를 발생시키지 않기 때문에 클라이언트는 마땅히 *HTTP 500 Internal Server Error* 응답을 보게 되지만, 서버에는 어떤 오류였는지에 대한 **로그**나 다른 표시가 **전혀 남지 않게 됩니다**. 😱
### `yield`와 `except`를 사용하는 의존성에서 항상 `raise` 하기 { #always-raise-in-dependencies-with-yield-and-except }
`yield`가 있는 의존성에서 예외를 잡았을 때, 다른 `HTTPException`이나 유사한 예외를 발생시키는 것이 아니라면, **원래 예외를 다시 발생시켜야 합니다**.
`raise`를 사용하여 동일한 예외를 다시 발생시킬 수 있습니다:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
이제 클라이언트는 동일한 *HTTP 500 Internal Server Error* 응답을 받게 되지만, 서버 로그에는 사용자 정의 `InternalError`가 기록됩니다. 😎
## `yield`를 사용하는 의존성의 실행 순서 { #execution-of-dependencies-with-yield }
실행 순서는 아래 다이어그램과 거의 비슷합니다. 시간은 위에서 아래로 흐릅니다. 그리고 각 열은 상호 작용하거나 코드를 실행하는 부분 중 하나입니다.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info
클라이언트에는 **하나의 응답**만 전송됩니다. 이는 오류 응답 중 하나일 수도 있고, *경로 처리*에서 생성된 응답일 수도 있습니다.
이러한 응답 중 하나가 전송된 후에는 다른 응답을 보낼 수 없습니다.
///
/// tip
*경로 처리 함수*의 코드에서 어떤 예외를 발생시키면 `HTTPException`을 포함해 `yield`를 사용하는 의존성으로 전달됩니다. 대부분의 경우 해당 예외(또는 새 예외)를 `yield`를 사용하는 의존성에서 다시 발생시켜, 제대로 처리되도록 해야 합니다.
///
## 조기 종료와 `scope` { #early-exit-and-scope }
일반적으로 `yield`를 사용하는 의존성의 종료 코드는 클라이언트로 **응답이 전송된 후에** 실행됩니다.
하지만 *경로 처리 함수*에서 반환한 뒤에는 더 이상 해당 의존성이 필요 없다는 것을 알고 있다면, `Depends(scope="function")`을 사용하여 FastAPI에 *경로 처리 함수*가 반환된 후, 하지만 **응답이 전송되기 전에** 의존성을 종료(닫기)해야 한다고 알려줄 수 있습니다.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()`는 다음이 될 수 있는 `scope` 매개변수를 받습니다:
* `"function"`: 요청을 처리하는 *경로 처리 함수* 전에 의존성을 시작하고, *경로 처리 함수*가 끝난 후, 하지만 응답이 클라이언트로 전송되기 **전에** 의존성을 종료합니다. 즉, 의존성 함수는 *경로 처리 **함수***를 **둘러싸며** 실행됩니다.
* `"request"`: 요청을 처리하는 *경로 처리 함수* 전에 의존성을 시작하고(`"function"`을 사용할 때와 유사), 응답이 클라이언트로 전송된 **후에** 종료합니다. 즉, 의존성 함수는 **요청**과 응답 사이클을 **둘러싸며** 실행됩니다.
지정하지 않고 의존성이 `yield`를 사용한다면, 기본 `scope`는 `"request"`입니다.
### 하위 의존성을 위한 `scope` { #scope-for-sub-dependencies }
`scope="request"`(기본값)로 의존성을 선언하면, 모든 하위 의존성도 `scope`가 `"request"`여야 합니다.
하지만 `scope`가 `"function"`인 의존성은 `scope`가 `"function"`인 의존성과 `"request"`인 의존성을 모두 의존성으로 가질 수 있습니다.
이는 어떤 의존성이든, 종료 코드에서 하위 의존성을 계속 사용해야 할 수도 있으므로, 하위 의존성보다 먼저 종료 코드를 실행할 수 있어야 하기 때문입니다.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성 { #dependencies-with-yield-httpexception-except-and-background-tasks }
`yield`를 사용하는 의존성은 시간이 지나면서 서로 다른 사용 사례를 다루고 일부 문제를 수정하기 위해 발전해 왔습니다.
FastAPI의 여러 버전에서 무엇이 바뀌었는지 보고 싶다면, 고급 가이드의 [고급 의존성 - `yield`, `HTTPException`, `except` 및 백그라운드 작업을 사용하는 의존성](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks)에서 더 자세히 읽을 수 있습니다.
## 컨텍스트 관리자 { #context-managers }
### "컨텍스트 관리자"란 { #what-are-context-managers }
"컨텍스트 관리자"는 Python에서 `with` 문에서 사용할 수 있는 모든 객체를 의미합니다.
예를 들어, [with를 사용해 파일을 읽을 수 있습니다](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
내부적으로 `open("./somefile.txt")` 는 "컨텍스트 관리자(Context Manager)"라고 불리는 객체를 생성합니다.
`with` 블록이 끝나면, 예외가 발생했더라도 파일을 닫도록 보장합니다.
`yield`가 있는 의존성을 생성하면 **FastAPI**는 내부적으로 이를 위한 컨텍스트 매니저를 생성하고 다른 관련 도구들과 결합합니다.
### `yield`를 사용하는 의존성에서 컨텍스트 관리자 사용하기 { #using-context-managers-in-dependencies-with-yield }
/// warning
이것은 어느 정도 "고급" 개념입니다.
**FastAPI**를 처음 시작하는 경우 지금은 이 부분을 건너뛰어도 좋습니다.
///
Python에서는 [두 가지 메서드: `__enter__()`와 `__exit__()`가 있는 클래스를 생성하여](https://docs.python.org/3/reference/datamodel.html#context-managers) 컨텍스트 관리자를 만들 수 있습니다.
**FastAPI**의 `yield`가 있는 의존성 내에서
`with` 또는 `async with`문을 사용하여 이들을 활용할 수 있습니다:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip
컨텍스트 관리자를 생성하는 또 다른 방법은 다음과 같습니다:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) 또는
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
이들은 단일 `yield`가 있는 함수를 꾸미는 데 사용합니다.
이것이 **FastAPI**가 `yield`가 있는 의존성을 위해 내부적으로 사용하는 방식입니다.
하지만 FastAPI 의존성에는 이러한 데코레이터를 사용할 필요가 없습니다(그리고 사용해서도 안됩니다).
FastAPI가 내부적으로 이를 처리해 줄 것입니다.
///
================================================
FILE: docs/ko/docs/tutorial/dependencies/global-dependencies.md
================================================
# 전역 의존성 { #global-dependencies }
몇몇 유형의 애플리케이션에서는 애플리케이션 전체에 의존성을 추가하고 싶을 수 있습니다.
[*경로 처리 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md)와 유사한 방법으로 `FastAPI` 애플리케이션에 그것들을 추가할 수 있습니다.
그런 경우에, 애플리케이션의 모든 *경로 처리*에 적용될 것입니다:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
그리고 [*경로 처리 데코레이터*에 `dependencies` 추가하기](dependencies-in-path-operation-decorators.md) 섹션의 모든 아이디어는 여전히 적용되지만, 이 경우에는 애플리케이션의 모든 *경로 처리*에 적용됩니다.
## *경로 처리* 그룹에 대한 의존성 { #dependencies-for-groups-of-path-operations }
나중에 여러 파일을 포함할 수도 있는 더 큰 애플리케이션을 구조화하는 법([더 큰 애플리케이션 - 여러 파일들](../../tutorial/bigger-applications.md))을 읽을 때, *경로 처리* 그룹에 대한 단일 `dependencies` 매개변수를 선언하는 법을 배우게 될 것입니다.
================================================
FILE: docs/ko/docs/tutorial/dependencies/index.md
================================================
# 의존성 { #dependencies }
**FastAPI**는 아주 강력하지만 직관적인 **의존성 주입** 시스템을 가지고 있습니다.
이는 사용하기 아주 쉽게 설계했으며, 어느 개발자나 다른 컴포넌트와 **FastAPI**를 쉽게 통합할 수 있도록 만들었습니다.
## "의존성 주입"은 무엇입니까? { #what-is-dependency-injection }
**"의존성 주입"**은 프로그래밍에서 여러분의 코드(이 경우, *경로 처리 함수*)가 작동하고 사용하는 데 필요로 하는 것, 즉 "의존성"을 선언할 수 있는 방법을 의미합니다.
그 후에, 시스템(이 경우 **FastAPI**)은 여러분의 코드가 요구하는 의존성을 제공하기 위해 필요한 모든 작업을 처리합니다.(의존성을 "주입"합니다)
이는 여러분이 다음과 같은 사항을 필요로 할 때 매우 유용합니다:
* 공용된 로직을 가졌을 경우 (같은 코드 로직이 계속 반복되는 경우).
* 데이터베이스 연결을 공유하는 경우.
* 보안, 인증, 역할 요구 사항 등을 강제하는 경우.
* 그리고 많은 다른 사항...
이 모든 사항을 할 때 코드 반복을 최소화합니다.
## 첫번째 단계 { #first-steps }
아주 간단한 예제를 봅시다. 너무 간단할 것이기에 지금 당장은 유용하지 않을 수 있습니다.
하지만 이를 통해 **의존성 주입** 시스템이 어떻게 작동하는지에 중점을 둘 것입니다.
### 의존성 혹은 "디펜더블" 만들기 { #create-a-dependency-or-dependable }
의존성에 집중해 봅시다.
*경로 처리 함수*가 가질 수 있는 모든 매개변수를 갖는 단순한 함수입니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
이게 다입니다.
**단 두 줄입니다**.
그리고, 이 함수는 여러분의 모든 *경로 처리 함수*가 가지고 있는 것과 같은 형태와 구조를 가지고 있습니다.
여러분은 이를 "데코레이터"가 없는 (`@app.get("/some-path")`가 없는) *경로 처리 함수*라고 생각할 수 있습니다.
그리고 여러분이 원하는 무엇이든 반환할 수 있습니다.
이 경우, 이 의존성은 다음과 같은 경우를 기대합니다:
* 선택적인 쿼리 매개변수 `q`, `str`을 자료형으로 가집니다.
* 선택적인 쿼리 매개변수 `skip`, `int`를 자료형으로 가지며 기본 값은 `0`입니다.
* 선택적인 쿼리 매개변수 `limit`, `int`를 자료형으로 가지며 기본 값은 `100`입니다.
그 후 위의 값을 포함한 `dict` 자료형으로 반환할 뿐입니다.
/// info | 정보
FastAPI는 0.95.0 버전부터 `Annotated`에 대한 지원을 (그리고 이를 사용하기 권장합니다) 추가했습니다.
옛날 버전을 가지고 있는 경우, `Annotated`를 사용하려 하면 에러를 맞이하게 될 것입니다.
`Annotated`를 사용하기 전에 최소 0.95.1로 [FastAPI 버전 업그레이드](../../deployment/versions.md#upgrading-the-fastapi-versions)를 확실하게 하세요.
///
### `Depends` 불러오기 { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### "의존자"에 의존성 명시하기 { #declare-the-dependency-in-the-dependant }
*경로 처리 함수*의 매개변수로 `Body`, `Query` 등을 사용하는 방식과 같이 새로운 매개변수로 `Depends`를 사용합니다:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
비록 `Depends`를 함수의 매개변수에서 `Body`, `Query` 등을 사용하는 것과 같은 방식으로 사용하지만, `Depends`는 약간 다르게 작동합니다.
`Depends`에 단일 매개변수만 전달했습니다.
이 매개변수는 함수같은 것이어야 합니다.
여러분은 직접 **호출하지 않았습니다** (끝에 괄호를 치지 않았습니다), 단지 `Depends()`에 매개변수로 넘겨 줬을 뿐입니다.
그리고 그 함수는 *경로 처리 함수*가 작동하는 것과 같은 방식으로 매개변수를 받습니다.
/// tip | 팁
여러분은 다음 장에서 함수를 제외하고서, "다른 것들"이 어떻게 의존성으로 사용되는지 알게 될 것입니다.
///
새로운 요청이 도착할 때마다, **FastAPI**는 다음을 처리합니다:
* 올바른 매개변수를 가진 의존성("디펜더블") 함수를 호출합니다.
* 함수에서 결과를 받아옵니다.
* *경로 처리 함수*에 있는 매개변수에 그 결과를 할당합니다
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
이렇게 하면 공용 코드를 한번만 적어도 되며, **FastAPI**는 *경로 처리*을 위해 이에 대한 호출을 처리합니다.
/// check | 확인
특별한 클래스를 만들지 않아도 되며, 이러한 것 혹은 비슷한 종류를 **FastAPI**에 "등록"하기 위해 어떤 곳에 넘겨주지 않아도 됩니다.
단순히 `Depends`에 넘겨주기만 하면 되며, **FastAPI**는 나머지를 어찌할지 알고 있습니다.
///
## `Annotated`인 의존성 공유하기 { #share-annotated-dependencies }
위의 예제에서 몇몇 작은 **코드 중복**이 있다는 것을 보았을 겁니다.
`common_parameters()`의존을 사용해야 한다면, 타입 명시와 `Depends()`와 함께 전체 매개변수를 적어야 합니다:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
하지만 `Annotated`를 사용하고 있기에, `Annotated` 값을 변수에 저장하고 여러 장소에서 사용할 수 있습니다:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | 팁
이는 그저 표준 파이썬이고 "type alias"라고 부르며 사실 **FastAPI**에 국한되는 것은 아닙니다.
하지만, `Annotated`를 포함하여, **FastAPI**가 파이썬 표준을 기반으로 하고 있기에, 이를 여러분의 코드 트릭으로 사용할 수 있습니다. 😎
///
이 의존성은 계속해서 예상한대로 작동할 것이며, **제일 좋은 부분**은 **타입 정보가 보존된다는 것입니다**. 즉 여러분의 편집기가 **자동 완성**, **인라인 에러** 등을 계속해서 제공할 수 있다는 것입니다. `mypy`같은 다른 도구도 마찬가지입니다.
이는 특히 **많은 *경로 처리***에서 **같은 의존성**을 계속해서 사용하는 **거대 코드 기반**안에서 사용하면 유용할 것입니다.
## `async`하게, 혹은 `async`하지 않게 { #to-async-or-not-to-async }
의존성이 (*경로 처리 함수*에서 처럼 똑같이) **FastAPI**에 의해 호출될 수 있으며, 함수를 정의할 때 동일한 규칙이 적용됩니다.
`async def`을 사용하거나 혹은 일반적인 `def`를 사용할 수 있습니다.
그리고 일반적인 `def` *경로 처리 함수* 안에 `async def`로 의존성을 선언할 수 있으며, `async def` *경로 처리 함수* 안에 `def`로 의존성을 선언하는 등의 방법이 있습니다.
아무 문제 없습니다. **FastAPI**는 무엇을 할지 알고 있습니다.
/// note | 참고
잘 모르시겠다면, [Async: *"In a hurry?"*](../../async.md#in-a-hurry) 문서에서 `async`와 `await`에 대해 확인할 수 있습니다.
///
## OpenAPI와 통합 { #integrated-with-openapi }
모든 요청 선언, 검증과 의존성(및 하위 의존성)에 대한 요구 사항은 동일한 OpenAPI 스키마에 통합됩니다.
따라서 대화형 문서에 이러한 의존성에 대한 모든 정보 역시 포함하고 있습니다:
## 간단한 사용법 { #simple-usage }
이를 보면, *경로 처리 함수*는 *경로*와 *작동*이 매칭되면 언제든지 사용되도록 정의되었으며, **FastAPI**는 올바른 매개변수를 가진 함수를 호출하고 해당 요청에서 데이터를 추출합니다.
사실, 모든 (혹은 대부분의) 웹 프레임워크는 이와 같은 방식으로 작동합니다.
여러분은 이러한 함수들을 절대 직접 호출하지 않습니다. 프레임워크(이 경우 **FastAPI**)에 의해 호출됩니다.
의존성 주입 시스템과 함께라면 **FastAPI**에게 여러분의 *경로 처리 함수*가 실행되기 전에 실행되어야 하는 무언가에 여러분의 *경로 처리 함수* 또한 "의존"하고 있음을 알릴 수 있으며, **FastAPI**는 이를 실행하고 결과를 "주입"할 것입니다.
"의존성 주입"이라는 동일한 아이디어에 대한 다른 일반적인 용어는 다음과 같습니다:
* 리소스
* 제공자
* 서비스
* 인젝터블
* 컴포넌트
## **FastAPI** 플러그인 { #fastapi-plug-ins }
통합과 "플러그인"은 **의존성 주입** 시스템을 사용하여 구축할 수 있습니다. 하지만 실제로 **"플러그인"을 만들 필요는 없습니다**, 왜냐하면 의존성을 사용함으로써 여러분의 *경로 처리 함수*에 통합과 상호 작용을 무한대로 선언할 수 있기 때문입니다.
그리고 "말 그대로", 그저 필요로 하는 파이썬 패키지를 임포트하고 단 몇 줄의 코드로 여러분의 API 함수와 통합함으로써, 의존성을 아주 간단하고 직관적인 방법으로 만들 수 있습니다.
관계형 및 NoSQL 데이터베이스, 보안 등, 이에 대한 예시를 다음 장에서 볼 수 있습니다.
## **FastAPI** 호환성 { #fastapi-compatibility }
의존성 주입 시스템의 단순함은 **FastAPI**를 다음과 같은 요소들과 호환할 수 있게 합니다:
* 모든 관계형 데이터베이스
* NoSQL 데이터베이스
* 외부 패키지
* 외부 API
* 인증 및 권한 부여 시스템
* API 사용 모니터링 시스템
* 응답 데이터 주입 시스템
* 기타 등등.
## 간편하고 강력하다 { #simple-and-powerful }
계층적인 의존성 주입 시스템은 정의하고 사용하기 쉽지만, 여전히 매우 강력합니다.
여러분은 스스로를 의존하는 의존성을 정의할 수 있습니다.
끝에는, 계층적인 나무로 된 의존성이 만들어지며, 그리고 **의존성 주입** 시스템은 (하위 의존성도 마찬가지로) 이러한 의존성들을 처리하고 각 단계마다 결과를 제공합니다(주입합니다).
예를 들면, 여러분이 4개의 API 엔드포인트(*경로 처리*)를 가지고 있다고 해봅시다:
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
그 다음 각각에 대해 그저 의존성과 하위 의존성을 사용하여 다른 권한 요구 사항을 추가할 수 있을 겁니다:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## **OpenAPI**와의 통합 { #integrated-with-openapi_1 }
이 모든 의존성은 각각의 요구사항을 선언하는 동시에, *경로 처리*에 매개변수, 검증 등을 추가합니다.
**FastAPI**는 이 모든 것을 OpenAPI 스키마에 추가할 것이며, 이를 통해 대화형 문서 시스템에 나타날 것입니다.
================================================
FILE: docs/ko/docs/tutorial/dependencies/sub-dependencies.md
================================================
# 하위 의존성 { #sub-dependencies }
**하위 의존성**을 가지는 의존성을 만들 수 있습니다.
필요한 만큼 **깊게** 중첩할 수도 있습니다.
이것을 해결하는 일은 **FastAPI**가 알아서 처리합니다.
## 첫 번째 의존성 "dependable" { #first-dependency-dependable }
다음과 같이 첫 번째 의존성("dependable")을 만들 수 있습니다:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
이 의존성은 선택적 쿼리 파라미터 `q`를 `str`로 선언하고, 그대로 반환합니다.
매우 단순한 예시(그다지 유용하진 않음)이지만, 하위 의존성이 어떻게 동작하는지에 집중하는 데 도움이 됩니다.
## 두 번째 의존성 "dependable"과 "dependant" { #second-dependency-dependable-and-dependant }
그다음, 또 다른 의존성 함수("dependable")를 만들 수 있는데, 이 함수는 동시에 자기 자신의 의존성도 선언합니다(그래서 "dependant"이기도 합니다):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
선언된 파라미터를 살펴보겠습니다:
* 이 함수 자체가 의존성("dependable")이지만, 다른 의존성도 하나 선언합니다(즉, 다른 무언가에 "의존"합니다).
* `query_extractor`에 의존하며, 그 반환값을 파라미터 `q`에 할당합니다.
* 또한 선택적 `last_query` 쿠키를 `str`로 선언합니다.
* 사용자가 쿼리 `q`를 제공하지 않았다면, 이전에 쿠키에 저장해 둔 마지막 쿼리를 사용합니다.
## 의존성 사용하기 { #use-the-dependency }
그다음 다음과 같이 의존성을 사용할 수 있습니다:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | 정보
*경로 처리 함수*에서는 `query_or_cookie_extractor`라는 의존성 하나만 선언하고 있다는 점에 주목하세요.
하지만 **FastAPI**는 `query_or_cookie_extractor`를 호출하는 동안 그 결과를 전달하기 위해, 먼저 `query_extractor`를 해결해야 한다는 것을 알고 있습니다.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## 같은 의존성을 여러 번 사용하기 { #using-the-same-dependency-multiple-times }
같은 *경로 처리*에 대해 의존성 중 하나가 여러 번 선언되는 경우(예: 여러 의존성이 공통 하위 의존성을 갖는 경우), **FastAPI**는 그 하위 의존성을 요청당 한 번만 호출해야 한다는 것을 알고 있습니다.
그리고 같은 요청에 대해 동일한 의존성을 여러 번 호출하는 대신, 반환값을 "캐시"에 저장하고, 그 요청에서 해당 값이 필요한 모든 "dependants"에 전달합니다.
고급 시나리오로, 같은 요청에서 "cached" 값을 쓰는 대신 매 단계마다(아마도 여러 번) 의존성이 호출되어야 한다는 것을 알고 있다면, `Depends`를 사용할 때 `use_cache=False` 파라미터를 설정할 수 있습니다:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ 비 Annotated
/// tip | 팁
가능하다면 `Annotated` 버전을 사용하는 것을 권장합니다.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## 정리 { #recap }
여기서 사용한 그럴듯한 용어들을 제외하면, **Dependency Injection** 시스템은 꽤 단순합니다.
*경로 처리 함수*와 같은 형태의 함수들일 뿐입니다.
하지만 여전히 매우 강력하며, 임의로 깊게 중첩된 의존성 "그래프"(트리)를 선언할 수 있습니다.
/// tip | 팁
이 단순한 예시만 보면 그다지 유용해 보이지 않을 수도 있습니다.
하지만 **보안**에 관한 챕터에서 이것이 얼마나 유용한지 보게 될 것입니다.
또한 얼마나 많은 코드를 아껴주는지도 보게 될 것입니다.
///
================================================
FILE: docs/ko/docs/tutorial/encoder.md
================================================
# JSON 호환 가능 인코더 { #json-compatible-encoder }
데이터 유형(예: Pydantic 모델)을 JSON과 호환되는 형태(예: `dict`, `list` 등)로 변환해야 하는 경우가 있습니다.
예를 들면, 데이터베이스에 저장해야 하는 경우입니다.
이를 위해, **FastAPI**에서는 `jsonable_encoder()` 함수를 제공합니다.
## `jsonable_encoder` 사용 { #using-the-jsonable-encoder }
JSON 호환 가능 데이터만 수신하는 `fake_db` 데이터베이스가 존재한다고 가정하겠습니다.
예를 들면, `datetime` 객체는 JSON과 호환되지 않으므로 이 데이터베이스는 이를 받지 않습니다.
따라서 `datetime` 객체는 [ISO 형식](https://en.wikipedia.org/wiki/ISO_8601)의 데이터를 포함하는 `str`로 변환되어야 합니다.
같은 방식으로 이 데이터베이스는 Pydantic 모델(속성이 있는 객체)을 받지 않고, `dict`만을 받습니다.
이를 위해 `jsonable_encoder`를 사용할 수 있습니다.
Pydantic 모델 같은 객체를 받고 JSON 호환 가능한 버전을 반환합니다:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
이 예시에서는 Pydantic 모델을 `dict`로, `datetime`을 `str`로 변환합니다.
이렇게 호출한 결과는 파이썬 표준인 [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps)로 인코딩할 수 있습니다.
JSON 형식(문자열)의 데이터가 들어있는 큰 `str`을 반환하지 않습니다. JSON과 모두 호환되는 값과 하위 값이 있는 파이썬 표준 데이터 구조(예: `dict`)를 반환합니다.
/// note | 참고
`jsonable_encoder`는 실제로 **FastAPI**에서 내부적으로 데이터를 변환하는 데 사용하지만, 다른 많은 시나리오에서도 유용합니다.
///
================================================
FILE: docs/ko/docs/tutorial/extra-data-types.md
================================================
# 추가 데이터 자료형 { #extra-data-types }
지금까지 일반적인 데이터 자료형을 사용했습니다. 예를 들면 다음과 같습니다:
* `int`
* `float`
* `str`
* `bool`
하지만 더 복잡한 데이터 자료형 또한 사용할 수 있습니다.
그리고 지금까지와 같은 기능들을 여전히 사용할 수 있습니다.
* 훌륭한 편집기 지원.
* 들어오는 요청의 데이터 변환.
* 응답 데이터의 데이터 변환.
* 데이터 검증.
* 자동 어노테이션과 문서화.
## 다른 데이터 자료형 { #other-data-types }
아래의 추가적인 데이터 자료형을 사용할 수 있습니다:
* `UUID`:
* 표준 "범용 고유 식별자"로, 많은 데이터베이스와 시스템에서 ID로 사용됩니다.
* 요청과 응답에서 `str`로 표현됩니다.
* `datetime.datetime`:
* 파이썬의 `datetime.datetime`.
* 요청과 응답에서 `2008-09-15T15:53:00+05:00`와 같은 ISO 8601 형식의 `str`로 표현됩니다.
* `datetime.date`:
* 파이썬의 `datetime.date`.
* 요청과 응답에서 `2008-09-15`와 같은 ISO 8601 형식의 `str`로 표현됩니다.
* `datetime.time`:
* 파이썬의 `datetime.time`.
* 요청과 응답에서 `14:23:55.003`와 같은 ISO 8601 형식의 `str`로 표현됩니다.
* `datetime.timedelta`:
* 파이썬의 `datetime.timedelta`.
* 요청과 응답에서 전체 초(seconds)의 `float`로 표현됩니다.
* Pydantic은 "ISO 8601 time diff encoding"으로 표현하는 것 또한 허용합니다. [더 많은 정보는 문서를 확인하세요](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* 요청과 응답에서 `set`와 동일하게 취급됩니다:
* 요청 시, 리스트를 읽어 중복을 제거하고 `set`로 변환합니다.
* 응답 시, `set`는 `list`로 변환됩니다.
* 생성된 스키마는 (JSON 스키마의 `uniqueItems`를 이용해) `set`의 값이 고유함을 명시합니다.
* `bytes`:
* 표준 파이썬의 `bytes`.
* 요청과 응답에서 `str`로 취급됩니다.
* 생성된 스키마는 이것이 `binary` "형식"의 `str`임을 명시합니다.
* `Decimal`:
* 표준 파이썬의 `Decimal`.
* 요청과 응답에서 `float`와 동일하게 다뤄집니다.
* 여기에서 모든 유효한 Pydantic 데이터 자료형을 확인할 수 있습니다: [Pydantic 데이터 자료형](https://docs.pydantic.dev/latest/usage/types/types/).
## 예시 { #example }
위의 몇몇 자료형을 매개변수로 사용하는 *경로 처리* 예시입니다.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
함수 안의 매개변수가 그들만의 데이터 자료형을 가지고 있으며, 예를 들어, 다음과 같이 날짜를 조작할 수 있음을 참고하십시오:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/ko/docs/tutorial/extra-models.md
================================================
# 추가 모델 { #extra-models }
지난 예제에 이어서, 연관된 모델을 여러 개 갖는 것은 흔한 일입니다.
특히 사용자 모델의 경우에 그러한데, 왜냐하면:
* **입력 모델**은 비밀번호를 가질 수 있어야 합니다.
* **출력 모델**은 비밀번호를 가지면 안 됩니다.
* **데이터베이스 모델**은 아마도 해시 처리된 비밀번호를 가질 필요가 있을 것입니다.
/// danger
절대 사용자의 비밀번호를 평문으로 저장하지 마세요. 항상 이후에 검증 가능한 "안전한 해시(secure hash)"로 저장하세요.
만약 이게 무엇인지 모르겠다면, [보안 장](security/simple-oauth2.md#password-hashing)에서 "password hash"가 무엇인지 배울 수 있습니다.
///
## 다중 모델 { #multiple-models }
아래는 비밀번호 필드와 해당 필드가 사용되는 위치를 포함하여, 각 모델들이 어떤 형태를 가질 수 있는지 전반적인 예시입니다:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### `**user_in.model_dump()` 에 대하여 { #about-user-in-model-dump }
#### Pydantic의 `.model_dump()` { #pydantics-model-dump }
`user_in`은 Pydantic 모델 클래스인 `UserIn`입니다.
Pydantic 모델은 모델 데이터를 포함한 `dict`를 반환하는 `.model_dump()` 메서드를 제공합니다.
따라서, 다음과 같이 Pydantic 객체 `user_in`을 생성할 수 있습니다:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
그 다음, 다음과 같이 호출합니다:
```Python
user_dict = user_in.model_dump()
```
이제 변수 `user_dict`에 데이터가 포함된 `dict`를 가지게 됩니다(이는 Pydantic 모델 객체가 아닌 `dict`입니다).
그리고 다음과 같이 호출하면:
```Python
print(user_dict)
```
Python의 `dict`가 다음과 같이 출력됩니다:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### `dict` 언패킹 { #unpacking-a-dict }
`user_dict`와 같은 `dict`를 함수(또는 클래스)에 `**user_dict`로 전달하면, Python은 이를 "언팩(unpack)"합니다. 이 과정에서 `user_dict`의 키와 값을 각각 키-값 인자로 직접 전달합니다.
따라서, 위에서 생성한 `user_dict`를 사용하여 다음과 같이 작성하면:
```Python
UserInDB(**user_dict)
```
다음과 같은 결과를 생성합니다:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
혹은 더 정확히 말하자면, `user_dict`를 직접 사용하는 것은, 나중에 어떤 값이 추가되더라도 아래와 동일한 효과를 냅니다:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### 다른 모델 데이터로 새 Pydantic 모델 생성 { #a-pydantic-model-from-the-contents-of-another }
위의 예제에서 `user_in.model_dump()`로부터 `user_dict`를 생성한 것처럼, 아래 코드는:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
다음과 동일합니다:
```Python
UserInDB(**user_in.model_dump())
```
...왜냐하면 `user_in.model_dump()`는 `dict`이며, 이를 `**`로 Python이 "언팩(unpack)"하도록 하여 `UserInDB`에 전달하기 때문입니다.
따라서, 다른 Pydantic 모델의 데이터를 사용하여 새로운 Pydantic 모델을 생성할 수 있습니다.
#### `dict` 언패킹과 추가 키워드 { #unpacking-a-dict-and-extra-keywords }
그리고 다음과 같이 추가 키워드 인자 `hashed_password=hashed_password`를 추가하면:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
다음과 같은 결과를 생성합니다:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning
추가적으로 제공된 함수 `fake_password_hasher`와 `fake_save_user`는 데이터 흐름을 시연하기 위한 예제일 뿐이며, 실제 보안을 제공하지 않습니다.
///
## 중복 줄이기 { #reduce-duplication }
코드 중복을 줄이는 것은 **FastAPI**의 핵심 아이디어 중 하나입니다.
코드 중복은 버그, 보안 문제, 코드 비동기화 문제(한 곳은 업데이트되었지만 다른 곳은 업데이트되지 않는 문제) 등의 가능성을 증가시킵니다.
그리고 이 모델들은 많은 데이터를 공유하면서 속성 이름과 타입을 중복하고 있습니다.
더 나은 방법이 있습니다.
`UserBase` 모델을 선언하여 다른 모델들의 기본(base)으로 사용할 수 있습니다. 그런 다음 이 모델을 상속받아 속성과 타입 선언(유형 선언, 검증 등)을 상속하는 서브클래스를 만들 수 있습니다.
모든 데이터 변환, 검증, 문서화 등은 정상적으로 작동할 것입니다.
이렇게 하면 각 모델 간의 차이점만 선언할 수 있습니다(평문 `password`, `hashed_password`, 그리고 비밀번호가 없는 경우):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` 또는 `anyOf` { #union-or-anyof }
두 가지 이상의 타입을 포함하는 `Union`으로 응답을 선언할 수 있습니다. 이는 응답이 그 중 하나의 타입일 수 있음을 의미합니다.
OpenAPI에서는 이를 `anyOf`로 정의합니다.
이를 위해 표준 Python 타입 힌트인 [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union)을 사용할 수 있습니다:
/// note
[`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions)을 정의할 때는 더 구체적인 타입을 먼저 포함하고, 덜 구체적인 타입을 그 뒤에 나열해야 합니다. 아래 예제에서는 `Union[PlaneItem, CarItem]`에서 더 구체적인 `PlaneItem`이 `CarItem`보다 앞에 위치합니다.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### Python 3.10에서 `Union` { #union-in-python-3-10 }
위의 예제에서는 `response_model` 인자 값으로 `Union[PlaneItem, CarItem]`을 전달합니다.
이 경우, 이를 **타입 어노테이션(type annotation)**이 아닌 **인자 값(argument value)**으로 전달하고 있기 때문에 Python 3.10에서도 `Union`을 사용해야 합니다.
만약 타입 어노테이션에 사용한다면, 다음과 같이 수직 막대(|)를 사용할 수 있습니다:
```Python
some_variable: PlaneItem | CarItem
```
하지만 이를 `response_model=PlaneItem | CarItem`과 같이 할당하면 에러가 발생합니다. 이는 Python이 이를 타입 어노테이션으로 해석하지 않고, `PlaneItem`과 `CarItem` 사이의 **잘못된 연산(invalid operation)**을 시도하기 때문입니다.
## 모델 리스트 { #list-of-models }
마찬가지로, 객체 리스트 형태의 응답을 선언할 수도 있습니다.
이를 위해 표준 Python의 `list`를 사용하세요:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## 임의의 `dict` 응답 { #response-with-arbitrary-dict }
Pydantic 모델을 사용하지 않고, 키와 값의 타입만 선언하여 평범한 임의의 `dict`로 응답을 선언할 수도 있습니다.
이는 Pydantic 모델에 필요한 유효한 필드/속성 이름을 사전에 알 수 없는 경우에 유용합니다.
이 경우, `dict`를 사용할 수 있습니다:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## 요약 { #recap }
여러 Pydantic 모델을 사용하고, 각 경우에 맞게 자유롭게 상속하세요.
엔터티가 서로 다른 "상태"를 가져야 하는 경우, 엔터티당 단일 데이터 모델을 사용할 필요는 없습니다. 예를 들어, 사용자 "엔터티"가 `password`, `password_hash`, 그리고 비밀번호가 없는 상태를 포함할 수 있는 경우처럼 말입니다.
================================================
FILE: docs/ko/docs/tutorial/first-steps.md
================================================
# 첫걸음 { #first-steps }
가장 단순한 FastAPI 파일은 다음과 같이 보일 것입니다:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
위 코드를 `main.py`에 복사합니다.
라이브 서버를 실행합니다:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
출력되는 줄들 중에는 아래와 같은 내용이 있습니다:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
해당 줄은 로컬 머신에서 앱이 서비스되는 URL을 보여줍니다.
### 확인하기 { #check-it }
브라우저로 [http://127.0.0.1:8000](http://127.0.0.1:8000)를 여세요.
아래와 같은 JSON 응답을 볼 수 있습니다:
```JSON
{"message": "Hello World"}
```
### 대화형 API 문서 { #interactive-api-docs }
이제 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)로 가봅니다.
자동 대화형 API 문서를 볼 수 있습니다 ([Swagger UI](https://github.com/swagger-api/swagger-ui) 제공):

### 대안 API 문서 { #alternative-api-docs }
그리고 이제, [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)로 가봅니다.
대안 자동 문서를 볼 수 있습니다 ([ReDoc](https://github.com/Rebilly/ReDoc) 제공):

### OpenAPI { #openapi }
**FastAPI**는 API를 정의하기 위한 **OpenAPI** 표준을 사용하여 여러분의 모든 API를 이용해 "스키마"를 생성합니다.
#### "스키마" { #schema }
"스키마"는 무언가의 정의 또는 설명입니다. 이를 구현하는 코드가 아니라 추상적인 설명일 뿐입니다.
#### API "스키마" { #api-schema }
이 경우, [OpenAPI](https://github.com/OAI/OpenAPI-Specification)는 여러분의 API 스키마를 어떻게 정의하는지 지시하는 규격입니다.
이 스키마 정의는 API 경로, 가능한 매개변수 등을 포함합니다.
#### 데이터 "스키마" { #data-schema }
"스키마"라는 용어는 JSON처럼 어떤 데이터의 형태를 나타낼 수도 있습니다.
이러한 경우 JSON 속성, 가지고 있는 데이터 타입 등을 뜻합니다.
#### OpenAPI와 JSON 스키마 { #openapi-and-json-schema }
OpenAPI는 여러분의 API에 대한 API 스키마를 정의합니다. 또한 이 스키마는 JSON 데이터 스키마의 표준인 **JSON 스키마**를 사용하여 여러분의 API가 보내고 받는 데이터의 정의(또는 "스키마")를 포함합니다.
#### `openapi.json` 확인 { #check-the-openapi-json }
가공되지 않은 OpenAPI 스키마가 어떻게 생겼는지 궁금하다면, FastAPI는 자동으로 여러분의 모든 API에 대한 설명과 함께 JSON (스키마)를 생성합니다.
여기에서 직접 볼 수 있습니다: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
다음과 같이 시작하는 JSON을 확인할 수 있습니다:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### OpenAPI의 용도 { #what-is-openapi-for }
OpenAPI 스키마는 포함된 두 개의 대화형 문서 시스템을 제공합니다.
그리고 OpenAPI의 모든 것을 기반으로 하는 수십 가지 대안이 있습니다. **FastAPI**로 빌드한 애플리케이션에 이러한 대안을 쉽게 추가 할 수 있습니다.
API와 통신하는 클라이언트(프론트엔드, 모바일, IoT 애플리케이션 등)를 위해 코드를 자동으로 생성하는 데도 사용할 수 있습니다.
### `pyproject.toml`에 앱 `entrypoint` 구성하기 { #configure-the-app-entrypoint-in-pyproject-toml }
다음과 같이 `pyproject.toml` 파일에서 앱이 위치한 곳을 구성할 수 있습니다:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
해당 `entrypoint`는 `fastapi` 명령어에 다음과 같이 앱을 임포트하라고 알려줍니다:
```python
from main import app
```
코드 구조가 다음과 같다면:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
그럼 `entrypoint`를 다음과 같이 설정합니다:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
이는 다음과 동일합니다:
```python
from backend.main import app
```
### `fastapi dev`에 경로 지정하기 { #fastapi-dev-with-path }
`fastapi dev` 명령어에 파일 경로를 전달할 수도 있으며, 그러면 사용할 FastAPI app 객체를 추정합니다:
```console
$ fastapi dev main.py
```
하지만 매번 `fastapi` 명령어를 호출할 때마다 올바른 경로를 전달해야 합니다.
또한 다른 도구들, 예를 들어 [VS Code 확장](../editor-support.md)이나 [FastAPI Cloud](https://fastapicloud.com)가 이를 찾지 못할 수 있으므로, `pyproject.toml`의 `entrypoint`를 사용하는 것을 권장합니다.
### 앱 배포하기(선택 사항) { #deploy-your-app-optional }
선택적으로 FastAPI 앱을 [FastAPI Cloud](https://fastapicloud.com)에 배포할 수 있습니다. 아직 대기자 명단에 등록하지 않았다면, 등록하러 가세요. 🚀
이미 **FastAPI Cloud** 계정이 있다면(대기자 명단에서 초대해 드렸습니다 😉), 한 번의 명령으로 애플리케이션을 배포할 수 있습니다.
배포하기 전에, 로그인되어 있는지 확인하세요:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
그 다음 앱을 배포합니다:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
이게 전부입니다! 이제 해당 URL에서 앱에 접근할 수 있습니다. ✨
## 단계별 요약 { #recap-step-by-step }
### 1 단계: `FastAPI` 임포트 { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI`는 여러분의 API를 위한 모든 기능을 제공하는 파이썬 클래스입니다.
/// note | 기술 세부사항
`FastAPI`는 `Starlette`를 직접 상속하는 클래스입니다.
`FastAPI`로 [Starlette](https://www.starlette.dev/)의 모든 기능을 사용할 수 있습니다.
///
### 2 단계: `FastAPI` "인스턴스" 생성 { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
여기에서 `app` 변수는 `FastAPI` 클래스의 "인스턴스"가 됩니다.
이것은 여러분의 모든 API를 생성하기 위한 상호작용의 주요 지점이 될 것입니다.
### 3 단계: *경로 처리* 생성 { #step-3-create-a-path-operation }
#### 경로 { #path }
여기서 "경로"는 첫 번째 `/`부터 시작하는 URL의 뒷부분을 의미합니다.
그러므로 아래와 같은 URL에서:
```
https://example.com/items/foo
```
...경로는 다음과 같습니다:
```
/items/foo
```
/// info | 정보
"경로"는 일반적으로 "엔드포인트" 또는 "라우트"라고도 불립니다.
///
API를 설계할 때 "경로"는 "관심사"와 "리소스"를 분리하기 위한 주요한 방법입니다.
#### 작동 { #operation }
"작동(Operation)"은 HTTP "메소드" 중 하나를 나타냅니다.
다음 중 하나이며:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...흔히 사용되지 않는 것들도 있습니다:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
HTTP 프로토콜에서는 이러한 "메소드"를 하나(또는 이상) 사용하여 각 경로와 통신할 수 있습니다.
---
API를 설계할 때 일반적으로 특정 행동을 수행하기 위해 특정 HTTP 메소드를 사용합니다.
일반적으로 다음과 같습니다:
* `POST`: 데이터를 생성하기 위해.
* `GET`: 데이터를 읽기 위해.
* `PUT`: 데이터를 수정하기 위해.
* `DELETE`: 데이터를 삭제하기 위해.
그래서 OpenAPI에서는 각 HTTP 메소드들을 "작동"이라 부릅니다.
우리 역시 이제부터 메소드를 "**작동**"이라고 부를 것입니다.
#### *경로 처리 데코레이터* 정의 { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
`@app.get("/")`은 **FastAPI**에게 바로 아래에 있는 함수가 다음으로 이동하는 요청을 처리한다는 것을 알려줍니다:
* 경로 `/`
* get 작동 사용
/// info | `@decorator` 정보
이 `@something` 문법은 파이썬에서 "데코레이터"라 부릅니다.
마치 예쁜 장식용(Decorative) 모자처럼(개인적으로 이 용어가 여기서 유래한 것 같습니다) 함수 맨 위에 놓습니다.
"데코레이터"는 아래 있는 함수를 받아 그것으로 무언가를 합니다.
우리의 경우, 이 데코레이터는 **FastAPI**에게 아래 함수가 **경로** `/`의 `get` **작동**에 해당한다고 알려줍니다.
이것이 "**경로 처리 데코레이터**"입니다.
///
다른 작동도 사용할 수 있습니다:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
흔히 사용되지 않는 것들도 있습니다:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | 팁
각 작동(HTTP 메소드)을 원하는 대로 사용해도 됩니다.
**FastAPI**는 특정 의미를 강제하지 않습니다.
여기서 정보는 지침서일뿐 강제사항이 아닙니다.
예를 들어 GraphQL을 사용하는 경우, 일반적으로 `POST` 작동만 사용하여 모든 행동을 수행합니다.
///
### 4 단계: **경로 처리 함수** 정의 { #step-4-define-the-path-operation-function }
다음은 우리의 "**경로 처리 함수**"입니다:
* **경로**: 는 `/`입니다.
* **작동**: 은 `get`입니다.
* **함수**: 는 "데코레이터" 아래에 있는 함수입니다 (`@app.get("/")` 아래).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
이것은 파이썬 함수입니다.
URL "`/`"에 대한 `GET` 작동을 사용하는 요청을 받을 때마다 **FastAPI**에 의해 호출됩니다.
위의 예시에서 이 함수는 `async`(비동기) 함수입니다.
---
`async def`을 이용하는 대신 일반 함수로 정의할 수 있습니다:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | 참고
차이점을 모르겠다면 [Async: *"바쁘신 경우"*](../async.md#in-a-hurry)를 확인하세요.
///
### 5 단계: 콘텐츠 반환 { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
`dict`, `list`, 단일값을 가진 `str`, `int` 등을 반환할 수 있습니다.
Pydantic 모델을 반환할 수도 있습니다(나중에 더 자세히 살펴봅니다).
JSON으로 자동 변환되는 객체들과 모델들(ORM 등을 포함해서)이 많이 있습니다. 가장 마음에 드는 것을 사용하십시오, 이미 지원되고 있을 것입니다.
### 6 단계: 배포하기 { #step-6-deploy-it }
한 번의 명령으로 **[FastAPI Cloud](https://fastapicloud.com)**에 앱을 배포합니다: `fastapi deploy`. 🎉
#### FastAPI Cloud 소개 { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**는 **FastAPI** 뒤에 있는 동일한 작성자와 팀이 만들었습니다.
최소한의 노력으로 API를 **빌드**, **배포**, **접근**하는 과정을 간소화합니다.
FastAPI로 앱을 빌드할 때의 동일한 **개발자 경험**을 클라우드에 **배포**할 때도 제공합니다. 🎉
FastAPI Cloud는 *FastAPI와 친구들* 오픈 소스 프로젝트의 주요 스폰서이자 자금 제공자입니다. ✨
#### 다른 클라우드 제공업체에 배포하기 { #deploy-to-other-cloud-providers }
FastAPI는 오픈 소스이며 표준을 기반으로 합니다. 선택한 어떤 클라우드 제공업체에도 FastAPI 앱을 배포할 수 있습니다.
클라우드 제공업체의 가이드를 따라 FastAPI 앱을 배포하세요. 🤓
## 요약 { #recap }
* `FastAPI` 임포트.
* `app` 인스턴스 생성.
* (`@app.get("/")`처럼) **경로 처리 데코레이터** 작성.
* (위에 있는 `def root(): ...`처럼) **경로 처리 함수** 작성.
* `fastapi dev` 명령으로 개발 서버 실행.
* 선택적으로 `fastapi deploy`로 앱 배포.
================================================
FILE: docs/ko/docs/tutorial/handling-errors.md
================================================
# 오류 처리 { #handling-errors }
API를 사용하는 클라이언트에 오류를 알려야 하는 상황은 많이 있습니다.
이 클라이언트는 프론트엔드가 있는 브라우저일 수도 있고, 다른 사람이 작성한 코드일 수도 있고, IoT 장치일 수도 있습니다.
클라이언트에 다음과 같은 내용을 알려야 할 수도 있습니다:
* 클라이언트가 해당 작업을 수행할 충분한 권한이 없습니다.
* 클라이언트가 해당 리소스에 접근할 수 없습니다.
* 클라이언트가 접근하려고 한 항목이 존재하지 않습니다.
* 등등.
이런 경우 보통 **400**번대(400에서 499) 범위의 **HTTP 상태 코드**를 반환합니다.
이는 200번대 HTTP 상태 코드(200에서 299)와 비슷합니다. "200" 상태 코드는 어떤 형태로든 요청이 "성공"했음을 의미합니다.
400번대 상태 코드는 클라이언트 측에서 오류가 발생했음을 의미합니다.
**"404 Not Found"** 오류(그리고 농담들)도 다들 기억하시죠?
## `HTTPException` 사용하기 { #use-httpexception }
클라이언트에 오류가 포함된 HTTP 응답을 반환하려면 `HTTPException`을 사용합니다.
### `HTTPException` 가져오기 { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### 코드에서 `HTTPException` 발생시키기 { #raise-an-httpexception-in-your-code }
`HTTPException`은 API와 관련된 추가 데이터를 가진 일반적인 Python 예외입니다.
Python 예외이므로 `return` 하는 것이 아니라 `raise` 합니다.
이는 또한, *경로 처리 함수* 내부에서 호출하는 유틸리티 함수 안에서 `HTTPException`을 `raise`하면, *경로 처리 함수*의 나머지 코드는 실행되지 않고 즉시 해당 요청이 종료되며 `HTTPException`의 HTTP 오류가 클라이언트로 전송된다는 뜻입니다.
값을 반환하는 것보다 예외를 발생시키는 것의 이점은 의존성과 보안에 대한 섹션에서 더 분명해집니다.
이 예시에서는, 클라이언트가 존재하지 않는 ID로 항목을 요청하면 상태 코드 `404`로 예외를 발생시킵니다:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### 결과 응답 { #the-resulting-response }
클라이언트가 `http://example.com/items/foo`( `item_id` `"foo"`)를 요청하면, HTTP 상태 코드 200과 다음 JSON 응답을 받습니다:
```JSON
{
"item": "The Foo Wrestlers"
}
```
하지만 클라이언트가 `http://example.com/items/bar`(존재하지 않는 `item_id` `"bar"`)를 요청하면, HTTP 상태 코드 404("not found" 오류)와 다음 JSON 응답을 받습니다:
```JSON
{
"detail": "Item not found"
}
```
/// tip | 팁
`HTTPException`을 발생시킬 때 `detail` 파라미터로 `str`만 전달할 수 있는 것이 아니라, JSON으로 변환할 수 있는 어떤 값이든 전달할 수 있습니다.
`dict`, `list` 등을 전달할 수 있습니다.
이들은 **FastAPI**가 자동으로 처리해 JSON으로 변환합니다.
///
## 커스텀 헤더 추가하기 { #add-custom-headers }
HTTP 오류에 커스텀 헤더를 추가할 수 있으면 유용한 상황이 있습니다. 예를 들어 특정 보안 유형에서 그렇습니다.
아마 코드에서 직접 사용할 일은 거의 없을 것입니다.
하지만 고급 시나리오에서 필요하다면 커스텀 헤더를 추가할 수 있습니다:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## 커스텀 예외 핸들러 설치하기 { #install-custom-exception-handlers }
[Starlette의 동일한 예외 유틸리티](https://www.starlette.dev/exceptions/)를 사용해 커스텀 예외 핸들러를 추가할 수 있습니다.
여러분(또는 사용하는 라이브러리)이 `raise`할 수 있는 커스텀 예외 `UnicornException`이 있다고 가정해 봅시다.
그리고 이 예외를 FastAPI에서 전역적으로 처리하고 싶다고 해봅시다.
`@app.exception_handler()`로 커스텀 예외 핸들러를 추가할 수 있습니다:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
여기서 `/unicorns/yolo`를 요청하면, *경로 처리*가 `UnicornException`을 `raise`합니다.
하지만 `unicorn_exception_handler`가 이를 처리합니다.
따라서 HTTP 상태 코드 `418`과 다음 JSON 내용을 가진 깔끔한 오류를 받게 됩니다:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | 기술 세부사항
`from starlette.requests import Request`와 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로도 동일하게 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 옵니다. `Request`도 마찬가지입니다.
///
## 기본 예외 핸들러 오버라이드하기 { #override-the-default-exception-handlers }
**FastAPI**에는 몇 가지 기본 예외 핸들러가 있습니다.
이 핸들러들은 `HTTPException`을 `raise`했을 때, 그리고 요청에 유효하지 않은 데이터가 있을 때 기본 JSON 응답을 반환하는 역할을 합니다.
이 예외 핸들러들을 여러분의 것으로 오버라이드할 수 있습니다.
### 요청 검증 예외 오버라이드하기 { #override-request-validation-exceptions }
요청에 유효하지 않은 데이터가 포함되면, **FastAPI**는 내부적으로 `RequestValidationError`를 `raise`합니다.
그리고 이에 대한 기본 예외 핸들러도 포함되어 있습니다.
이를 오버라이드하려면 `RequestValidationError`를 가져오고, `@app.exception_handler(RequestValidationError)`로 예외 핸들러를 데코레이트해 사용하세요.
예외 핸들러는 `Request`와 예외를 받습니다.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
이제 `/items/foo`로 이동하면, 다음과 같은 기본 JSON 오류 대신:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
다음과 같은 텍스트 버전을 받게 됩니다:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### `HTTPException` 오류 핸들러 오버라이드하기 { #override-the-httpexception-error-handler }
같은 방식으로 `HTTPException` 핸들러도 오버라이드할 수 있습니다.
예를 들어, 이런 오류들에 대해 JSON 대신 일반 텍스트 응답을 반환하고 싶을 수 있습니다:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | 기술 세부사항
`from starlette.responses import PlainTextResponse`를 사용할 수도 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로도 동일하게 제공합니다. 하지만 사용 가능한 대부분의 응답은 Starlette에서 직접 옵니다.
///
/// warning | 경고
`RequestValidationError`에는 검증 오류가 발생한 파일 이름과 줄 정보가 포함되어 있어, 원한다면 관련 정보와 함께 로그에 표시할 수 있다는 점을 유념하세요.
하지만 이는 단순히 문자열로 변환해 그 정보를 그대로 반환하면 시스템에 대한 일부 정보를 누설할 수 있다는 뜻이기도 합니다. 그래서 여기의 코드는 각 오류를 독립적으로 추출해 보여줍니다.
///
### `RequestValidationError`의 body 사용하기 { #use-the-requestvalidationerror-body }
`RequestValidationError`에는 유효하지 않은 데이터와 함께 받은 `body`가 포함됩니다.
앱을 개발하는 동안 body를 로그로 남기고 디버그하거나, 사용자에게 반환하는 등으로 사용할 수 있습니다.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
이제 다음처럼 유효하지 않은 item을 보내보세요:
```JSON
{
"title": "towel",
"size": "XL"
}
```
받은 body를 포함해 데이터가 유효하지 않다고 알려주는 응답을 받게 됩니다:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### FastAPI의 `HTTPException` vs Starlette의 `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI**에는 자체 `HTTPException`이 있습니다.
그리고 **FastAPI**의 `HTTPException` 오류 클래스는 Starlette의 `HTTPException` 오류 클래스를 상속합니다.
유일한 차이는 **FastAPI**의 `HTTPException`은 `detail` 필드에 JSON으로 변환 가능한 어떤 데이터든 받을 수 있는 반면, Starlette의 `HTTPException`은 문자열만 받을 수 있다는 점입니다.
따라서 코드에서는 평소처럼 **FastAPI**의 `HTTPException`을 계속 `raise`하면 됩니다.
하지만 예외 핸들러를 등록할 때는 Starlette의 `HTTPException`에 대해 등록해야 합니다.
이렇게 하면 Starlette 내부 코드의 어떤 부분, 또는 Starlette 확장/플러그인이 Starlette `HTTPException`을 `raise`하더라도, 여러분의 핸들러가 이를 잡아서 처리할 수 있습니다.
이 예시에서는 동일한 코드에서 두 `HTTPException`을 모두 사용할 수 있도록, Starlette의 예외를 `StarletteHTTPException`으로 이름을 바꿉니다:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### **FastAPI**의 예외 핸들러 재사용하기 { #reuse-fastapis-exception-handlers }
예외를 사용하면서 **FastAPI**의 동일한 기본 예외 핸들러도 함께 사용하고 싶다면, `fastapi.exception_handlers`에서 기본 예외 핸들러를 가져와 재사용할 수 있습니다:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
이 예시에서는 매우 표현력 있는 메시지로 오류를 출력만 하고 있지만, 요지는 이해하셨을 겁니다. 예외를 사용한 뒤 기본 예외 핸들러를 그대로 재사용할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/header-param-models.md
================================================
# 헤더 매개변수 모델 { #header-parameter-models }
관련 있는 **헤더 매개변수** 그룹이 있는 경우, **Pydantic 모델**을 생성하여 선언할 수 있습니다.
이를 통해 **여러 위치**에서 **모델을 재사용할** 수 있고 모든 매개변수에 대한 유효성 검사 및 메타데이터를 한 번에 선언할 수도 있습니다. 😎
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0` 이후부터 지원됩니다. 🤓
///
## Pydantic 모델을 사용한 헤더 매개변수 { #header-parameters-with-a-pydantic-model }
**Pydantic 모델**에 필요한 **헤더 매개변수**를 선언한 다음, 해당 매개변수를 `Header`로 선언합니다:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI**는 요청에서 받은 **헤더**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 필요한 헤더를 볼 수 있습니다:
## 추가 헤더 금지하기 { #forbid-extra-headers }
일부 특별한 사용 사례(흔하지는 않겠지만)에서는 수신하려는 헤더를 **제한**할 수 있습니다.
Pydantic의 모델 구성을 사용하여 추가(`extra`) 필드를 금지(`forbid`)할 수 있습니다:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
클라이언트가 **추가 헤더**를 보내려고 시도하면, **오류** 응답을 받게 됩니다.
예를 들어, 클라이언트가 `plumbus` 값으로 `tool` 헤더를 보내려고 하면, 클라이언트는 헤더 매개변수 `tool`이 허용되지 않는다는 **오류** 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## 밑줄 변환 비활성화하기 { #disable-convert-underscores }
일반적인 헤더 매개변수와 마찬가지로, 매개변수 이름에 밑줄 문자가 있으면 **자동으로 하이픈으로 변환**됩니다.
예를 들어, 코드에 `save_data` 헤더 매개변수가 있으면, 기대되는 HTTP 헤더는 `save-data`이고, 문서에서도 그렇게 표시됩니다.
어떤 이유로든 이 자동 변환을 비활성화해야 한다면, 헤더 매개변수용 Pydantic 모델에서도 비활성화할 수 있습니다.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | 경고
`convert_underscores`를 `False`로 설정하기 전에, 일부 HTTP 프록시와 서버에서는 밑줄이 포함된 헤더 사용을 허용하지 않는다는 점을 염두에 두세요.
///
## 요약 { #summary }
**Pydantic 모델**을 사용하여 **FastAPI**에서 **헤더**를 선언할 수 있습니다. 😎
================================================
FILE: docs/ko/docs/tutorial/header-params.md
================================================
# 헤더 매개변수 { #header-parameters }
헤더 매개변수를 `Query`, `Path` 그리고 `Cookie` 매개변수들과 같은 방식으로 정의할 수 있습니다.
## `Header` 임포트 { #import-header }
먼저 `Header`를 임포트합니다:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## `Header` 매개변수 선언 { #declare-header-parameters }
`Path`, `Query` 그리고 `Cookie`를 사용한 동일한 구조를 이용하여 헤더 매개변수를 선언합니다.
첫 번째 값은 기본값이며, 추가 검증이나 어노테이션 매개변수 모두 전달할 수 있습니다:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | 기술 세부사항
`Header`는 `Path`, `Query` 및 `Cookie`의 "자매"클래스입니다. 이 역시 동일한 공통 `Param` 클래스를 상속합니다.
`Query`, `Path`, `Header` 그리고 다른 것들을 `fastapi`에서 임포트 할 때, 이들은 실제로 특별한 클래스를 반환하는 함수임을 기억하세요.
///
/// info | 정보
헤더를 선언하기 위해서 `Header`를 사용해야 합니다. 그렇지 않으면 해당 매개변수를 쿼리 매개변수로 해석하기 때문입니다.
///
## 자동 변환 { #automatic-conversion }
`Header`는 `Path`, `Query` 그리고 `Cookie`가 제공하는 것 외에 기능이 조금 더 있습니다.
대부분의 표준 헤더는 "마이너스 기호" (`-`)라고도 하는 "하이픈" 문자로 구분됩니다.
그러나 파이썬에서 `user-agent`와 같은 형태의 변수는 유효하지 않습니다.
따라서 `Header`는 기본적으로 매개변수 이름을 언더스코어(`_`)에서 하이픈(`-`)으로 변환하여 헤더를 추출하고 기록합니다.
또한 HTTP 헤더는 대소문자를 구분하지 않으므로 "snake_case"로 알려진 표준 파이썬 스타일로 선언할 수 있습니다.
따라서, `User_Agent` 등과 같이 첫 문자를 대문자화할 필요없이 파이썬 코드에서처럼 `user_agent`로 사용합니다.
만약 언더스코어를 하이픈으로 자동 변환을 비활성화해야 할 어떤 이유가 있다면, `Header`의 `convert_underscores` 매개변수를 `False`로 설정하십시오:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | 경고
`convert_underscores`를 `False`로 설정하기 전에, 어떤 HTTP 프록시들과 서버들은 언더스코어가 포함된 헤더 사용을 허락하지 않는다는 것을 명심하십시오.
///
## 중복 헤더 { #duplicate-headers }
중복 헤더들을 수신할 수 있습니다. 즉, 다중값을 갖는 동일한 헤더를 뜻합니다.
타입 정의에서 리스트를 사용하여 이러한 케이스를 정의할 수 있습니다.
중복 헤더의 모든 값을 파이썬 `list`로 수신합니다.
예를 들어, 두 번 이상 나타날 수 있는 `X-Token`헤더를 선언하려면, 다음과 같이 작성합니다:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
다음과 같은 두 개의 HTTP 헤더를 전송하여 해당 *경로 처리* 와 통신할 경우:
```
X-Token: foo
X-Token: bar
```
응답은 다음과 같습니다:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## 요약 { #recap }
`Header`는 `Query`, `Path`, `Cookie`와 동일한 패턴을 사용하여 선언합니다.
변수의 언더스코어를 걱정하지 마십시오, **FastAPI**가 변수를 변환할 것입니다.
================================================
FILE: docs/ko/docs/tutorial/index.md
================================================
# 자습서 - 사용자 안내서 { #tutorial-user-guide }
이 자습서는 **FastAPI**의 대부분의 기능을 단계별로 사용하는 방법을 보여줍니다.
각 섹션은 이전 섹션을 바탕으로 점진적으로 구성되지만, 주제를 분리한 구조로 되어 있어 특정 API 요구사항을 해결하기 위해 원하는 섹션으로 바로 이동할 수 있습니다.
또한 나중에 참고 자료로도 사용할 수 있도록 만들어졌으므로, 필요할 때 다시 돌아와 정확히 필요한 내용을 확인할 수 있습니다.
## 코드 실행하기 { #run-the-code }
모든 코드 블록은 복사해서 바로 사용할 수 있습니다(실제로 테스트된 Python 파일입니다).
예제 중 어떤 것이든 실행하려면, 코드를 `main.py` 파일에 복사하고 다음으로 `fastapi dev`를 시작하세요:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
코드를 작성하거나 복사한 뒤 편집하고, 로컬에서 실행하는 것을 **강력히 권장**합니다.
에디터에서 사용해 보면, 작성해야 하는 코드가 얼마나 적은지, 모든 타입 검사와 자동완성 등 FastAPI의 이점을 제대로 확인할 수 있습니다.
---
## FastAPI 설치 { #install-fastapi }
첫 단계는 FastAPI를 설치하는 것입니다.
[가상 환경](../virtual-environments.md)을 생성하고 활성화한 다음, **FastAPI를 설치**하세요:
/// note | 참고
`pip install "fastapi[standard]"`로 설치하면 `fastapi-cloud-cli`를 포함한 몇 가지 기본 선택적 standard 의존성이 함께 설치되며, 이를 사용해 [FastAPI Cloud](https://fastapicloud.com)에 배포할 수 있습니다.
이러한 선택적 의존성이 필요 없다면 `pip install fastapi`로 대신 설치할 수 있습니다.
standard 의존성은 설치하되 `fastapi-cloud-cli` 없이 설치하려면 `pip install "fastapi[standard-no-fastapi-cloud-cli]"`로 설치할 수 있습니다.
///
/// tip | 팁
FastAPI는 VS Code(및 Cursor)용 [공식 확장 프로그램](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode)이 있습니다. 경로 처리 탐색기, 경로 처리 검색, 테스트에서의 CodeLens 탐색(테스트에서 정의로 바로 이동), FastAPI Cloud 배포와 로그 등 많은 기능을 에디터에서 바로 제공합니다.
///
## 고급 사용자 안내서 { #advanced-user-guide }
이 **자습서 - 사용자 안내서**를 읽은 뒤에 나중에 읽을 수 있는 **고급 사용자 안내서**도 있습니다.
**고급 사용자 안내서**는 이 문서를 바탕으로 동일한 개념을 사용하며, 몇 가지 추가 기능을 알려줍니다.
하지만 먼저 **자습서 - 사용자 안내서**(지금 읽고 있는 내용)를 읽어야 합니다.
**자습서 - 사용자 안내서**만으로 완전한 애플리케이션을 만들 수 있도록 설계되었고, 필요에 따라 **고급 사용자 안내서**의 추가 아이디어를 활용해 다양한 방식으로 확장할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/metadata.md
================================================
# 메타데이터 및 문서화 URL { #metadata-and-docs-urls }
**FastAPI** 애플리케이션에서 다양한 메타데이터 구성을 사용자 맞춤 설정할 수 있습니다.
## API에 대한 메타데이터 { #metadata-for-api }
OpenAPI 명세 및 자동화된 API 문서 UI에 사용되는 다음 필드를 설정할 수 있습니다:
| 매개변수 | 타입 | 설명 |
|----------|------|-------|
| `title` | `str` | API의 제목입니다. |
| `summary` | `str` | API에 대한 짧은 요약입니다. OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능. |
| `description` | `str` | API에 대한 짧은 설명입니다. 마크다운을 사용할 수 있습니다. |
| `version` | `string` | API의 버전입니다. OpenAPI의 버전이 아닌, 여러분의 애플리케이션의 버전을 나타냅니다. 예: `2.5.0`. |
| `terms_of_service` | `str` | API 이용 약관의 URL입니다. 제공하는 경우 URL 형식이어야 합니다. |
| `contact` | `dict` | 노출된 API에 대한 연락처 정보입니다. 여러 필드를 포함할 수 있습니다. contact 필드
매개변수
타입
설명
name
str
연락처 인물/조직의 식별명입니다.
url
str
연락처 정보가 담긴 URL입니다. URL 형식이어야 합니다.
email
str
연락처 인물/조직의 이메일 주소입니다. 이메일 주소 형식이어야 합니다.
|
| `license_info` | `dict` | 노출된 API의 라이선스 정보입니다. 여러 필드를 포함할 수 있습니다. license_info 필드
매개변수
타입
설명
name
str
필수 (license_info가 설정된 경우). API에 사용된 라이선스 이름입니다.
identifier
str
API에 대한 [SPDX](https://spdx.org/licenses/) 라이선스 표현입니다. identifier 필드는 url 필드와 상호 배타적입니다. OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능.
url
str
API에 사용된 라이선스의 URL입니다. URL 형식이어야 합니다.
|
다음과 같이 설정할 수 있습니다:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | 팁
`description` 필드에 마크다운을 사용할 수 있으며, 출력에서 렌더링됩니다.
///
이 구성을 사용하면 문서 자동화(로 생성된) API 문서는 다음과 같이 보입니다:
## 라이선스 식별자 { #license-identifier }
OpenAPI 3.1.0 및 FastAPI 0.99.0부터 `license_info`에 `url` 대신 `identifier`를 설정할 수도 있습니다.
예:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## 태그에 대한 메타데이터 { #metadata-for-tags }
`openapi_tags` 매개변수를 사용하여 경로 처리를 그룹화하는 데 사용되는 여러 태그에 추가 메타데이터를 추가할 수도 있습니다.
리스트는 각 태그에 대해 하나의 딕셔너리를 포함합니다.
각 딕셔너리에는 다음이 포함될 수 있습니다:
* `name` (**필수**): *경로 처리* 및 `APIRouter`의 `tags` 매개변수에서 사용하는 태그 이름과 동일한 `str`입니다.
* `description`: 태그에 대한 간단한 설명을 담은 `str`입니다. 마크다운을 포함할 수 있으며 문서 UI에 표시됩니다.
* `externalDocs`: 외부 문서를 설명하는 `dict`이며:
* `description`: 외부 문서에 대한 간단한 설명을 담은 `str`입니다.
* `url` (**필수**): 외부 문서의 URL을 담은 `str`입니다.
### 태그에 대한 메타데이터 생성 { #create-metadata-for-tags }
`users` 및 `items`에 대한 태그 예시로 시도해 보겠습니다.
태그에 대한 메타데이터를 생성하고 이를 `openapi_tags` 매개변수로 전달하세요:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
설명 안에 마크다운을 사용할 수 있다는 점에 유의하세요. 예를 들어 "login"은 굵게(**login**) 표시되고, "fancy"는 기울임꼴(_fancy_)로 표시됩니다.
/// tip | 팁
사용 중인 모든 태그에 메타데이터를 추가할 필요는 없습니다.
///
### 태그 사용 { #use-your-tags }
`tags` 매개변수를 *경로 처리* (및 `APIRouter`)와 함께 사용하여 이를 서로 다른 태그에 할당하세요:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | 정보
태그에 대한 자세한 내용은 [경로 처리 구성](path-operation-configuration.md#tags)에서 읽어보세요.
///
### 문서 확인 { #check-the-docs }
이제 문서를 확인하면 모든 추가 메타데이터가 표시됩니다:
### 태그 순서 { #order-of-tags }
각 태그 메타데이터 딕셔너리의 순서는 문서 UI에 표시되는 순서도 정의합니다.
예를 들어, 알파벳 순서상 `users`는 `items` 뒤에 오지만, 우리는 해당 메타데이터를 리스트의 첫 번째 딕셔너리로 추가했기 때문에 먼저 표시됩니다.
## OpenAPI URL { #openapi-url }
기본적으로 OpenAPI 스키마는 `/openapi.json`에서 제공됩니다.
하지만 `openapi_url` 매개변수로 이를 설정할 수 있습니다.
예를 들어, 이를 `/api/v1/openapi.json`에서 제공하도록 설정하려면:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
OpenAPI 스키마를 완전히 비활성화하려면 `openapi_url=None`으로 설정할 수 있으며, 이를 사용하여 문서화 사용자 인터페이스도 비활성화됩니다.
## 문서화 URL { #docs-urls }
포함된 두 가지 문서화 사용자 인터페이스를 설정할 수 있습니다:
* **Swagger UI**: `/docs`에서 제공됩니다.
* `docs_url` 매개변수로 URL을 설정할 수 있습니다.
* `docs_url=None`으로 설정하여 비활성화할 수 있습니다.
* **ReDoc**: `/redoc`에서 제공됩니다.
* `redoc_url` 매개변수로 URL을 설정할 수 있습니다.
* `redoc_url=None`으로 설정하여 비활성화할 수 있습니다.
예를 들어, Swagger UI를 `/documentation`에서 제공하고 ReDoc을 비활성화하려면:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/ko/docs/tutorial/middleware.md
================================================
# 미들웨어 { #middleware }
미들웨어를 **FastAPI** 애플리케이션에 추가할 수 있습니다.
"미들웨어"는 특정 *경로 처리*에 의해 처리되기 전, 모든 **요청**에 대해서 동작하는 함수입니다. 또한 모든 **응답**이 반환되기 전에도 동일하게 동작합니다.
* 미들웨어는 애플리케이션으로 오는 각 **요청**을 가져옵니다.
* 그런 다음 해당 **요청**에 대해 무언가를 하거나 필요한 코드를 실행할 수 있습니다.
* 그런 다음 **요청**을 나머지 애플리케이션(어떤 *경로 처리*가)을 통해 처리되도록 전달합니다.
* 그런 다음 애플리케이션(어떤 *경로 처리*가)이 생성한 **응답**을 가져옵니다.
* 그런 다음 해당 **응답**에 대해 무언가를 하거나 필요한 코드를 실행할 수 있습니다.
* 그런 다음 **응답**을 반환합니다.
/// note | 기술 세부사항
`yield`를 사용하는 의존성이 있다면, exit 코드는 미들웨어 *후에* 실행됩니다.
백그라운드 작업(뒤에서 보게 될 [백그라운드 작업](background-tasks.md) 섹션에서 다룹니다)이 있다면, 모든 미들웨어 *후에* 실행됩니다.
///
## 미들웨어 만들기 { #create-a-middleware }
미들웨어를 만들기 위해 함수 상단에 데코레이터 `@app.middleware("http")`를 사용합니다.
미들웨어 함수는 다음을 받습니다:
* `request`.
* `request`를 매개변수로 받는 `call_next` 함수.
* 이 함수는 `request`를 해당하는 *경로 처리*로 전달합니다.
* 그런 다음 해당 *경로 처리*가 생성한 `response`를 반환합니다.
* 그런 다음 반환하기 전에 `response`를 추가로 수정할 수 있습니다.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | 팁
사용자 정의 독점 헤더는 [`X-` 접두사를 사용](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)하여 추가할 수 있다는 점을 기억하세요.
하지만 브라우저에서 클라이언트가 볼 수 있게 하려는 사용자 정의 헤더가 있다면, [CORS (Cross-Origin Resource Sharing)](cors.md) 설정에 [Starlette의 CORS 문서](https://www.starlette.dev/middleware/#corsmiddleware)에 문서화된 `expose_headers` 매개변수를 사용해 추가해야 합니다.
///
/// note | 기술 세부사항
`from starlette.requests import Request`를 사용할 수도 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 이를 제공합니다. 하지만 이는 Starlette에서 직접 가져온 것입니다.
///
### `response`의 전과 후 { #before-and-after-the-response }
어떤 *경로 처리*가 받기 전에, `request`와 함께 실행될 코드를 추가할 수 있습니다.
또한 `response`가 생성된 후, 반환하기 전에 코드를 추가할 수도 있습니다.
예를 들어, 요청을 처리하고 응답을 생성하는 데 걸린 시간을 초 단위로 담는 사용자 정의 헤더 `X-Process-Time`을 추가할 수 있습니다:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | 팁
여기서는 이러한 사용 사례에서 더 정확할 수 있기 때문에 `time.time()` 대신 [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter)를 사용합니다. 🤓
///
## 여러 미들웨어 실행 순서 { #multiple-middleware-execution-order }
`@app.middleware()` 데코레이터 또는 `app.add_middleware()` 메서드를 사용해 여러 미들웨어를 추가하면, 새로 추가된 각 미들웨어가 애플리케이션을 감싸 스택을 형성합니다. 마지막에 추가된 미들웨어가 *가장 바깥쪽*이고, 처음에 추가된 미들웨어가 *가장 안쪽*입니다.
요청 경로에서는 *가장 바깥쪽* 미들웨어가 먼저 실행됩니다.
응답 경로에서는 마지막에 실행됩니다.
예를 들어:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
이 경우 실행 순서는 다음과 같습니다:
* **요청**: MiddlewareB → MiddlewareA → route
* **응답**: route → MiddlewareA → MiddlewareB
이러한 스태킹 동작은 미들웨어가 예측 가능하고 제어 가능한 순서로 실행되도록 보장합니다.
## 다른 미들웨어 { #other-middlewares }
다른 미들웨어에 대한 더 많은 정보는 나중에 [숙련된 사용자 안내서: 향상된 미들웨어](../advanced/middleware.md)에서 확인할 수 있습니다.
다음 섹션에서 미들웨어로 CORS를 처리하는 방법을 보게 될 것입니다.
================================================
FILE: docs/ko/docs/tutorial/path-operation-configuration.md
================================================
# 경로 처리 설정 { #path-operation-configuration }
*경로 처리 데코레이터*를 설정하기 위해 전달할 수 있는 몇 가지 매개변수가 있습니다.
/// warning | 경고
아래 매개변수들은 *경로 처리 함수*가 아닌 *경로 처리 데코레이터*에 직접 전달된다는 사실을 기억하세요.
///
## 응답 상태 코드 { #response-status-code }
*경로 처리*의 응답에 사용될 (HTTP) `status_code`를 정의할 수 있습니다.
`404`와 같은 `int`형 코드를 직접 전달할 수 있습니다.
하지만 각 숫자 코드가 무엇을 의미하는지 기억하지 못한다면, `status`에 있는 단축 상수들을 사용할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
해당 상태 코드는 응답에 사용되며, OpenAPI 스키마에 추가됩니다.
/// note | 기술 세부사항
다음과 같이 임포트하셔도 좋습니다. `from starlette import status`.
**FastAPI**는 개발자 여러분의 편의를 위해 `starlette.status`와 동일한 `fastapi.status`를 제공합니다. 하지만 이는 Starlette에서 직접 온 것입니다.
///
## 태그 { #tags }
(보통 단일 `str`인) `str`로 구성된 `list`와 함께 매개변수 `tags`를 전달하여, *경로 처리*에 태그를 추가할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
전달된 태그들은 OpenAPI의 스키마에 추가되며, 자동 문서 인터페이스에서 사용됩니다:
### Enum을 사용한 태그 { #tags-with-enums }
큰 애플리케이션이 있다면, **여러 태그**가 쌓이게 될 수 있고, 관련된 *경로 처리*에 항상 **같은 태그**를 사용하는지 확인하고 싶을 것입니다.
이런 경우에는 태그를 `Enum`에 저장하는 것이 합리적일 수 있습니다.
**FastAPI**는 일반 문자열과 동일한 방식으로 이를 지원합니다:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## 요약과 설명 { #summary-and-description }
`summary`와 `description`을 추가할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## 독스트링으로 만든 설명 { #description-from-docstring }
설명은 보통 길어지고 여러 줄에 걸쳐있기 때문에, *경로 처리* 설명을 함수 독스트링에 선언할 수 있으며, **FastAPI**는 그곳에서 이를 읽습니다.
독스트링에는 [Markdown](https://en.wikipedia.org/wiki/Markdown)을 작성할 수 있으며, (독스트링의 들여쓰기를 고려하여) 올바르게 해석되고 표시됩니다.
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
이는 대화형 문서에서 사용됩니다:
## 응답 설명 { #response-description }
`response_description` 매개변수로 응답에 관한 설명을 명시할 수 있습니다:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | 정보
`response_description`은 구체적으로 응답을 지칭하며, `description`은 일반적인 *경로 처리*를 지칭합니다.
///
/// check | 확인
OpenAPI는 각 *경로 처리*가 응답에 관한 설명을 요구할 것을 명시합니다.
따라서, 응답에 관한 설명을 제공하지 않으면, **FastAPI**가 "Successful response" 중 하나를 자동으로 생성합니다.
///
## *경로 처리* 지원중단하기 { #deprecate-a-path-operation }
*경로 처리*를 제거하지 않고 지원중단으로 표시해야 한다면, `deprecated` 매개변수를 전달하면 됩니다:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
대화형 문서에서 지원중단으로 명확하게 표시됩니다:
지원중단된 *경로 처리*와 지원중단되지 않은 *경로 처리*가 어떻게 보이는지 확인해 보세요:
## 정리 { #recap }
*경로 처리 데코레이터*에 매개변수(들)를 전달하여 *경로 처리*를 설정하고 메타데이터를 쉽게 추가할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/path-params-numeric-validations.md
================================================
# 경로 매개변수와 숫자 검증 { #path-parameters-and-numeric-validations }
`Query`를 사용하여 쿼리 매개변수에 더 많은 검증과 메타데이터를 선언하는 방법과 동일하게 `Path`를 사용하여 경로 매개변수에 검증과 메타데이터를 같은 타입으로 선언할 수 있습니다.
## `Path` 임포트 { #import-path }
먼저 `fastapi`에서 `Path`를 임포트하고, `Annotated`도 임포트합니다:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | 정보
FastAPI는 0.95.0 버전에서 `Annotated` 지원을 추가했고(그리고 이를 권장하기 시작했습니다).
더 오래된 버전이 있다면 `Annotated`를 사용하려고 할 때 오류가 발생합니다.
`Annotated`를 사용하기 전에 최소 0.95.1까지 [FastAPI 버전 업그레이드](../deployment/versions.md#upgrading-the-fastapi-versions)를 꼭 하세요.
///
## 메타데이터 선언 { #declare-metadata }
`Query`에 동일한 매개변수를 선언할 수 있습니다.
예를 들어, 경로 매개변수 `item_id`에 `title` 메타데이터 값을 선언하려면 다음과 같이 입력할 수 있습니다:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | 참고
경로 매개변수는 경로의 일부여야 하므로 언제나 필수입니다. `None`으로 선언하거나 기본값을 지정하더라도 아무 영향이 없으며, 항상 필수입니다.
///
## 필요한 대로 매개변수 정렬하기 { #order-the-parameters-as-you-need }
/// tip | 팁
`Annotated`를 사용한다면 이것은 아마 그렇게 중요하지 않거나 필요하지 않을 수 있습니다.
///
`str` 형인 쿼리 매개변수 `q`를 필수로 선언하고 싶다고 해봅시다.
해당 매개변수에 대해 아무런 선언을 할 필요가 없으므로 `Query`를 정말로 써야 할 필요는 없습니다.
하지만 `item_id` 경로 매개변수는 여전히 `Path`를 사용해야 합니다. 그리고 어떤 이유로 `Annotated`를 사용하고 싶지 않다고 해봅시다.
파이썬은 "기본값"이 있는 값을 "기본값"이 없는 값 앞에 두면 불평합니다.
하지만 순서를 재정렬해서 기본값이 없는 값(쿼리 매개변수 `q`)을 앞에 둘 수 있습니다.
**FastAPI**에서는 중요하지 않습니다. 이름, 타입 그리고 기본값 선언(`Query`, `Path` 등)로 매개변수를 감지하며 순서는 신경 쓰지 않습니다.
따라서 함수를 다음과 같이 선언할 수 있습니다:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
하지만 `Annotated`를 사용하면 이 문제가 없다는 점을 기억하세요. `Query()`나 `Path()`에 함수 매개변수 기본값을 사용하지 않기 때문에, 순서는 중요하지 않습니다.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## 필요한 대로 매개변수 정렬하기, 트릭 { #order-the-parameters-as-you-need-tricks }
/// tip | 팁
`Annotated`를 사용한다면 이것은 아마 그렇게 중요하지 않거나 필요하지 않을 수 있습니다.
///
유용할 수 있는 **작은 트릭**이 하나 있지만, 자주 필요하진 않을 겁니다.
만약 다음을 원한다면:
* `Query`나 어떤 기본값 없이 쿼리 매개변수 `q`를 선언하기
* `Path`를 사용해서 경로 매개변수 `item_id`를 선언하기
* 이들을 다른 순서로 두기
* `Annotated`를 사용하지 않기
...이를 위해 파이썬에는 작은 특별한 문법이 있습니다.
함수의 첫 번째 매개변수로 `*`를 전달하세요.
파이썬은 `*`으로 아무것도 하지 않지만, 뒤따르는 모든 매개변수는 키워드 인자(키-값 쌍)로 호출되어야 함을 알게 됩니다. 이는 kwargs로도 알려져 있습니다. 기본값이 없더라도 마찬가지입니다.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### `Annotated`를 쓰면 더 좋습니다 { #better-with-annotated }
`Annotated`를 사용하면 함수 매개변수 기본값을 사용하지 않기 때문에 이 문제가 발생하지 않으며, 아마 `*`도 사용할 필요가 없다는 점을 기억하세요.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## 숫자 검증: 크거나 같음 { #number-validations-greater-than-or-equal }
`Query`와 `Path`(그리고 나중에 볼 다른 것들)를 사용하여 숫자 제약을 선언할 수 있습니다.
여기서 `ge=1`인 경우, `item_id`는 `1`보다 "`g`reater than or `e`qual"(크거나 같은) 정수형 숫자여야 합니다.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## 숫자 검증: 크거나 및 작거나 같음 { #number-validations-greater-than-and-less-than-or-equal }
동일하게 적용됩니다:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## 숫자 검증: 부동소수, 크거나 및 작거나 { #number-validations-floats-greater-than-and-less-than }
숫자 검증은 `float` 값에도 동작합니다.
여기에서 gt를, ge뿐만 아니라 선언할 수 있다는 점이 중요해집니다. 예를 들어 값이 `1`보다 작더라도, 반드시 `0`보다 커야 한다고 요구할 수 있습니다.
즉, `0.5`는 유효한 값입니다. 그러나 `0.0` 또는 `0`은 그렇지 않습니다.
lt 역시 마찬가지입니다.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## 요약 { #recap }
`Query`, `Path`(아직 보지 못한 다른 것들도)를 사용하면 [쿼리 매개변수와 문자열 검증](query-params-str-validations.md)에서와 마찬가지로 메타데이터와 문자열 검증을 선언할 수 있습니다.
그리고 숫자 검증 또한 선언할 수 있습니다:
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info | 정보
`Query`, `Path`, 그리고 나중에 보게 될 다른 클래스들은 공통 `Param` 클래스의 서브클래스입니다.
이들 모두는 여러분이 본 추가 검증과 메타데이터에 대한 동일한 매개변수를 공유합니다.
///
/// note | 기술 세부사항
`fastapi`에서 `Query`, `Path` 등을 임포트할 때, 이것들은 실제로 함수입니다.
호출되면 동일한 이름의 클래스의 인스턴스를 반환합니다.
즉, 함수인 `Query`를 임포트한 겁니다. 그리고 호출하면 `Query`라는 이름을 가진 클래스의 인스턴스를 반환합니다.
이 함수들이 있는 이유는(클래스를 직접 사용하는 대신) 편집기에서 타입에 대한 오류를 표시하지 않도록 하기 위해서입니다.
이렇게 하면 오류를 무시하기 위한 사용자 설정을 추가하지 않고도 일반 편집기와 코딩 도구를 사용할 수 있습니다.
///
================================================
FILE: docs/ko/docs/tutorial/path-params.md
================================================
# 경로 매개변수 { #path-parameters }
파이썬의 포맷 문자열 리터럴에서 사용되는 문법을 이용하여 경로 "매개변수" 또는 "변수"를 선언할 수 있습니다:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
경로 매개변수 `item_id`의 값은 함수의 `item_id` 인자로 전달됩니다.
그래서 이 예제를 실행하고 [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo)로 이동하면, 다음 응답을 볼 수 있습니다:
```JSON
{"item_id":"foo"}
```
## 타입이 있는 경로 매개변수 { #path-parameters-with-types }
파이썬 표준 타입 어노테이션을 사용하여 함수에 있는 경로 매개변수의 타입을 선언할 수 있습니다:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
위의 예시에서, `item_id`는 `int`로 선언되었습니다.
/// check | 확인
이 기능은 함수 내에서 오류 검사, 자동완성 등의 편집기 기능을 활용할 수 있게 해줍니다.
///
## 데이터 변환 { #data-conversion }
이 예제를 실행하고 [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3)을 열면, 다음 응답을 볼 수 있습니다:
```JSON
{"item_id":3}
```
/// check | 확인
함수가 받은(반환도 하는) 값은 문자열 `"3"`이 아니라 파이썬 `int` 형인 `3`입니다.
즉, 타입 선언을 하면 **FastAPI**는 자동으로 요청을 "파싱"합니다.
///
## 데이터 검증 { #data-validation }
하지만 브라우저에서 [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo)로 이동하면, 다음과 같은 HTTP 오류를 볼 수 있습니다:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
경로 매개변수 `item_id`가 `int`가 아닌 `"foo"` 값을 가졌기 때문입니다.
`int` 대신 `float`을 제공하면(예: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)) 동일한 오류가 나타납니다.
/// check | 확인
즉, 파이썬 타입 선언을 하면 **FastAPI**는 데이터 검증을 합니다.
또한 오류에는 검증을 통과하지 못한 지점이 정확히 명시됩니다.
이는 API와 상호 작용하는 코드를 개발하고 디버깅하는 데 매우 유용합니다.
///
## 문서화 { #documentation }
그리고 브라우저에서 [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)를 열면, 다음과 같이 자동 대화식 API 문서를 볼 수 있습니다:
/// check | 확인
다시 한 번, 동일한 파이썬 타입 선언만으로 **FastAPI**는 자동 대화형 문서(Swagger UI 통합)를 제공합니다.
경로 매개변수가 정수형으로 선언된 것을 확인할 수 있습니다.
///
## 표준 기반의 이점, 대체 문서 { #standards-based-benefits-alternative-documentation }
그리고 생성된 스키마는 [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) 표준에서 나온 것이기 때문에 호환되는 도구가 많이 있습니다.
이 덕분에 **FastAPI** 자체에서 [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)로 접속할 수 있는 (ReDoc을 사용하는) 대체 API 문서를 제공합니다:
이와 마찬가지로 다양한 언어에 대한 코드 생성 도구를 포함하여 여러 호환되는 도구가 있습니다.
## Pydantic { #pydantic }
모든 데이터 검증은 [Pydantic](https://docs.pydantic.dev/)에 의해 내부적으로 수행되므로 이로 인한 이점을 모두 얻을 수 있습니다. 여러분은 관리를 잘 받고 있음을 느낄 수 있습니다.
`str`, `float`, `bool`, 그리고 다른 여러 복잡한 데이터 타입 선언을 할 수 있습니다.
이 중 몇 가지는 자습서의 다음 장에 설명되어 있습니다.
## 순서 문제 { #order-matters }
*경로 처리*를 만들 때 고정 경로를 갖고 있는 상황들을 맞닥뜨릴 수 있습니다.
`/users/me`처럼, 현재 사용자의 데이터를 가져온다고 합시다.
사용자 ID를 이용해 특정 사용자의 정보를 가져오는 경로 `/users/{user_id}`도 있습니다.
*경로 처리*는 순차적으로 평가되기 때문에 `/users/me`에 대한 경로가 `/users/{user_id}` 이전에 먼저 선언되었는지 확인해야 합니다:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
그렇지 않으면 `/users/{user_id}`에 대한 경로가 `/users/me`에도 매칭되어, 매개변수 `user_id`에 `"me"` 값이 들어왔다고 "생각하게" 됩니다.
마찬가지로, 경로 처리를 재정의할 수는 없습니다:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
경로가 먼저 매칭되기 때문에 첫 번째 것이 항상 사용됩니다.
## 사전정의 값 { #predefined-values }
만약 *경로 매개변수*를 받는 *경로 처리*가 있지만, 가능한 유효한 *경로 매개변수* 값들을 미리 정의하고 싶다면 파이썬 표준 `Enum`을 사용할 수 있습니다.
### `Enum` 클래스 생성 { #create-an-enum-class }
`Enum`을 임포트하고 `str`과 `Enum`을 상속하는 서브 클래스를 만듭니다.
`str`을 상속함으로써 API 문서는 값이 `string` 형이어야 하는 것을 알게 되고 이는 문서에 제대로 표시됩니다.
가능한 값들에 해당하는 고정된 값의 클래스 어트리뷰트들을 만듭니다:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | 팁
혹시 궁금하다면, "AlexNet", "ResNet", 그리고 "LeNet"은 그저 머신 러닝 모델들의 이름입니다.
///
### *경로 매개변수* 선언 { #declare-a-path-parameter }
생성한 열거형 클래스(`ModelName`)를 사용하는 타입 어노테이션으로 *경로 매개변수를* 만듭니다:
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### 문서 확인 { #check-the-docs }
*경로 매개변수*에 사용할 수 있는 값은 미리 정의되어 있으므로 대화형 문서에서 잘 표시됩니다:
### 파이썬 *열거형*으로 작업하기 { #working-with-python-enumerations }
*경로 매개변수*의 값은 *열거형 멤버*가 됩니다.
#### *열거형 멤버* 비교 { #compare-enumeration-members }
생성한 열거형 `ModelName`의 *열거형 멤버*와 비교할 수 있습니다:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### *열거형 값* 가져오기 { #get-the-enumeration-value }
`model_name.value` 또는 일반적으로 `your_enum_member.value`를 이용하여 실제 값(위 예시의 경우 `str`)을 가져올 수 있습니다:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | 팁
`ModelName.lenet.value`로도 값 `"lenet"`에 접근할 수 있습니다.
///
#### *열거형 멤버* 반환 { #return-enumeration-members }
*경로 처리*에서 *enum 멤버*를 반환할 수 있습니다. 이는 JSON 본문(예: `dict`) 내에 중첩된 형태로도 가능합니다.
클라이언트에 반환하기 전에 해당 값(이 경우 문자열)으로 변환됩니다:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
클라이언트는 아래와 같은 JSON 응답을 얻게 됩니다:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## 경로를 포함하는 경로 매개변수 { #path-parameters-containing-paths }
경로 `/files/{file_path}`를 가진 *경로 처리*가 있다고 해봅시다.
하지만 `file_path` 자체가 `home/johndoe/myfile.txt`와 같은 *경로*를 포함해야 합니다.
이때 해당 파일의 URL은 다음처럼 됩니다: `/files/home/johndoe/myfile.txt`.
### OpenAPI 지원 { #openapi-support }
테스트와 정의가 어려운 시나리오로 이어질 수 있으므로 OpenAPI는 *경로*를 포함하는 *경로 매개변수*를 내부에 선언하는 방법을 지원하지 않습니다.
그럼에도 Starlette의 내부 도구 중 하나를 사용하여 **FastAPI**에서는 이가 가능합니다.
또한 문서가 여전히 동작하긴 하지만, 매개변수에 경로가 포함되어야 한다는 내용을 추가로 문서화하지는 않습니다.
### 경로 변환기 { #path-convertor }
Starlette의 옵션을 직접 이용하여 다음과 같은 URL을 사용함으로써 *경로*를 포함하는 *경로 매개변수*를 선언할 수 있습니다:
```
/files/{file_path:path}
```
이러한 경우 매개변수의 이름은 `file_path`이며, 마지막 부분 `:path`는 매개변수가 어떤 *경로*와도 매칭되어야 함을 의미합니다.
따라서 다음과 같이 사용할 수 있습니다:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | 팁
매개변수가 선행 슬래시(`/`)가 있는 `/home/johndoe/myfile.txt`를 포함해야 할 수도 있습니다.
그 경우 URL은: `/files//home/johndoe/myfile.txt`이며 `files`와 `home` 사이에 이중 슬래시(`//`)가 생깁니다.
///
## 요약 { #recap }
**FastAPI**를 이용하면 짧고 직관적인 표준 파이썬 타입 선언을 사용하여 다음을 얻을 수 있습니다:
* 편집기 지원: 오류 검사, 자동완성 등
* 데이터 "파싱"
* 데이터 검증
* API 주석(Annotation)과 자동 문서
그리고 한 번만 선언하면 됩니다.
이는 대체 프레임워크와 비교했을 때 (엄청나게 빠른 성능 외에도) **FastAPI**의 주요한 장점일 것입니다.
================================================
FILE: docs/ko/docs/tutorial/query-param-models.md
================================================
# 쿼리 매개변수 모델 { #query-parameter-models }
연관된 **쿼리 매개변수** 그룹이 있다면 이를 선언하기 위해 **Pydantic 모델**을 생성할 수 있습니다.
이렇게 하면 **여러 곳**에서 **모델을 재사용**할 수 있을 뿐만 아니라, 매개변수에 대한 검증 및 메타데이터도 한 번에 선언할 수 있습니다. 😎
/// note | 참고
이 기능은 FastAPI 버전 `0.115.0`부터 지원됩니다. 🤓
///
## Pydantic 모델과 쿼리 매개변수 { #query-parameters-with-a-pydantic-model }
필요한 **쿼리 매개변수**를 **Pydantic 모델** 안에 선언한 다음, 매개변수를 `Query`로 선언합니다:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI**는 요청의 **쿼리 매개변수**에서 **각 필드**의 데이터를 **추출**해 정의한 Pydantic 모델을 제공합니다.
## 문서 확인하기 { #check-the-docs }
`/docs`의 문서 UI에서 쿼리 매개변수를 확인할 수 있습니다:
## 추가 쿼리 매개변수 금지 { #forbid-extra-query-parameters }
몇몇의 특이한 경우에 (흔치 않지만), 받으려는 쿼리 매개변수를 **제한**하고 싶을 수 있습니다.
Pydantic의 모델 설정을 사용해 어떤 `extra` 필드도 `forbid`할 수 있습니다:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
클라이언트가 **쿼리 매개변수**로 **추가적인** 데이터를 보내려고 하면 **에러** 응답을 받게 됩니다.
예를 들어, 아래와 같이 클라이언트가 `tool` 쿼리 매개변수에 `plumbus` 값을 보내려고 하면:
```http
https://example.com/items/?limit=10&tool=plumbus
```
쿼리 매개변수 `tool`이 허용되지 않는다는 **에러** 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## 요약 { #summary }
**FastAPI**에서 **쿼리 매개변수**를 선언할 때 **Pydantic 모델**을 사용할 수 있습니다. 😎
/// tip | 팁
스포일러 경고: Pydantic 모델을 쿠키와 헤더에도 사용할 수 있지만, 이에 대해서는 이후 튜토리얼에서 읽게 될 것입니다. 🤫
///
================================================
FILE: docs/ko/docs/tutorial/query-params-str-validations.md
================================================
# 쿼리 매개변수와 문자열 검증 { #query-parameters-and-string-validations }
**FastAPI**를 사용하면 매개변수에 대한 추가 정보 및 검증을 선언할 수 있습니다.
이 애플리케이션을 예로 들어보겠습니다:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
쿼리 매개변수 `q`는 `str | None` 자료형입니다. 즉, `str` 자료형이지만 `None` 역시 될 수 있음을 뜻하고, 실제로 기본값은 `None`이기 때문에 FastAPI는 이 매개변수가 필수가 아니라는 것을 압니다.
/// note | 참고
FastAPI는 `q`의 기본값이 `= None`이기 때문에 필수가 아님을 압니다.
`str | None`을 사용하면 편집기가 더 나은 지원과 오류 탐지를 제공하게 해줍니다.
///
## 추가 검증 { #additional-validation }
`q`가 선택적이지만 값이 주어질 때마다 **길이가 50자를 초과하지 않게** 강제하려 합니다.
### `Query`와 `Annotated` 임포트 { #import-query-and-annotated }
이를 위해 먼저 다음을 임포트합니다:
* `fastapi`에서 `Query`
* `typing`에서 `Annotated`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | 정보
FastAPI는 0.95.0 버전에서 `Annotated` 지원을 추가했고(그리고 이를 권장하기 시작했습니다).
이전 버전을 사용하면 `Annotated`를 사용하려고 할 때 오류가 발생합니다.
`Annotated`를 사용하기 전에 최소 0.95.1 버전으로 [FastAPI 버전 업그레이드](../deployment/versions.md#upgrading-the-fastapi-versions)를 진행하세요.
///
## `q` 매개변수의 타입에 `Annotated` 사용하기 { #use-annotated-in-the-type-for-the-q-parameter }
이전에 [Python 타입 소개](../python-types.md#type-hints-with-metadata-annotations)에서 `Annotated`를 사용해 매개변수에 메타데이터를 추가할 수 있다고 말씀드린 것을 기억하시나요?
이제 FastAPI에서 사용할 차례입니다. 🚀
다음과 같은 타입 어노테이션이 있었습니다:
```Python
q: str | None = None
```
여기서 `Annotated`로 감싸서 다음과 같이 만듭니다:
```Python
q: Annotated[str | None] = None
```
두 버전 모두 같은 의미로, `q`는 `str` 또는 `None`이 될 수 있는 매개변수이며 기본값은 `None`입니다.
이제 재미있는 부분으로 넘어가 봅시다. 🎉
## `q` 매개변수의 `Annotated`에 `Query` 추가하기 { #add-query-to-annotated-in-the-q-parameter }
이제 이 `Annotated`에 더 많은 정보를 넣을 수 있으므로(이 경우에는 추가 검증), `Annotated` 안에 `Query`를 추가하고 `max_length` 매개변수를 `50`으로 설정합니다:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
기본값은 여전히 `None`이므로, 매개변수는 여전히 선택적입니다.
하지만 `Annotated` 안에 `Query(max_length=50)`를 넣음으로써, 이 값에 대해 **추가 검증**을 적용하고 최대 50자까지만 허용하도록 FastAPI에 알려줍니다. 😎
/// tip | 팁
여기서는 **쿼리 매개변수**이기 때문에 `Query()`를 사용합니다. 나중에 `Path()`, `Body()`, `Header()`, `Cookie()`와 같이 `Query()`와 동일한 인자를 받는 것들도 보게 될 것입니다.
///
이제 FastAPI는 다음을 수행합니다:
* 최대 길이가 50자인지 확인하도록 데이터를 **검증**합니다
* 데이터가 유효하지 않을 때 클라이언트에게 **명확한 오류**를 보여줍니다
* OpenAPI 스키마 *경로 처리*에 매개변수를 **문서화**합니다(따라서 **자동 문서 UI**에 표시됩니다)
## 대안(이전 방식): 기본값으로 `Query` 사용 { #alternative-old-query-as-the-default-value }
이전 FastAPI 버전(0.95.0 이전)에서는 `Annotated`에 넣는 대신, 매개변수의 기본값으로 `Query`를 사용해야 했습니다. 주변에서 이 방식을 사용하는 코드를 볼 가능성이 높기 때문에 설명해 드리겠습니다.
/// tip | 팁
새 코드를 작성할 때와 가능할 때는 위에서 설명한 대로 `Annotated`를 사용하세요. 여러 장점이 있고(아래에서 설명합니다) 단점은 없습니다. 🍰
///
다음은 함수 매개변수의 기본값으로 `Query()`를 사용하면서 `max_length`를 50으로 설정하는 방법입니다:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
이 경우(`Annotated`를 사용하지 않는 경우) 함수에서 기본값 `None`을 `Query()`로 바꿔야 하므로, 이제 `Query(default=None)`로 기본값을 설정해야 합니다. (최소한 FastAPI 입장에서는) 이 인자는 해당 기본값을 정의하는 것과 같은 목적을 수행합니다.
그러므로:
```Python
q: str | None = Query(default=None)
```
...위 코드는 기본값이 `None`인 선택적 매개변수를 만들며, 아래와 동일합니다:
```Python
q: str | None = None
```
하지만 `Query` 버전은 이것이 쿼리 매개변수임을 명시적으로 선언합니다.
그 다음, `Query`로 더 많은 매개변수를 전달할 수 있습니다. 지금의 경우 문자열에 적용되는 `max_length` 매개변수입니다:
```Python
q: str | None = Query(default=None, max_length=50)
```
이는 데이터를 검증할 것이고, 데이터가 유효하지 않다면 명백한 오류를 보여주며, OpenAPI 스키마 *경로 처리*에 매개변수를 문서화 합니다.
### 기본값으로 `Query` 사용 또는 `Annotated`에 넣기 { #query-as-the-default-value-or-in-annotated }
`Annotated` 안에서 `Query`를 사용할 때는 `Query`에 `default` 매개변수를 사용할 수 없다는 점을 기억하세요.
대신 함수 매개변수의 실제 기본값을 사용하세요. 그렇지 않으면 일관성이 깨집니다.
예를 들어, 다음은 허용되지 않습니다:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...왜냐하면 기본값이 `"rick"`인지 `"morty"`인지 명확하지 않기 때문입니다.
따라서 (가능하면) 다음과 같이 사용합니다:
```Python
q: Annotated[str, Query()] = "rick"
```
...또는 오래된 코드베이스에서는 다음과 같은 코드를 찾게 될 것입니다:
```Python
q: str = Query(default="rick")
```
### `Annotated`의 장점 { #advantages-of-annotated }
함수 매개변수의 기본값 방식 대신 **`Annotated`를 사용하는 것을 권장**합니다. 여러 이유로 **더 좋기** 때문입니다. 🤓
**함수 매개변수**의 **기본값**이 **실제 기본값**이 되므로, 전반적으로 Python에 더 직관적입니다. 😌
FastAPI 없이도 **다른 곳에서** 같은 함수를 **호출**할 수 있고, **예상대로 동작**합니다. **필수** 매개변수(기본값이 없는 경우)가 있다면 **편집기**가 오류로 알려줄 것이고, 필수 매개변수를 전달하지 않고 실행하면 **Python**도 오류를 냅니다.
`Annotated`를 사용하지 않고 **(이전) 기본값 스타일**을 사용하면, FastAPI 없이 **다른 곳에서** 함수를 호출할 때도 제대로 동작하도록 함수에 인자를 전달해야 한다는 것을 **기억**해야 합니다. 그렇지 않으면 값이 기대와 다르게 됩니다(예: `str` 대신 `QueryInfo` 같은 것). 그리고 편집기도 경고하지 않고 Python도 그 함수를 실행할 때는 불평하지 않으며, 오직 내부 동작에서 오류가 발생할 때만 문제가 드러납니다.
`Annotated`는 하나 이상의 메타데이터 어노테이션을 가질 수 있기 때문에, 이제 [Typer](https://typer.tiangolo.com/) 같은 다른 도구에서도 같은 함수를 사용할 수 있습니다. 🚀
## 검증 더 추가하기 { #add-more-validations }
`min_length` 매개변수도 추가할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## 정규식 추가 { #add-regular-expressions }
매개변수와 일치해야 하는 정규 표현식 `pattern`을 정의할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
이 특정 정규표현식 패턴은 전달 받은 매개변수 값이 다음을 만족하는지 검사합니다:
* `^`: 뒤따르는 문자로 시작하며, 앞에는 문자가 없습니다.
* `fixedquery`: 정확히 `fixedquery` 값을 가집니다.
* `$`: 여기서 끝나며, `fixedquery` 이후로 더 이상 문자가 없습니다.
**"정규 표현식"** 개념에 대해 상실감을 느꼈다면 걱정하지 않아도 됩니다. 많은 사람에게 어려운 주제입니다. 아직은 정규 표현식 없이도 많은 작업들을 할 수 있습니다.
이제 필요할 때 언제든지 **FastAPI**에서 직접 사용할 수 있다는 사실을 알게 되었습니다.
## 기본값 { #default-values }
물론 `None`이 아닌 다른 기본값을 사용할 수도 있습니다.
`q` 쿼리 매개변수에 `min_length`를 `3`으로 설정하고, 기본값을 `"fixedquery"`로 선언하고 싶다고 해봅시다:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | 참고
`None`을 포함해 어떤 타입이든 기본값을 가지면 매개변수는 선택적(필수 아님)이 됩니다.
///
## 필수 매개변수 { #required-parameters }
더 많은 검증이나 메타데이터를 선언할 필요가 없는 경우, 다음과 같이 기본값을 선언하지 않고 쿼리 매개변수 `q`를 필수로 만들 수 있습니다:
```Python
q: str
```
아래 대신:
```Python
q: str | None = None
```
하지만 이제는 예를 들어 다음과 같이 `Query`로 선언합니다:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
따라서 `Query`를 사용하면서 값을 필수로 선언해야 할 때는, 기본값을 선언하지 않으면 됩니다:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### 필수지만 `None` 가능 { #required-can-be-none }
매개변수가 `None`을 허용하지만 여전히 필수라고 선언할 수 있습니다. 이렇게 하면 값이 `None`이더라도 클라이언트는 값을 반드시 전송해야 합니다.
이를 위해 `None`이 유효한 타입이라고 선언하되, 기본값은 선언하지 않으면 됩니다:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## 쿼리 매개변수 리스트 / 다중값 { #query-parameter-list-multiple-values }
`Query`로 쿼리 매개변수를 명시적으로 정의할 때 값들의 리스트를 받도록 선언할 수도 있고, 다른 말로 하면 여러 값을 받도록 선언할 수도 있습니다.
예를 들어, URL에서 여러 번 나타날 수 있는 `q` 쿼리 매개변수를 선언하려면 다음과 같이 작성할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
그 다음, 아래와 같은 URL로:
```
http://localhost:8000/items/?q=foo&q=bar
```
여러 `q` *쿼리 매개변수* 값들(`foo` 및 `bar`)을 파이썬 `list`로 *경로 처리 함수*의 *함수 매개변수* `q`에서 받게 됩니다.
따라서 해당 URL에 대한 응답은 다음과 같습니다:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | 팁
위의 예와 같이 `list` 타입으로 쿼리 매개변수를 선언하려면 `Query`를 명시적으로 사용해야 합니다. 그렇지 않으면 요청 본문으로 해석됩니다.
///
대화형 API 문서는 여러 값을 허용하도록 수정 됩니다:
### 쿼리 매개변수 리스트 / 기본값이 있는 다중값 { #query-parameter-list-multiple-values-with-defaults }
제공된 값이 없으면 기본 `list` 값을 정의할 수도 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
다음으로 이동하면:
```
http://localhost:8000/items/
```
`q`의 기본값은 `["foo", "bar"]`가 되고, 응답은 다음이 됩니다:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### `list`만 사용하기 { #using-just-list }
`list[str]` 대신 `list`를 직접 사용할 수도 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | 참고
이 경우 FastAPI는 리스트의 내용을 검사하지 않음을 명심하세요.
예를 들어, `list[int]`는 리스트 내용이 정수인지 검사(및 문서화)합니다. 하지만 `list` 단독일 경우는 아닙니다.
///
## 더 많은 메타데이터 선언 { #declare-more-metadata }
매개변수에 대한 정보를 추가할 수 있습니다.
해당 정보는 생성된 OpenAPI에 포함되고 문서 사용자 인터페이스 및 외부 도구에서 사용됩니다.
/// note | 참고
도구에 따라 OpenAPI 지원 수준이 다를 수 있음을 명심하세요.
일부는 아직 선언된 추가 정보를 모두 표시하지 않을 수 있지만, 대부분의 경우 누락된 기능은 이미 개발 계획이 있습니다.
///
`title`을 추가할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
그리고 `description`도 추가할 수 있습니다:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## 별칭 매개변수 { #alias-parameters }
매개변수가 `item-query`이길 원한다고 가정해 봅시다.
마치 다음과 같습니다:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
그러나 `item-query`는 유효한 파이썬 변수 이름이 아닙니다.
가장 가까운 것은 `item_query`일 겁니다.
하지만 정확히 `item-query`이길 원합니다...
이럴 경우 `alias`를 선언할 수 있으며, 해당 별칭은 매개변수 값을 찾는 데 사용됩니다:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## 매개변수 사용 중단하기 { #deprecating-parameters }
이제는 더 이상 이 매개변수를 마음에 들어하지 않는다고 가정해 봅시다.
이 매개변수를 사용하는 클라이언트가 있기 때문에 한동안은 남겨둬야 하지만, 문서에서 사용 중단됨으로 명확하게 보여주고 싶습니다.
그렇다면 `deprecated=True` 매개변수를 `Query`로 전달합니다:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
문서가 아래와 같이 보일겁니다:
## OpenAPI에서 매개변수 제외 { #exclude-parameters-from-openapi }
생성된 OpenAPI 스키마(따라서 자동 문서화 시스템)에서 쿼리 매개변수를 제외하려면 `Query`의 `include_in_schema` 매개변수를 `False`로 설정하세요:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## 커스텀 검증 { #custom-validation }
위에 나온 매개변수들로는 할 수 없는 **커스텀 검증**이 필요한 경우가 있을 수 있습니다.
그런 경우에는 일반적인 검증(예: 값이 `str`인지 검증한 뒤) 이후에 적용되는 **커스텀 검증 함수**를 사용할 수 있습니다.
`Annotated` 안에서 [Pydantic의 `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator)를 사용하면 이를 구현할 수 있습니다.
/// tip | 팁
Pydantic에는 [BeforeValidator](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator)와 같은 다른 것들도 있습니다. 🤓
///
예를 들어, 이 커스텀 validator는 ISBN 도서 번호의 경우 아이템 ID가 `isbn-`으로 시작하고, IMDB 영화 URL ID의 경우 `imdb-`로 시작하는지 확인합니다:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | 정보
이는 Pydantic 2 이상 버전에서 사용할 수 있습니다. 😎
///
/// tip | 팁
데이터베이스나 다른 API 같은 **외부 구성요소**와 통신이 필요한 어떤 종류의 검증이든 해야 한다면, 대신 **FastAPI Dependencies**를 사용해야 합니다. 이에 대해서는 나중에 배우게 됩니다.
이 커스텀 validator는 요청에서 제공된 **같은 데이터만**으로 확인할 수 있는 것들을 위한 것입니다.
///
### 코드 이해하기 { #understand-that-code }
중요한 부분은 **`Annotated` 안에서 함수와 함께 `AfterValidator`를 사용한다는 것**뿐입니다. 이 부분은 건너뛰셔도 됩니다. 🤸
---
하지만 이 특정 코드 예제가 궁금하고 계속 보고 싶다면, 추가 세부사항은 다음과 같습니다.
#### `value.startswith()`를 사용한 문자열 { #string-with-value-startswith }
알고 계셨나요? `value.startswith()`를 사용하는 문자열은 튜플을 받을 수 있으며, 튜플에 있는 각 값을 확인합니다:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### 임의의 항목 { #a-random-item }
`data.items()`를 사용하면 각 딕셔너리 항목의 키와 값을 담은 튜플로 구성된 이터러블 객체를 얻습니다.
이 이터러블 객체를 `list(data.items())`로 적절한 `list`로 변환합니다.
그 다음 `random.choice()`로 리스트에서 **무작위 값**을 얻어 `(id, name)` 형태의 튜플을 얻습니다. 예를 들어 `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")` 같은 값이 될 것입니다.
그 다음 이 튜플의 **두 값을** 변수 `id`와 `name`에 **할당**합니다.
따라서 사용자가 아이템 ID를 제공하지 않더라도, 무작위 추천을 받게 됩니다.
...이 모든 것을 **단 하나의 간단한 줄**로 합니다. 🤯 Python 정말 좋지 않나요? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## 요약 { #recap }
매개변수에 검증과 메타데이터를 추가 선언할 수 있습니다.
제네릭 검증과 메타데이터:
* `alias`
* `title`
* `description`
* `deprecated`
문자열에 특화된 검증:
* `min_length`
* `max_length`
* `pattern`
`AfterValidator`를 사용하는 커스텀 검증.
예제에서 `str` 값의 검증을 어떻게 추가하는지 살펴보았습니다.
숫자와 같은 다른 타입에 대한 검증을 어떻게 선언하는지 확인하려면 다음 장을 확인하기 바랍니다.
================================================
FILE: docs/ko/docs/tutorial/query-params.md
================================================
# 쿼리 매개변수 { #query-parameters }
경로 매개변수의 일부가 아닌 다른 함수 매개변수를 선언하면 "쿼리" 매개변수로 자동 해석합니다.
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
쿼리는 URL에서 `?` 후에 나오고 `&`으로 구분되는 키-값 쌍의 집합입니다.
예를 들어, 아래 URL에서:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...쿼리 매개변수는:
* `skip`: 값 `0`을 가집니다.
* `limit`: 값 `10`을 가집니다.
URL의 일부이므로 "자연스럽게" 문자열입니다.
하지만 파이썬 타입과 함께 선언할 경우(위 예에서 `int`), 해당 타입으로 변환 및 검증됩니다.
경로 매개변수에 적용된 동일한 프로세스가 쿼리 매개변수에도 적용됩니다:
* (당연히) 편집기 지원
* 데이터 "파싱"
* 데이터 검증
* 자동 문서화
## 기본값 { #defaults }
쿼리 매개변수는 경로에서 고정된 부분이 아니기 때문에 선택적일 수 있고 기본값을 가질 수 있습니다.
위 예에서 `skip=0`과 `limit=10`은 기본값을 갖고 있습니다.
그러므로 URL로 이동하는 것은:
```
http://127.0.0.1:8000/items/
```
아래로 이동하는 것과 같습니다:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
하지만 가령 아래로 이동한 경우:
```
http://127.0.0.1:8000/items/?skip=20
```
함수의 매개변수 값은 아래가 됩니다:
* `skip=20`: URL에서 지정했기 때문입니다
* `limit=10`: 기본값이기 때문입니다
## 선택적 매개변수 { #optional-parameters }
같은 방법으로 기본값을 `None`으로 설정하여 선택적 매개변수를 선언할 수 있습니다:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
이 경우 함수 매개변수 `q`는 선택적이며 기본값으로 `None` 값이 됩니다.
/// check
또한 **FastAPI**는 `item_id`가 경로 매개변수이고 `q`는 경로 매개변수가 아니라서 쿼리 매개변수라는 것을 알 정도로 충분히 똑똑하다는 점도 확인하세요.
///
## 쿼리 매개변수 형변환 { #query-parameter-type-conversion }
`bool` 형으로 선언할 수도 있고, 아래처럼 변환됩니다:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
이 경우, 아래로 이동하면:
```
http://127.0.0.1:8000/items/foo?short=1
```
또는
```
http://127.0.0.1:8000/items/foo?short=True
```
또는
```
http://127.0.0.1:8000/items/foo?short=true
```
또는
```
http://127.0.0.1:8000/items/foo?short=on
```
또는
```
http://127.0.0.1:8000/items/foo?short=yes
```
또는 다른 어떤 변형(대문자, 첫글자만 대문자 등)이더라도 함수는 `bool` 값이 `True`인 매개변수 `short`를 보게 됩니다. 그렇지 않은 경우 `False`입니다.
## 여러 경로/쿼리 매개변수 { #multiple-path-and-query-parameters }
여러 경로 매개변수와 쿼리 매개변수를 동시에 선언할 수 있으며 **FastAPI**는 어느 것이 무엇인지 알고 있습니다.
그리고 특정 순서로 선언할 필요가 없습니다.
매개변수들은 이름으로 감지됩니다:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## 필수 쿼리 매개변수 { #required-query-parameters }
경로가 아닌 매개변수에 대한 기본값을 선언할 때(지금은 쿼리 매개변수만 보았습니다), 해당 매개변수는 필수적(Required)이지 않았습니다.
특정값을 추가하지 않고 선택적으로 만들기 위해선 기본값을 `None`으로 설정하면 됩니다.
그러나 쿼리 매개변수를 필수로 만들려면 단순히 기본값을 선언하지 않으면 됩니다:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
여기 쿼리 매개변수 `needy`는 `str`형인 필수 쿼리 매개변수입니다.
브라우저에서 아래와 같은 URL을 연다면:
```
http://127.0.0.1:8000/items/foo-item
```
...필수 매개변수 `needy`를 넣지 않았기 때문에 아래와 같은 오류를 보게 됩니다:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
`needy`는 필수 매개변수이므로 URL에 반드시 설정해줘야 합니다:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...아래처럼 작동합니다:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
그리고 물론, 일부 매개변수는 필수로, 다른 일부는 기본값을, 또 다른 일부는 선택적으로 선언할 수 있습니다:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
위 예시에서는 3가지 쿼리 매개변수가 있습니다:
* `needy`, 필수적인 `str`.
* `skip`, 기본값이 `0`인 `int`.
* `limit`, 선택적인 `int`.
/// tip
[경로 매개변수](path-params.md#predefined-values)와 마찬가지로 `Enum`을 사용할 수 있습니다.
///
================================================
FILE: docs/ko/docs/tutorial/request-files.md
================================================
# 파일 요청 { #request-files }
`File`을 사용하여 클라이언트가 업로드할 파일들을 정의할 수 있습니다.
/// info | 정보
업로드된 파일을 전달받기 위해 먼저 [`python-multipart`](https://github.com/Kludex/python-multipart)를 설치해야합니다.
[가상 환경](../virtual-environments.md)을 생성하고, 활성화한 다음, 예를 들어 다음과 같이 설치하세요:
```console
$ pip install python-multipart
```
업로드된 파일들은 "폼 데이터"의 형태로 전송되기 때문에 이 작업이 필요합니다.
///
## `File` 임포트 { #import-file }
`fastapi` 에서 `File` 과 `UploadFile` 을 임포트 합니다:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## `File` 매개변수 정의 { #define-file-parameters }
`Body` 및 `Form` 과 동일한 방식으로 파일의 매개변수를 생성합니다:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | 정보
`File` 은 `Form` 으로부터 직접 상속된 클래스입니다.
하지만 `fastapi`로부터 `Query`, `Path`, `File` 등을 임포트 할 때, 이것들은 특별한 클래스들을 반환하는 함수라는 것을 기억하기 바랍니다.
///
/// tip | 팁
File의 본문을 선언할 때, 매개변수가 쿼리 매개변수 또는 본문(JSON) 매개변수로 해석되는 것을 방지하기 위해 `File` 을 사용해야합니다.
///
파일들은 "폼 데이터"의 형태로 업로드 됩니다.
*경로 처리 함수*의 매개변수를 `bytes` 로 선언하는 경우 **FastAPI**는 파일을 읽고 `bytes` 형태의 내용을 전달합니다.
이것은 전체 내용이 메모리에 저장된다는 것을 의미한다는 걸 염두하기 바랍니다. 이는 작은 크기의 파일들에 적합합니다.
어떤 경우에는 `UploadFile` 을 사용하는 것이 더 유리합니다.
## `UploadFile`을 사용하는 `File` 매개변수 { #file-parameters-with-uploadfile }
`File` 매개변수를 `UploadFile` 타입으로 정의합니다:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
`UploadFile` 을 사용하는 것은 `bytes` 과 비교해 다음과 같은 장점이 있습니다:
* 매개변수의 기본값에서 `File()`을 사용할 필요가 없습니다.
* "스풀 파일"을 사용합니다.
* 최대 크기 제한까지만 메모리에 저장되며, 이를 초과하는 경우 디스크에 저장됩니다.
* 따라서 이미지, 동영상, 큰 이진코드와 같은 대용량 파일들을 많은 메모리를 소모하지 않고 처리하기에 적합합니다.
* 업로드 된 파일의 메타데이터를 얻을 수 있습니다.
* [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` 인터페이스를 갖고 있습니다.
* file-like object를 필요로하는 다른 라이브러리에 직접적으로 전달할 수 있는 파이썬 [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) 객체를 반환합니다.
### `UploadFile` { #uploadfile }
`UploadFile` 은 다음과 같은 어트리뷰트가 있습니다:
* `filename` : 문자열(`str`)로 된 업로드된 파일의 파일명입니다 (예: `myimage.jpg`).
* `content_type` : 문자열(`str`)로 된 파일 형식(MIME type / media type)입니다 (예: `image/jpeg`).
* `file` : [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (a [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) object)입니다. 이것은 "file-like" 객체를 필요로하는 다른 함수나 라이브러리에 직접적으로 전달할 수 있는 실질적인 파이썬 파일 객체입니다.
`UploadFile` 에는 다음의 `async` 메소드들이 있습니다. 이들은 내부적인 `SpooledTemporaryFile` 을 사용하여 해당하는 파일 메소드를 호출합니다.
* `write(data)`: `data`(`str` 또는 `bytes`)를 파일에 작성합니다.
* `read(size)`: 파일의 바이트 및 글자의 `size`(`int`)를 읽습니다.
* `seek(offset)`: 파일 내 `offset`(`int`) 위치의 바이트로 이동합니다.
* 예) `await myfile.seek(0)` 를 사용하면 파일의 시작부분으로 이동합니다.
* `await myfile.read()` 를 사용한 후 내용을 다시 읽을 때 유용합니다.
* `close()`: 파일을 닫습니다.
상기 모든 메소드들이 `async` 메소드이기 때문에 “await”을 사용하여야 합니다.
예를들어, `async` *경로 처리 함수*의 내부에서 다음과 같은 방식으로 내용을 가져올 수 있습니다:
```Python
contents = await myfile.read()
```
만약 일반적인 `def` *경로 처리 함수*의 내부라면, 다음과 같이 `UploadFile.file` 에 직접 접근할 수 있습니다:
```Python
contents = myfile.file.read()
```
/// note | `async` 기술 세부사항
`async` 메소드들을 사용할 때 **FastAPI**는 스레드풀에서 파일 메소드들을 실행하고 그들을 기다립니다.
///
/// note | Starlette 기술 세부사항
**FastAPI**의 `UploadFile` 은 **Starlette**의 `UploadFile` 을 직접적으로 상속받지만, **Pydantic** 및 FastAPI의 다른 부분들과의 호환성을 위해 필요한 부분들이 추가되었습니다.
///
## "폼 데이터"란 { #what-is-form-data }
HTML의 폼들(``)이 서버에 데이터를 전송하는 방식은 대개 데이터에 JSON과는 다른 "특별한" 인코딩을 사용합니다.
**FastAPI**는 JSON 대신 올바른 위치에서 데이터를 읽을 수 있도록 합니다.
/// note | 기술 세부사항
폼의 데이터는 파일이 포함되지 않은 경우 일반적으로 "미디어 유형" `application/x-www-form-urlencoded` 을 사용해 인코딩 됩니다.
하지만 파일이 포함된 경우, `multipart/form-data`로 인코딩됩니다. `File`을 사용하였다면, **FastAPI**는 본문의 적합한 부분에서 파일을 가져와야 한다는 것을 인지합니다.
인코딩과 폼 필드에 대해 더 알고싶다면, [MDN 웹 문서의 `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)를 참고하기 바랍니다.
///
/// warning | 경고
다수의 `File` 과 `Form` 매개변수를 한 *경로 처리*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json` 가 아닌 `multipart/form-data` 로 인코딩 되기 때문에 JSON으로 받아야하는 `Body` 필드를 함께 선언할 수는 없습니다.
이는 **FastAPI**의 한계가 아니라, HTTP 프로토콜에 의한 것입니다.
///
## 선택적 파일 업로드 { #optional-file-upload }
표준 타입 애너테이션을 사용하고 기본값을 `None`으로 설정하여 파일을 선택적으로 만들 수 있습니다:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## 추가 메타데이터를 포함한 `UploadFile` { #uploadfile-with-additional-metadata }
추가 메타데이터를 설정하기 위해 예를 들어 `UploadFile`과 함께 `File()`을 사용할 수도 있습니다:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## 다중 파일 업로드 { #multiple-file-uploads }
여러 파일을 동시에 업로드 할 수 있습니다.
그들은 "폼 데이터"를 사용하여 전송된 동일한 "폼 필드"에 연결됩니다.
이 기능을 사용하기 위해 , `bytes` 의 `List` 또는 `UploadFile` 를 선언하기 바랍니다:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
선언한대로, `bytes` 의 `list` 또는 `UploadFile` 들을 전송받을 것입니다.
/// note | 기술 세부사항
`from starlette.responses import HTMLResponse` 역시 사용할 수 있습니다.
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses` 와 동일한 `starlette.responses` 도 제공합니다. 하지만 대부분의 응답들은 Starlette로부터 직접 제공됩니다.
///
### 추가 메타데이터를 포함한 다중 파일 업로드 { #multiple-file-uploads-with-additional-metadata }
이전과 같은 방식으로 `UploadFile`에 대해서도 `File()`을 사용해 추가 매개변수를 설정할 수 있습니다:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## 요약 { #recap }
`File`, `bytes`, `UploadFile`을 사용하여 폼 데이터로 전송되는 요청에서 업로드할 파일을 선언하세요.
================================================
FILE: docs/ko/docs/tutorial/request-form-models.md
================================================
# 폼 모델 { #form-models }
FastAPI에서 **Pydantic 모델**을 이용하여 **폼 필드**를 선언할 수 있습니다.
/// info | 정보
폼을 사용하려면, 먼저 [`python-multipart`](https://github.com/Kludex/python-multipart)를 설치하세요.
[가상 환경](../virtual-environments.md)을 생성하고 활성화한 다음, 예를 들어 아래와 같이 설치하세요:
```console
$ pip install python-multipart
```
///
/// note | 참고
이 기능은 FastAPI 버전 `0.113.0` 이후부터 지원됩니다. 🤓
///
## 폼을 위한 Pydantic 모델 { #pydantic-models-for-forms }
**폼 필드**로 받고 싶은 필드를 **Pydantic 모델**로 선언한 다음, 매개변수를 `Form`으로 선언하면 됩니다:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI**는 요청에서 받은 **폼 데이터**에서 **각 필드**에 대한 데이터를 **추출**하고 정의한 Pydantic 모델을 줍니다.
## 문서 확인하기 { #check-the-docs }
문서 UI `/docs`에서 확인할 수 있습니다:
## 추가 폼 필드 금지하기 { #forbid-extra-form-fields }
일부 특별한 사용 사례(아마도 흔하지는 않겠지만)에서는 Pydantic 모델에서 선언된 폼 필드로만 **제한**하길 원할 수도 있습니다. 그리고 **추가** 필드를 **금지**할 수도 있습니다.
/// note | 참고
이 기능은 FastAPI 버전 `0.114.0` 이후부터 지원됩니다. 🤓
///
Pydantic의 모델 구성을 사용하여 `extra` 필드를 `forbid`할 수 있습니다:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
클라이언트가 추가 데이터를 보내려고 하면 **오류** 응답을 받게 됩니다.
예를 들어, 클라이언트가 폼 필드를 보내려고 하면:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
`extra` 필드가 허용되지 않는다는 오류 응답을 받게 됩니다:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## 요약 { #summary }
Pydantic 모델을 사용하여 FastAPI에서 폼 필드를 선언할 수 있습니다. 😎
================================================
FILE: docs/ko/docs/tutorial/request-forms-and-files.md
================================================
# 폼 및 파일 요청 { #request-forms-and-files }
`File` 과 `Form` 을 사용하여 파일과 폼 필드를 동시에 정의할 수 있습니다.
/// info
업로드된 파일 및/또는 폼 데이터를 받으려면 먼저 [`python-multipart`](https://github.com/Kludex/python-multipart)를 설치해야 합니다.
[가상 환경](../virtual-environments.md)을 생성하고, 활성화한 다음 설치해야 합니다. 예:
```console
$ pip install python-multipart
```
///
## `File` 및 `Form` 임포트 { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## `File` 및 `Form` 매개변수 정의 { #define-file-and-form-parameters }
`Body` 및 `Query`와 동일한 방식으로 파일과 폼의 매개변수를 생성합니다:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
파일과 폼 필드는 폼 데이터로 업로드되며, 파일과 폼 필드를 받게 됩니다.
또한 일부 파일은 `bytes`로, 일부 파일은 `UploadFile`로 선언할 수 있습니다.
/// warning
다수의 `File`과 `Form` 매개변수를 한 *경로 처리*에 선언하는 것이 가능하지만, 요청의 본문이 `application/json`가 아닌 `multipart/form-data`로 인코딩되기 때문에 JSON으로 받기를 기대하는 `Body` 필드를 함께 선언할 수는 없습니다.
이는 **FastAPI**의 한계가 아니라, HTTP 프로토콜의 일부입니다.
///
## 요약 { #recap }
하나의 요청으로 데이터와 파일들을 받아야 할 경우 `File`과 `Form`을 함께 사용하기 바랍니다.
================================================
FILE: docs/ko/docs/tutorial/request-forms.md
================================================
# 폼 데이터 { #form-data }
JSON 대신 폼 필드를 받아야 하는 경우 `Form`을 사용할 수 있습니다.
/// info | 정보
폼을 사용하려면, 먼저 [`python-multipart`](https://github.com/Kludex/python-multipart)를 설치하세요.
[가상 환경](../virtual-environments.md)을 생성하고 활성화한 다음, 예를 들어 다음과 같이 설치하세요:
```console
$ pip install python-multipart
```
///
## `Form` 임포트하기 { #import-form }
`fastapi`에서 `Form`을 임포트합니다:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## `Form` 매개변수 정의하기 { #define-form-parameters }
`Body` 또는 `Query`와 동일한 방식으로 폼 매개변수를 만듭니다:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
예를 들어, OAuth2 사양을 사용할 수 있는 방법 중 하나("패스워드 플로우"라고 함)로 `username`과 `password`를 폼 필드로 보내야 합니다.
사양에서는 필드 이름이 `username` 및 `password`로 정확하게 명명되어야 하고, JSON이 아닌 폼 필드로 전송해야 합니다.
`Form`을 사용하면 유효성 검사, 예제, 별칭(예: `username` 대신 `user-name`) 등을 포함하여 `Body`(및 `Query`, `Path`, `Cookie`)와 동일한 구성을 선언할 수 있습니다.
/// info | 정보
`Form`은 `Body`에서 직접 상속되는 클래스입니다.
///
/// tip | 팁
폼 본문을 선언할 때는 `Form`을 명시적으로 사용해야 합니다. 그렇지 않으면 매개변수가 쿼리 매개변수나 본문(JSON) 매개변수로 해석됩니다.
///
## "폼 필드"에 대해 { #about-form-fields }
HTML 폼(``)이 데이터를 서버로 보내는 방식은 일반적으로 해당 데이터에 대해 "특수" 인코딩을 사용하며, 이는 JSON과 다릅니다.
**FastAPI**는 JSON 대신 올바른 위치에서 해당 데이터를 읽습니다.
/// note | 기술 세부사항
폼의 데이터는 일반적으로 "미디어 유형(media type)" `application/x-www-form-urlencoded`를 사용하여 인코딩합니다.
그러나 폼에 파일이 포함된 경우, `multipart/form-data`로 인코딩합니다. 다음 장에서 파일 처리에 대해 읽을 겁니다.
이러한 인코딩 및 폼 필드에 대해 더 읽고 싶다면, [`POST`에 대한 MDN 웹 문서를 참조하세요](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | 경고
*경로 처리*에서 여러 `Form` 매개변수를 선언할 수 있지만, JSON으로 수신할 것으로 예상되는 `Body` 필드와 함께 선언할 수 없습니다. 요청 본문은 `application/json` 대신에 `application/x-www-form-urlencoded`를 사용하여 인코딩되기 때문입니다.
이는 **FastAPI**의 제한 사항이 아니며 HTTP 프로토콜의 일부입니다.
///
## 요약 { #recap }
폼 데이터 입력 매개변수를 선언하려면 `Form`을 사용하세요.
================================================
FILE: docs/ko/docs/tutorial/response-model.md
================================================
# 응답 모델 - 반환 타입 { #response-model-return-type }
*경로 처리 함수*의 **반환 타입**을 어노테이션하여 응답에 사용될 타입을 선언할 수 있습니다.
함수 **매개변수**에서 입력 데이터를 위해 사용하는 것과 동일하게 **타입 어노테이션**을 사용할 수 있으며, Pydantic 모델, 리스트, 딕셔너리, 정수/불리언 같은 스칼라 값 등을 사용할 수 있습니다.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI는 이 반환 타입을 사용하여:
* 반환된 데이터를 **검증**합니다.
* 데이터가 유효하지 않다면(예: 필드가 누락된 경우), 이는 *여러분의* 앱 코드가 깨져서 의도한 값을 반환하지 못한다는 의미이며, 잘못된 데이터를 반환하는 대신 서버 오류를 반환합니다. 이렇게 하면 여러분과 클라이언트는 기대한 데이터와 데이터 형태를 받는다는 것을 확실히 할 수 있습니다.
* OpenAPI *경로 처리*의 응답에 **JSON Schema**를 추가합니다.
* 이는 **자동 문서**에서 사용됩니다.
* 또한 자동 클라이언트 코드 생성 도구에서도 사용됩니다.
* 반환된 데이터를 Pydantic을 사용해 JSON으로 **직렬화**합니다. Pydantic은 **Rust**로 작성되어 있어 **훨씬 더 빠릅니다**.
하지만 가장 중요한 것은:
* 반환 타입에 정의된 내용으로 출력 데이터를 **제한하고 필터링**합니다.
* 이는 특히 **보안**에 매우 중요합니다. 아래에서 더 자세히 살펴보겠습니다.
## `response_model` 매개변수 { #response-model-parameter }
타입 선언이 말하는 것과 정확히 일치하지 않는 데이터를 반환해야 하거나 반환하고 싶은 경우가 있습니다.
예를 들어, **딕셔너리**나 데이터베이스 객체를 **반환**하고 싶지만, **Pydantic 모델로 선언**하고 싶을 수 있습니다. 이렇게 하면 Pydantic 모델이 반환한 객체(예: 딕셔너리나 데이터베이스 객체)에 대해 데이터 문서화, 검증 등 모든 작업을 수행합니다.
반환 타입 어노테이션을 추가했다면, 도구와 에디터가 함수가 선언한 타입(예: Pydantic 모델)과 다른 타입(예: dict)을 반환하고 있다는 (올바른) 오류로 불평할 것입니다.
그런 경우에는 반환 타입 대신 *경로 처리 데코레이터*의 매개변수 `response_model`을 사용할 수 있습니다.
`response_model` 매개변수는 모든 *경로 처리*에서 사용할 수 있습니다:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* 등.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | 참고
`response_model`은 "데코레이터" 메서드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수와 body처럼, *경로 처리 함수*의 매개변수가 아닙니다.
///
`response_model`은 Pydantic 모델 필드에 선언하는 것과 동일한 타입을 받습니다. 따라서 Pydantic 모델이 될 수도 있고, `List[Item]`처럼 Pydantic 모델의 `list`가 될 수도 있습니다.
FastAPI는 이 `response_model`을 사용해 데이터 문서화, 검증 등을 수행하고, 또한 출력 데이터를 타입 선언에 맞게 **변환하고 필터링**합니다.
/// tip | 팁
에디터, mypy 등에서 엄격한 타입 체크를 사용하고 있다면, 함수 반환 타입을 `Any`로 선언할 수 있습니다.
이렇게 하면 에디터에 의도적으로 어떤 값이든 반환한다고 알려줍니다. 하지만 FastAPI는 여전히 `response_model`을 사용하여 데이터 문서화, 검증, 필터링 등을 수행합니다.
///
### `response_model` 우선순위 { #response-model-priority }
반환 타입과 `response_model`을 둘 다 선언하면, `response_model`이 우선순위를 가지며 FastAPI에서 사용됩니다.
이렇게 하면 응답 모델과 다른 타입을 반환하는 경우에도 에디터와 mypy 같은 도구에서 사용할 올바른 타입 어노테이션을 함수에 추가할 수 있습니다. 그리고 동시에 FastAPI가 `response_model`을 사용하여 데이터 검증, 문서화 등을 수행하게 할 수도 있습니다.
또한 `response_model=None`을 사용해 해당 *경로 처리*에 대한 응답 모델 생성을 비활성화할 수도 있습니다. 이는 유효한 Pydantic 필드가 아닌 것들에 대해 타입 어노테이션을 추가하는 경우에 필요할 수 있으며, 아래 섹션 중 하나에서 예시를 볼 수 있습니다.
## 동일한 입력 데이터 반환 { #return-the-same-input-data }
여기서는 평문 비밀번호를 포함하는 `UserIn` 모델을 선언합니다:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | 정보
`EmailStr`을 사용하려면 먼저 [`email-validator`](https://github.com/JoshData/python-email-validator)를 설치하세요.
[가상 환경](../virtual-environments.md)을 생성하고, 활성화한 다음 설치해야 합니다. 예를 들어:
```console
$ pip install email-validator
```
또는 다음과 같이:
```console
$ pip install "pydantic[email]"
```
///
그리고 이 모델을 사용하여 입력을 선언하고 같은 모델로 출력을 선언합니다:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
이제 브라우저가 비밀번호로 사용자를 만들 때마다 API는 응답으로 동일한 비밀번호를 반환합니다.
이 경우, 동일한 사용자가 비밀번호를 보내는 것이므로 문제가 되지 않을 수도 있습니다.
하지만 동일한 모델을 다른 *경로 처리*에서 사용하면, 모든 클라이언트에게 사용자의 비밀번호를 보내게 될 수도 있습니다.
/// danger | 위험
모든 주의사항을 알고 있으며 무엇을 하는지 정확히 알고 있지 않다면, 이런 방식으로 사용자의 평문 비밀번호를 저장하거나 응답으로 보내지 마세요.
///
## 출력 모델 추가 { #add-an-output-model }
대신 평문 비밀번호를 포함하는 입력 모델과, 비밀번호가 없는 출력 모델을 만들 수 있습니다:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
여기서 *경로 처리 함수*가 비밀번호를 포함하는 동일한 입력 사용자를 반환하더라도:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...비밀번호를 포함하지 않는 `UserOut` 모델로 `response_model`을 선언했습니다:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
따라서 **FastAPI**는 출력 모델에 선언되지 않은 모든 데이터를 (Pydantic을 사용하여) 필터링합니다.
### `response_model` 또는 반환 타입 { #response-model-or-return-type }
이 경우 두 모델이 서로 다르기 때문에, 함수 반환 타입을 `UserOut`으로 어노테이션하면 에디터와 도구는 서로 다른 클래스인데 잘못된 타입을 반환하고 있다고 불평할 것입니다.
그래서 이 예제에서는 `response_model` 매개변수로 선언해야 합니다.
...하지만 아래를 계속 읽으면 이를 극복하는 방법을 볼 수 있습니다.
## 반환 타입과 데이터 필터링 { #return-type-and-data-filtering }
이전 예제에서 계속해 봅시다. 함수에 **하나의 타입으로 어노테이션**을 하고 싶지만, 함수에서 실제로는 **더 많은 데이터**를 포함하는 것을 반환할 수 있길 원했습니다.
FastAPI가 응답 모델을 사용해 데이터를 계속 **필터링**하길 원합니다. 그래서 함수가 더 많은 데이터를 반환하더라도, 응답에는 응답 모델에 선언된 필드만 포함되게 합니다.
이전 예제에서는 클래스가 달랐기 때문에 `response_model` 매개변수를 써야 했습니다. 하지만 이는 에디터와 도구가 함수 반환 타입을 체크해 주는 지원을 받지 못한다는 뜻이기도 합니다.
하지만 대부분 이런 작업이 필요한 경우에는, 이 예제처럼 모델로 일부 데이터를 **필터링/제거**하길 원하는 경우가 많습니다.
그리고 그런 경우에는 클래스와 상속을 사용하여 함수 **타입 어노테이션**을 활용해 에디터/도구에서 더 나은 지원을 받으면서도 FastAPI의 **데이터 필터링**을 유지할 수 있습니다.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
이를 통해 이 코드는 타입 관점에서 올바르므로 에디터와 mypy 등의 도구 지원을 받을 수 있고, 동시에 FastAPI의 데이터 필터링도 받을 수 있습니다.
이게 어떻게 동작할까요? 확인해 봅시다. 🤓
### 타입 어노테이션과 도구 지원 { #type-annotations-and-tooling }
먼저 에디터, mypy 및 기타 도구가 이를 어떻게 보는지 살펴봅시다.
`BaseUser`는 기본 필드를 가집니다. 그리고 `UserIn`은 `BaseUser`를 상속하고 `password` 필드를 추가하므로, 두 모델의 모든 필드를 포함하게 됩니다.
함수 반환 타입을 `BaseUser`로 어노테이션하지만, 실제로는 `UserIn` 인스턴스를 반환합니다.
에디터, mypy 및 기타 도구는 이에 대해 불평하지 않습니다. 타이핑 관점에서 `UserIn`은 `BaseUser`의 서브클래스이므로, `BaseUser`인 어떤 것이 기대되는 곳에서는 *유효한* 타입이기 때문입니다.
### FastAPI 데이터 필터링 { #fastapi-data-filtering }
이제 FastAPI는 반환 타입을 보고, 여러분이 반환하는 값이 해당 타입에 선언된 필드 **만** 포함하도록 보장합니다.
FastAPI는 Pydantic을 내부적으로 여러 방식으로 사용하여, 클래스 상속의 동일한 규칙이 반환 데이터 필터링에는 적용되지 않도록 합니다. 그렇지 않으면 기대한 것보다 훨씬 더 많은 데이터를 반환하게 될 수도 있습니다.
이렇게 하면 **도구 지원**이 있는 타입 어노테이션과 **데이터 필터링**이라는 두 가지 장점을 모두 얻을 수 있습니다.
## 문서에서 보기 { #see-it-in-the-docs }
자동 생성 문서를 보면 입력 모델과 출력 모델이 각자의 JSON Schema를 가지고 있음을 확인할 수 있습니다:
그리고 두 모델 모두 대화형 API 문서에 사용됩니다:
## 기타 반환 타입 어노테이션 { #other-return-type-annotations }
유효한 Pydantic 필드가 아닌 것을 반환하면서도, 도구(에디터, mypy 등)가 제공하는 지원을 받기 위해 함수에 어노테이션을 달아두는 경우가 있을 수 있습니다.
### 응답을 직접 반환하기 { #return-a-response-directly }
가장 흔한 경우는 [고급 문서에서 나중에 설명하는 대로 Response를 직접 반환하는 것](../advanced/response-directly.md)입니다.
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
이 간단한 경우는 반환 타입 어노테이션이 `Response` 클래스(또는 그 서브클래스)이기 때문에 FastAPI에서 자동으로 처리됩니다.
그리고 `RedirectResponse`와 `JSONResponse`는 모두 `Response`의 서브클래스이므로, 타입 어노테이션이 올바르기 때문에 도구들도 만족합니다.
### Response 서브클래스 어노테이션 { #annotate-a-response-subclass }
타입 어노테이션에 `Response`의 서브클래스를 사용할 수도 있습니다:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
이는 `RedirectResponse`가 `Response`의 서브클래스이기 때문에 동작하며, FastAPI가 이 간단한 경우를 자동으로 처리합니다.
### 유효하지 않은 반환 타입 어노테이션 { #invalid-return-type-annotations }
하지만 유효한 Pydantic 타입이 아닌 다른 임의의 객체(예: 데이터베이스 객체)를 반환하고, 함수에서 그렇게 어노테이션하면, FastAPI는 그 타입 어노테이션으로부터 Pydantic 응답 모델을 만들려고 시도하다가 실패합니다.
또한, 유효한 Pydantic 타입이 아닌 타입이 하나 이상 포함된 여러 타입 간의 union이 있는 경우에도 동일합니다. 예를 들어, 아래는 실패합니다 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...이는 타입 어노테이션이 Pydantic 타입이 아니고, 단일 `Response` 클래스/서브클래스도 아니며, `Response`와 `dict` 간 union(둘 중 아무거나)이기 때문에 실패합니다.
### 응답 모델 비활성화 { #disable-response-model }
위 예제에서 이어서, FastAPI가 수행하는 기본 데이터 검증, 문서화, 필터링 등을 원하지 않을 수 있습니다.
하지만 에디터나 타입 체커(예: mypy) 같은 도구 지원을 받기 위해 함수에 반환 타입 어노테이션은 유지하고 싶을 수도 있습니다.
이 경우 `response_model=None`으로 설정하여 응답 모델 생성을 비활성화할 수 있습니다:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
그러면 FastAPI는 응답 모델 생성을 건너뛰며, FastAPI 애플리케이션에 영향을 주지 않고 필요한 반환 타입 어노테이션을 어떤 것이든 사용할 수 있습니다. 🤓
## 응답 모델 인코딩 매개변수 { #response-model-encoding-parameters }
응답 모델은 아래와 같이 기본값을 가질 수 있습니다:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (또는 Python 3.10에서 `str | None = None`)은 기본값으로 `None`을 갖습니다.
* `tax: float = 10.5`는 기본값으로 `10.5`를 갖습니다.
* `tags: List[str] = []`는 기본값으로 빈 리스트 `[]`를 갖습니다.
하지만 실제로 저장되지 않았을 경우 결과에서 이를 생략하고 싶을 수 있습니다.
예를 들어, NoSQL 데이터베이스에 많은 선택적 속성이 있는 모델이 있지만, 기본값으로 가득 찬 매우 긴 JSON 응답을 보내고 싶지 않습니다.
### `response_model_exclude_unset` 매개변수 사용 { #use-the-response-model-exclude-unset-parameter }
*경로 처리 데코레이터* 매개변수 `response_model_exclude_unset=True`로 설정할 수 있습니다:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
그러면 이러한 기본값은 응답에 포함되지 않고, 실제로 설정된 값만 포함됩니다.
따라서 ID가 `foo`인 항목에 대해 해당 *경로 처리*로 요청을 보내면, (기본값을 제외한) 응답은 다음과 같습니다:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | 정보
다음도 사용할 수 있습니다:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
`exclude_defaults` 및 `exclude_none`에 대해 [Pydantic 문서](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict)에 설명된 대로 사용할 수 있습니다.
///
#### 기본값이 있는 필드에 값이 있는 데이터 { #data-with-values-for-fields-with-defaults }
하지만 ID가 `bar`인 항목처럼, 기본값이 있는 모델의 필드에 값이 있다면:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
응답에 포함됩니다.
#### 기본값과 동일한 값을 갖는 데이터 { #data-with-the-same-values-as-the-defaults }
데이터가 ID가 `baz`인 항목처럼 기본값과 동일한 값을 갖는다면:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI는 충분히 똑똑해서(사실, Pydantic이 충분히 똑똑합니다) `description`, `tax`, `tags`가 기본값과 동일하더라도, 기본값에서 가져온 것이 아니라 명시적으로 설정되었다는 것을 알아냅니다.
그래서 JSON 응답에 포함됩니다.
/// tip | 팁
기본값은 `None`뿐만 아니라 어떤 것이든 될 수 있습니다.
리스트(`[]`), `float`인 `10.5` 등이 될 수 있습니다.
///
### `response_model_include` 및 `response_model_exclude` { #response-model-include-and-response-model-exclude }
*경로 처리 데코레이터* 매개변수 `response_model_include` 및 `response_model_exclude`를 사용할 수도 있습니다.
이들은 포함(나머지 생략)하거나 제외(나머지 포함)할 어트리뷰트 이름을 담은 `str`의 `set`을 받습니다.
Pydantic 모델이 하나만 있고 출력에서 일부 데이터를 제거하려는 경우, 빠른 지름길로 사용할 수 있습니다.
/// tip | 팁
하지만 이러한 매개변수 대신, 위에서 설명한 것처럼 여러 클래스를 사용하는 것을 여전히 권장합니다.
이는 일부 어트리뷰트를 생략하기 위해 `response_model_include` 또는 `response_model_exclude`를 사용하더라도, 앱의 OpenAPI(및 문서)에 생성되는 JSON Schema가 여전히 전체 모델에 대한 스키마이기 때문입니다.
비슷하게 동작하는 `response_model_by_alias`에도 동일하게 적용됩니다.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | 팁
문법 `{"name", "description"}`은 두 값을 갖는 `set`을 만듭니다.
이는 `set(["name", "description"])`과 동일합니다.
///
#### `set` 대신 `list` 사용하기 { #using-lists-instead-of-sets }
`set`을 쓰는 것을 잊고 `list`나 `tuple`을 대신 사용하더라도, FastAPI는 이를 `set`으로 변환하므로 올바르게 동작합니다:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## 요약 { #recap }
응답 모델을 정의하고 특히 개인정보가 필터링되도록 보장하려면 *경로 처리 데코레이터*의 매개변수 `response_model`을 사용하세요.
명시적으로 설정된 값만 반환하려면 `response_model_exclude_unset`을 사용하세요.
================================================
FILE: docs/ko/docs/tutorial/response-status-code.md
================================================
# 응답 상태 코드 { #response-status-code }
응답 모델을 지정하는 것과 같은 방법으로, 어떤 *경로 처리*에서든 `status_code` 매개변수를 사용하여 응답에 사용할 HTTP 상태 코드를 선언할 수도 있습니다:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* 등
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | 참고
`status_code` 는 "데코레이터" 메소드(`get`, `post` 등)의 매개변수입니다. 모든 매개변수들과 본문처럼 *경로 처리 함수*가 아닙니다.
///
`status_code` 매개변수는 HTTP 상태 코드를 숫자로 입력받습니다.
/// info | 정보
`status_code` 는 파이썬의 [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) 와 같은 `IntEnum` 을 입력받을 수도 있습니다.
///
`status_code` 매개변수는:
* 응답에서 해당 상태 코드를 반환합니다.
* 상태 코드를 OpenAPI 스키마(따라서, 사용자 인터페이스에도)에 문서화합니다:
/// note | 참고
일부 응답 코드(다음 섹션 참고)는 응답에 본문이 없다는 것을 나타냅니다.
FastAPI는 이를 알고 있으며, 응답 본문이 없다고 명시하는 OpenAPI 문서를 생성합니다.
///
## HTTP 상태 코드에 대하여 { #about-http-status-codes }
/// note | 참고
만약 HTTP 상태 코드가 무엇인지 이미 알고 있다면, 다음 섹션으로 넘어가세요.
///
HTTP에서는 응답의 일부로 3자리 숫자 상태 코드를 보냅니다.
이 상태 코드들은 이를 식별할 수 있도록 이름이 연결되어 있지만, 중요한 부분은 숫자입니다.
요약하자면:
* `100 - 199` 는 "정보"용입니다. 직접 사용할 일은 거의 없습니다. 이 상태 코드를 갖는 응답은 본문을 가질 수 없습니다.
* **`200 - 299`** 는 "성공적인" 응답을 위한 것입니다. 가장 많이 사용하게 될 유형입니다.
* `200` 은 기본 상태 코드로, 모든 것이 "OK"임을 의미합니다.
* 다른 예로는 `201` "생성됨"이 있습니다. 일반적으로 데이터베이스에 새 레코드를 생성한 후 사용합니다.
* 특별한 경우로 `204` "내용 없음"이 있습니다. 이 응답은 클라이언트에게 반환할 내용이 없을 때 사용되며, 따라서 응답은 본문을 가지면 안 됩니다.
* **`300 - 399`** 는 "리다이렉션"용입니다. 이 상태 코드를 갖는 응답은 본문이 있을 수도 없을 수도 있으며, 본문이 없어야 하는 `304` "수정되지 않음"을 제외합니다.
* **`400 - 499`** 는 "클라이언트 오류" 응답을 위한 것입니다. 아마 두 번째로 가장 많이 사용하게 될 유형입니다.
* 예를 들어 `404` 는 "찾을 수 없음" 응답을 위해 사용합니다.
* 클라이언트의 일반적인 오류에는 `400` 을 그냥 사용할 수 있습니다.
* `500 - 599` 는 서버 오류에 사용됩니다. 직접 사용할 일은 거의 없습니다. 애플리케이션 코드의 일부나 서버에서 문제가 발생하면 자동으로 이들 상태 코드 중 하나를 반환합니다.
/// tip | 팁
각 상태 코드와 어떤 코드가 어떤 용도인지 더 알고 싶다면 [MDN의 HTTP 상태 코드에 관한 문서](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)를 확인하세요.
///
## 이름을 기억하는 쉬운 방법 { #shortcut-to-remember-the-names }
이전 예시를 다시 확인해보겠습니다:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` 은 "생성됨"을 위한 상태 코드입니다.
하지만 각각의 코드가 무엇을 의미하는지 외울 필요는 없습니다.
`fastapi.status` 의 편의 변수를 사용할 수 있습니다.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
이것들은 단지 편의를 위한 것으로, 동일한 숫자를 갖고 있지만, 이를 통해 편집기의 자동완성 기능으로 찾을 수 있습니다:
/// note | 기술 세부사항
`from starlette import status` 역시 사용할 수 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `fastapi.status` 와 동일한 `starlette.status` 도 제공합니다. 하지만 이것은 Starlette로부터 직접 제공됩니다.
///
## 기본값 변경 { #changing-the-default }
나중에 [고급 사용자 지침서](../advanced/response-change-status-code.md)에서, 여기서 선언하는 기본값과 다른 상태 코드를 반환하는 방법을 확인할 수 있습니다.
================================================
FILE: docs/ko/docs/tutorial/schema-extra-example.md
================================================
# 요청 예제 데이터 선언 { #declare-request-example-data }
여러분의 앱이 받을 수 있는 데이터 예제를 선언할 수 있습니다.
여기 이를 위한 몇 가지 방식이 있습니다.
## Pydantic 모델 속 추가 JSON 스키마 데이터 { #extra-json-schema-data-in-pydantic-models }
생성된 JSON 스키마에 추가될 Pydantic 모델을 위한 `examples`을 선언할 수 있습니다.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
추가 정보는 있는 그대로 해당 모델의 **JSON 스키마** 결과에 추가되고, API 문서에서 사용합니다.
[Pydantic 문서: Configuration](https://docs.pydantic.dev/latest/api/config/)에 설명된 것처럼 `dict`를 받는 `model_config` 어트리뷰트를 사용할 수 있습니다.
`"json_schema_extra"`를 생성된 JSON 스키마에서 보여주고 싶은 별도의 데이터와 `examples`를 포함하는 `dict`으로 설정할 수 있습니다.
/// tip | 팁
JSON 스키마를 확장하고 여러분의 별도의 자체 데이터를 추가하기 위해 같은 기술을 사용할 수 있습니다.
예를 들면, 프론트엔드 사용자 인터페이스에 메타데이터를 추가하는 등에 사용할 수 있습니다.
///
/// info | 정보
(FastAPI 0.99.0부터 쓰이기 시작한) OpenAPI 3.1.0은 **JSON 스키마** 표준의 일부인 `examples`에 대한 지원을 추가했습니다.
그 전에는, 하나의 예제만 가능한 `example` 키워드만 지원했습니다. 이는 아직 OpenAPI 3.1.0에서 지원하지만, 지원이 종료될 것이며 JSON 스키마 표준에 포함되지 않습니다. 그렇기에 `example`을 `examples`으로 이전하는 것을 추천합니다. 🤓
이 페이지 끝에서 더 많은 내용을 읽을 수 있습니다.
///
## `Field` 추가 인자 { #field-additional-arguments }
Pydantic 모델과 같이 `Field()`를 사용할 때 추가적인 `examples`를 선언할 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## JSON Schema에서의 `examples` - OpenAPI { #examples-in-json-schema-openapi }
다음 중 하나를 사용할 때:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
**OpenAPI** 안의 **JSON 스키마**에 추가될 부가적인 정보를 포함한 `examples` 모음을 선언할 수도 있습니다.
### `examples`를 포함한 `Body` { #body-with-examples }
여기, `Body()`에 예상되는 예제 데이터 하나를 포함한 `examples`를 넘겼습니다:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### 문서 UI 예시 { #example-in-the-docs-ui }
위의 어느 방법과 함께라면 `/docs`에서 다음과 같이 보일 것입니다:
### 다중 `examples`를 포함한 `Body` { #body-with-multiple-examples }
물론 여러 `examples`를 넘길 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
이와 같이 하면 예제들은 그 본문 데이터의 내부 **JSON 스키마**의 일부가 될 것입니다.
그럼에도 불구하고, 지금 이 문서를 작성하는 시간에, 문서 UI를 보여주는 역할을 맡은 Swagger UI는 **JSON 스키마** 속 데이터를 위한 여러 예제의 표현을 지원하지 않습니다. 하지만 해결 방안을 밑에서 읽어보세요.
### OpenAPI-특화 `examples` { #openapi-specific-examples }
**JSON 스키마**가 `examples`를 지원하기 전부터 OpenAPI는 `examples`이라 불리는 다른 필드를 지원해 왔습니다.
이 **OpenAPI-특화** `examples`는 OpenAPI 명세서의 다른 구역으로 들어갑니다. 각 JSON 스키마 내부가 아니라 **각 *경로 처리* 세부 정보**에 포함됩니다.
그리고 Swagger UI는 이 특정한 `examples` 필드를 한동안 지원했습니다. 그래서, 이를 다른 **문서 UI에 있는 예제**를 **표시**하기 위해 사용할 수 있습니다.
이 OpenAPI-특화 필드인 `examples`의 형태는 (`list` 대신에) **다중 예제**가 포함된 `dict`이며, 각각의 별도 정보 또한 **OpenAPI**에 추가될 것입니다.
이는 OpenAPI에 포함된 각 JSON 스키마 안으로 포함되지 않으며, *경로 처리*에 직접적으로 포함됩니다.
### `openapi_examples` 매개변수 사용하기 { #using-the-openapi-examples-parameter }
다음에 대해 FastAPI에서 매개변수 `openapi_examples`로 OpenAPI-특화 `examples`를 선언할 수 있습니다:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
`dict`의 키는 각 예제를 식별하고, 각 값은 또 다른 `dict`입니다.
`examples` 안의 각 특정 예제 `dict`에는 다음이 포함될 수 있습니다:
* `summary`: 예제에 대한 짧은 설명문.
* `description`: 마크다운 텍스트를 포함할 수 있는 긴 설명문.
* `value`: 실제로 보여지는 예시, 예를 들면 `dict`.
* `externalValue`: `value`의 대안이며 예제를 가리키는 URL. 비록 `value`처럼 많은 도구를 지원하지 못할 수 있습니다.
이를 다음과 같이 사용할 수 있습니다:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### 문서 UI에서의 OpenAPI 예시 { #openapi-examples-in-the-docs-ui }
`Body()`에 `openapi_examples`가 추가되면 `/docs`는 다음과 같이 보일 것입니다:
## 기술적 세부 사항 { #technical-details }
/// tip | 팁
이미 **FastAPI**의 **0.99.0 혹은 그 이상** 버전을 사용하고 있다면, 이 세부 사항을 **스킵**해도 상관 없을 것입니다.
세부 사항은 OpenAPI 3.1.0이 사용가능하기 전, 예전 버전과 더 관련있습니다.
간략한 OpenAPI와 JSON 스키마 **역사 강의**로 생각할 수 있습니다. 🤓
///
/// warning | 경고
표준 **JSON 스키마**와 **OpenAPI**에 대한 아주 기술적인 세부사항입니다.
만약 위의 생각이 작동한다면, 그것으로 충분하며 이 세부 사항은 필요없을 것이니, 마음 편하게 스킵하셔도 됩니다.
///
OpenAPI 3.1.0 전에 OpenAPI는 오래된 **JSON 스키마**의 수정된 버전을 사용했습니다.
JSON 스키마는 `examples`를 가지고 있지 않았고, 따라서 OpenAPI는 그들만의 `example` 필드를 수정된 버전에 추가했습니다.
OpenAPI는 또한 `example`과 `examples` 필드를 명세서의 다른 부분에 추가했습니다:
* [`Parameter Object` (명세서에 있는)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object)는 FastAPI의 다음 기능에서 쓰였습니다:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, `Media Type Object` (명세서에 있는)의 `content` 필드에 있는](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object)는 FastAPI의 다음 기능에서 쓰였습니다:
* `Body()`
* `File()`
* `Form()`
/// info | 정보
이 예전 OpenAPI-특화 `examples` 매개변수는 이제 FastAPI `0.103.0`부터 `openapi_examples`입니다.
///
### JSON 스키마의 `examples` 필드 { #json-schemas-examples-field }
하지만, 후에 JSON 스키마는 [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) 필드를 명세서의 새 버전에 추가했습니다.
그리고 새로운 OpenAPI 3.1.0은 이 새로운 `examples` 필드가 포함된 최신 버전 (JSON 스키마 2020-12)을 기반으로 했습니다.
그리고 이제 이 새로운 `examples` 필드는 이제 지원 중단된, 예전의 단일 (그리고 커스텀) `example` 필드보다 우선됩니다.
JSON 스키마의 새로운 `examples` 필드는 예제의 **단순한 `list`**일 뿐이며, (위에서 상술한 것처럼) OpenAPI의 다른 곳에 존재하는 추가 메타데이터가 있는 dict가 아닙니다.
/// info | 정보
더 쉽고 새로운 JSON 스키마와의 통합과 함께 OpenAPI 3.1.0가 배포되었지만, 잠시동안 자동 문서 생성을 제공하는 도구인 Swagger UI는 OpenAPI 3.1.0을 지원하지 않았습니다 (5.0.0 버전부터 지원합니다 🎉).
이로인해, FastAPI 0.99.0 이전 버전은 아직 OpenAPI 3.1.0 보다 낮은 버전을 사용했습니다.
///
### Pydantic과 FastAPI `examples` { #pydantic-and-fastapi-examples }
Pydantic 모델 안에 `examples`를 추가할 때, `schema_extra` 또는 `Field(examples=["something"])`를 사용하면 그 예제는 해당 Pydantic 모델의 **JSON 스키마**에 추가됩니다.
그리고 Pydantic 모델의 **JSON 스키마**는 API의 **OpenAPI**에 포함되고, 그 후 문서 UI 속에서 사용됩니다.
FastAPI 0.99.0 이전 버전에서 (0.99.0 이상 버전은 새로운 OpenAPI 3.1.0을 사용합니다), 다른 유틸리티(`Query()`, `Body()` 등)와 함께 `example` 또는 `examples`를 사용했을 때, 그러한 예제는 그 데이터를 설명하는 JSON 스키마에 추가되지 않고 (OpenAPI 자체의 JSON 스키마에도 포함되지 않습니다), OpenAPI의 *경로 처리* 선언에 직접적으로 추가됩니다 (JSON 스키마를 사용하는 OpenAPI 부분 밖에서).
하지만 이제 FastAPI 0.99.0 및 이후 버전에서는 JSON 스키마 2020-12를 사용하는 OpenAPI 3.1.0과 Swagger UI 5.0.0 및 이후 버전을 사용하기 때문에, 모든 것이 더 일관성을 띄고 예제도 JSON 스키마에 포함됩니다.
### Swagger UI와 OpenAPI-특화 `examples` { #swagger-ui-and-openapi-specific-examples }
Swagger UI는 다중 JSON 스키마 예제를 지원하지 않았기 때문에(2023-08-26 기준), 사용자는 문서에 여러 예제를 표시할 방법이 없었습니다.
이를 해결하기 위해, FastAPI `0.103.0`은 새로운 매개변수인 `openapi_examples`로 동일한 예전 **OpenAPI-특화** `examples` 필드를 선언하는 **지원**을 추가했습니다. 🤓
### 요약 { #summary }
저는 역사를 그다지 좋아하는 편이 아니라고 말하고는 했지만... "기술 역사" 강의를 하는 지금의 저를 보세요. 😅
요약하자면 **FastAPI 0.99.0 혹은 그 이상의 버전**으로 업그레이드하면, 많은 것들이 훨씬 더 **단순하고, 일관적이며 직관적**이 되며, 여러분은 이 모든 역사적 세부 사항을 알 필요가 없습니다. 😎
================================================
FILE: docs/ko/docs/tutorial/security/first-steps.md
================================================
# 보안 - 첫 단계 { #security-first-steps }
어떤 도메인에 **backend** API가 있다고 가정해 보겠습니다.
그리고 다른 도메인에 **frontend**가 있거나, 같은 도메인의 다른 경로에 있거나(또는 모바일 애플리케이션에 있을 수도 있습니다).
그리고 frontend가 **username**과 **password**를 사용해 backend에 인증할 수 있는 방법이 필요하다고 해봅시다.
**FastAPI**와 함께 **OAuth2**를 사용해서 이를 구현할 수 있습니다.
하지만 필요한 작은 정보 조각들을 찾기 위해 길고 긴 전체 스펙을 읽느라 시간을 쓰지 않도록 하겠습니다.
보안을 처리하기 위해 **FastAPI**가 제공하는 도구들을 사용해 봅시다.
## 어떻게 보이는지 { #how-it-looks }
먼저 코드를 그냥 사용해서 어떻게 동작하는지 보고, 그다음에 무슨 일이 일어나는지 이해하러 다시 돌아오겠습니다.
## `main.py` 만들기 { #create-main-py }
예제를 파일 `main.py`에 복사하세요:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## 실행하기 { #run-it }
/// info | 정보
[`python-multipart`](https://github.com/Kludex/python-multipart) 패키지는 `pip install "fastapi[standard]"` 명령을 실행하면 **FastAPI**와 함께 자동으로 설치됩니다.
하지만 `pip install fastapi` 명령을 사용하면 `python-multipart` 패키지가 기본으로 포함되지 않습니다.
수동으로 설치하려면, [가상 환경](../../virtual-environments.md)을 만들고 활성화한 다음, 아래로 설치하세요:
```console
$ pip install python-multipart
```
이는 **OAuth2**가 `username`과 `password`를 보내기 위해 "form data"를 사용하기 때문입니다.
///
다음으로 예제를 실행하세요:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## 확인하기 { #check-it }
대화형 문서로 이동하세요: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
다음과 비슷한 화면이 보일 것입니다:
/// check | Authorize 버튼!
반짝이는 새 "Authorize" 버튼이 이미 있습니다.
그리고 *경로 처리*에는 오른쪽 상단에 클릭할 수 있는 작은 자물쇠가 있습니다.
///
그리고 이를 클릭하면 `username`과 `password`(그리고 다른 선택적 필드들)를 입력할 수 있는 작은 인증 폼이 나타납니다:
/// note | 참고
폼에 무엇을 입력하든 아직은 동작하지 않습니다. 하지만 곧 여기까지 구현할 것입니다.
///
물론 이것은 최종 사용자를 위한 frontend는 아니지만, 모든 API를 대화형으로 문서화하는 훌륭한 자동 도구입니다.
frontend 팀(그게 본인일 수도 있습니다)이 사용할 수 있습니다.
서드파티 애플리케이션과 시스템에서도 사용할 수 있습니다.
그리고 동일한 애플리케이션을 디버그하고, 확인하고, 테스트하기 위해 본인이 사용할 수도 있습니다.
## `password` 플로우 { #the-password-flow }
이제 조금 돌아가서 이것들이 무엇인지 이해해 봅시다.
`password` "flow"는 보안과 인증을 처리하기 위해 OAuth2에서 정의한 여러 방식("flows") 중 하나입니다.
OAuth2는 backend 또는 API가 사용자를 인증하는 서버와 독립적일 수 있도록 설계되었습니다.
하지만 이 경우에는 같은 **FastAPI** 애플리케이션이 API와 인증을 모두 처리합니다.
따라서, 단순화된 관점에서 다시 정리해보면:
* 사용자가 frontend에서 `username`과 `password`를 입력하고 `Enter`를 누릅니다.
* frontend(사용자의 브라우저에서 실행됨)는 해당 `username`과 `password`를 우리 API의 특정 URL로 보냅니다(`tokenUrl="token"`로 선언됨).
* API는 `username`과 `password`를 확인하고 "token"으로 응답합니다(아직 아무것도 구현하지 않았습니다).
* "token"은 나중에 이 사용자를 검증하는 데 사용할 수 있는 어떤 내용이 담긴 문자열일 뿐입니다.
* 보통 token은 일정 시간이 지나면 만료되도록 설정합니다.
* 그래서 사용자는 나중에 어느 시점엔 다시 로그인해야 합니다.
* 그리고 token이 도난당하더라도 위험이 더 낮습니다. 대부분의 경우 영구적으로 항상 동작하는 키와는 다릅니다.
* frontend는 그 token을 임시로 어딘가에 저장합니다.
* 사용자가 frontend에서 클릭해서 frontend 웹 앱의 다른 섹션으로 이동합니다.
* frontend는 API에서 더 많은 데이터를 가져와야 합니다.
* 하지만 그 특정 endpoint에는 인증이 필요합니다.
* 그래서 우리 API에 인증하기 위해 `Authorization` 헤더를, 값은 `Bearer `에 token을 더한 형태로 보냅니다.
* token에 `foobar`가 들어 있다면 `Authorization` 헤더의 내용은 `Bearer foobar`가 됩니다.
## **FastAPI**의 `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer }
**FastAPI**는 이런 보안 기능을 구현하기 위해, 서로 다른 추상화 수준에서 여러 도구를 제공합니다.
이 예제에서는 **OAuth2**의 **Password** 플로우와 **Bearer** token을 사용합니다. 이를 위해 `OAuth2PasswordBearer` 클래스를 사용합니다.
/// info | 정보
"bearer" token만이 유일한 선택지는 아닙니다.
하지만 이 사용 사례에는 가장 적합한 선택입니다.
또한 OAuth2 전문가로서 왜 다른 옵션이 더 적합한지 정확히 아는 경우가 아니라면, 대부분의 사용 사례에도 가장 적합할 가능성이 큽니다.
그런 경우를 위해서도 **FastAPI**는 이를 구성할 수 있는 도구를 제공합니다.
///
`OAuth2PasswordBearer` 클래스의 인스턴스를 만들 때 `tokenUrl` 파라미터를 전달합니다. 이 파라미터에는 클라이언트(사용자의 브라우저에서 실행되는 frontend)가 token을 받기 위해 `username`과 `password`를 보낼 URL이 들어 있습니다.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | 팁
여기서 `tokenUrl="token"`은 아직 만들지 않은 상대 URL `token`을 가리킵니다. 상대 URL이므로 `./token`과 동일합니다.
상대 URL을 사용하므로, 예를 들어 API가 `https://example.com/`에 있다면 `https://example.com/token`을 가리킵니다. 하지만 API가 `https://example.com/api/v1/`에 있다면 `https://example.com/api/v1/token`을 가리킵니다.
상대 URL을 사용하는 것은 [프록시 뒤에서](../../advanced/behind-a-proxy.md) 같은 고급 사용 사례에서도 애플리케이션이 계속 동작하도록 보장하는 데 중요합니다.
///
이 파라미터는 그 endpoint / *경로 처리*를 만들지는 않지만, URL `/token`이 클라이언트가 token을 얻기 위해 사용해야 할 URL이라고 선언합니다. 이 정보는 OpenAPI에 사용되고, 이어서 대화형 API 문서 시스템에서도 사용됩니다.
곧 실제 경로 처리를 만들 것입니다.
/// info | 정보
엄격한 "Pythonista"라면 `token_url` 대신 `tokenUrl` 같은 파라미터 이름 스타일이 마음에 들지 않을 수도 있습니다.
이는 OpenAPI 스펙에서 사용하는 이름과 동일하게 맞춘 것이기 때문입니다. 그래서 이런 보안 스킴에 대해 더 조사해야 할 때, 그대로 복사해서 붙여 넣어 더 많은 정보를 찾을 수 있습니다.
///
`oauth2_scheme` 변수는 `OAuth2PasswordBearer`의 인스턴스이지만, "callable"이기도 합니다.
다음처럼 호출될 수 있습니다:
```Python
oauth2_scheme(some, parameters)
```
따라서 `Depends`와 함께 사용할 수 있습니다.
### 사용하기 { #use-it }
이제 `Depends`로 `oauth2_scheme`를 의존성에 전달할 수 있습니다.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
이 의존성은 `str`을 제공하고, 그 값은 *경로 처리 함수*의 파라미터 `token`에 할당됩니다.
**FastAPI**는 이 의존성을 사용해 OpenAPI 스키마(및 자동 API 문서)에 "security scheme"를 정의할 수 있다는 것을 알게 됩니다.
/// info | 기술 세부사항
**FastAPI**는 (의존성에 선언된) `OAuth2PasswordBearer` 클래스를 사용해 OpenAPI에서 보안 스킴을 정의할 수 있다는 것을 알고 있습니다. 이는 `OAuth2PasswordBearer`가 `fastapi.security.oauth2.OAuth2`를 상속하고, 이것이 다시 `fastapi.security.base.SecurityBase`를 상속하기 때문입니다.
OpenAPI(및 자동 API 문서)와 통합되는 모든 보안 유틸리티는 `SecurityBase`를 상속하며, 그래서 **FastAPI**가 이를 OpenAPI에 어떻게 통합할지 알 수 있습니다.
///
## 무엇을 하는지 { #what-it-does }
요청에서 `Authorization` 헤더를 찾아, 값이 `Bearer `에 어떤 token이 붙은 형태인지 확인한 뒤, 그 token을 `str`로 반환합니다.
`Authorization` 헤더가 없거나, 값에 `Bearer ` token이 없다면, 곧바로 401 상태 코드 오류(`UNAUTHORIZED`)로 응답합니다.
오류를 반환하기 위해 token이 존재하는지 직접 확인할 필요조차 없습니다. 함수가 실행되었다면 그 token에는 `str`이 들어 있다고 확신할 수 있습니다.
대화형 문서에서 이미 시도해 볼 수 있습니다:
아직 token의 유효성을 검증하진 않지만, 이것만으로도 시작은 된 셈입니다.
## 요약 { #recap }
즉, 추가로 3~4줄만으로도 이미 원시적인 형태의 보안을 갖추게 됩니다.
================================================
FILE: docs/ko/docs/tutorial/security/get-current-user.md
================================================
# 현재 사용자 가져오기 { #get-current-user }
이전 장에서 (의존성 주입 시스템을 기반으로 한) 보안 시스템은 *경로 처리 함수*에 `str`로 `token`을 제공했습니다:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
하지만 이는 여전히 그다지 유용하지 않습니다.
현재 사용자를 제공하도록 해봅시다.
## 사용자 모델 생성하기 { #create-a-user-model }
먼저 Pydantic 사용자 모델을 만들어 봅시다.
Pydantic을 사용해 본문을 선언하는 것과 같은 방식으로, 다른 곳에서도 어디서든 사용할 수 있습니다:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## `get_current_user` 의존성 생성하기 { #create-a-get-current-user-dependency }
의존성 `get_current_user`를 만들어 봅시다.
의존성이 하위 의존성을 가질 수 있다는 것을 기억하시나요?
`get_current_user`는 이전에 생성한 것과 동일한 `oauth2_scheme`에 대한 의존성을 갖게 됩니다.
이전에 *경로 처리*에서 직접 수행했던 것과 동일하게, 새 의존성 `get_current_user`는 하위 의존성 `oauth2_scheme`로부터 `str`로 `token`을 받게 됩니다:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## 사용자 가져오기 { #get-the-user }
`get_current_user`는 우리가 만든 (가짜) 유틸리티 함수를 사용합니다. 이 함수는 `str`로 토큰을 받아 Pydantic `User` 모델을 반환합니다:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## 현재 사용자 주입하기 { #inject-the-current-user }
이제 *경로 처리*에서 `get_current_user`와 함께 같은 `Depends`를 사용할 수 있습니다:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
`current_user`의 타입을 Pydantic 모델 `User`로 선언한다는 점에 주목하세요.
이는 함수 내부에서 자동 완성과 타입 체크에 도움을 줍니다.
/// tip | 팁
요청 본문도 Pydantic 모델로 선언된다는 것을 기억하실지도 모릅니다.
여기서 **FastAPI**는 `Depends`를 사용하고 있기 때문에 혼동하지 않습니다.
///
/// check | 확인
이 의존성 시스템이 설계된 방식은 모두 `User` 모델을 반환하는 서로 다른 의존성(서로 다른 "dependables")을 가질 수 있도록 합니다.
해당 타입의 데이터를 반환할 수 있는 의존성이 하나만 있어야 하는 것으로 제한되지 않습니다.
///
## 다른 모델 { #other-models }
이제 *경로 처리 함수*에서 현재 사용자를 직접 가져올 수 있으며, `Depends`를 사용해 **의존성 주입** 수준에서 보안 메커니즘을 처리할 수 있습니다.
그리고 보안 요구 사항을 위해 어떤 모델이나 데이터든 사용할 수 있습니다(이 경우 Pydantic 모델 `User`).
하지만 특정 데이터 모델, 클래스 또는 타입만 사용해야 하는 것은 아닙니다.
모델에 `id`와 `email`이 있고 `username`은 없게 하고 싶으신가요? 물론입니다. 같은 도구를 사용할 수 있습니다.
`str`만 갖고 싶으신가요? 아니면 `dict`만요? 또는 데이터베이스 클래스 모델 인스턴스를 직접 쓰고 싶으신가요? 모두 같은 방식으로 동작합니다.
애플리케이션에 로그인하는 사용자는 없고, 액세스 토큰만 가진 로봇, 봇 또는 다른 시스템만 있나요? 이것도 마찬가지로 모두 동일하게 동작합니다.
애플리케이션에 필요한 어떤 종류의 모델, 어떤 종류의 클래스, 어떤 종류의 데이터베이스든 사용하세요. **FastAPI**는 의존성 주입 시스템으로 이를 지원합니다.
## 코드 크기 { #code-size }
이 예시는 장황해 보일 수 있습니다. 동일한 파일에서 보안, 데이터 모델, 유틸리티 함수 및 *경로 처리*를 섞어서 사용하고 있다는 점을 기억하세요.
하지만 여기 핵심이 있습니다.
보안과 의존성 주입 관련 코드는 한 번만 작성합니다.
그리고 원하는 만큼 복잡하게 만들 수 있습니다. 그럼에도 여전히 한 번만, 한 곳에만 작성하면 됩니다. 유연성을 모두 유지하면서요.
하지만 같은 보안 시스템을 사용해 수천 개의 엔드포인트(*경로 처리*)를 가질 수 있습니다.
그리고 그들 모두(또는 원하는 일부)는 이러한 의존성 또는 여러분이 생성한 다른 의존성을 재사용하는 이점을 얻을 수 있습니다.
그리고 이 수천 개의 *경로 처리*는 3줄 정도로도 만들 수 있습니다:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## 요약 { #recap }
이제 *경로 처리 함수*에서 현재 사용자를 직접 가져올 수 있습니다.
우리는 이미 절반은 왔습니다.
사용자/클라이언트가 실제로 `username`과 `password`를 보내도록 하는 *경로 처리*만 추가하면 됩니다.
다음에 이어집니다.
================================================
FILE: docs/ko/docs/tutorial/security/index.md
================================================
# 보안 { #security }
보안, 인증(authentication), 인가(authorization)를 처리하는 방법은 매우 다양합니다.
그리고 보통 복잡하고 "어려운" 주제이기도 합니다.
많은 프레임워크와 시스템에서 보안과 인증만 처리하는 데도 큰 노력과 코드가 필요합니다(많은 경우 작성된 전체 코드의 50% 이상이 될 수도 있습니다).
**FastAPI**는 모든 보안 명세를 전부 공부하고 배울 필요 없이, 표준적인 방식으로 쉽고 빠르게 **보안(Security)** 을 다룰 수 있도록 여러 도구를 제공합니다.
하지만 먼저, 몇 가지 작은 개념을 확인해 보겠습니다.
## 급하신가요? { #in-a-hurry }
이 용어들에 관심이 없고 사용자명과 비밀번호 기반 인증을 사용한 보안을 *지금 당장* 추가하기만 하면 된다면, 다음 장들로 넘어가세요.
## OAuth2 { #oauth2 }
OAuth2는 인증과 인가를 처리하는 여러 방법을 정의하는 명세입니다.
상당히 방대한 명세이며 여러 복잡한 사용 사례를 다룹니다.
"제3자"를 사용해 인증하는 방법도 포함합니다.
바로 `"Facebook, Google, X (Twitter), GitHub로 로그인"` 같은 시스템들이 내부적으로 사용하는 방식입니다.
### OAuth 1 { #oauth-1 }
OAuth 1도 있었는데, 이는 OAuth2와 매우 다르고 통신을 암호화하는 방법까지 직접 명세에 포함했기 때문에 더 복잡했습니다.
요즘에는 그다지 인기 있거나 사용되지는 않습니다.
OAuth2는 통신을 어떻게 암호화할지는 명세하지 않고, 애플리케이션이 HTTPS로 제공될 것을 기대합니다.
/// tip | 팁
**배포**에 대한 섹션에서 Traefik과 Let's Encrypt를 사용해 무료로 HTTPS를 설정하는 방법을 볼 수 있습니다.
///
## OpenID Connect { #openid-connect }
OpenID Connect는 **OAuth2**를 기반으로 한 또 다른 명세입니다.
OAuth2에서 비교적 모호한 부분을 일부 구체화하여 상호 운용성을 높이려는 확장입니다.
예를 들어, Google 로그인은 OpenID Connect를 사용합니다(내부적으로는 OAuth2를 사용).
하지만 Facebook 로그인은 OpenID Connect를 지원하지 않습니다. 자체적인 변형의 OAuth2를 사용합니다.
### OpenID("OpenID Connect"가 아님) { #openid-not-openid-connect }
"OpenID"라는 명세도 있었습니다. 이는 **OpenID Connect**와 같은 문제를 해결하려고 했지만, OAuth2를 기반으로 하지 않았습니다.
따라서 완전히 별도의 추가 시스템이었습니다.
요즘에는 그다지 인기 있거나 사용되지는 않습니다.
## OpenAPI { #openapi }
OpenAPI(이전에는 Swagger로 알려짐)는 API를 구축하기 위한 공개 명세입니다(현재 Linux Foundation의 일부).
**FastAPI**는 **OpenAPI**를 기반으로 합니다.
이 덕분에 여러 자동 대화형 문서 인터페이스, 코드 생성 등과 같은 기능을 사용할 수 있습니다.
OpenAPI에는 여러 보안 "scheme"을 정의하는 방법이 있습니다.
이를 사용하면 이러한 대화형 문서 시스템을 포함해, 표준 기반 도구들을 모두 활용할 수 있습니다.
OpenAPI는 다음 보안 scheme들을 정의합니다:
* `apiKey`: 다음에서 전달될 수 있는 애플리케이션 전용 키:
* 쿼리 파라미터
* 헤더
* 쿠키
* `http`: 표준 HTTP 인증 시스템, 예:
* `bearer`: `Authorization` 헤더에 `Bearer ` + 토큰 값을 넣는 방식. OAuth2에서 유래했습니다.
* HTTP Basic 인증
* HTTP Digest 등
* `oauth2`: 보안을 처리하는 모든 OAuth2 방식(이를 "flow"라고 부릅니다).
* 이 flow들 중 여러 개는 OAuth 2.0 인증 제공자(예: Google, Facebook, X (Twitter), GitHub 등)를 구축하는 데 적합합니다:
* `implicit`
* `clientCredentials`
* `authorizationCode`
* 하지만 같은 애플리케이션에서 직접 인증을 처리하는 데 완벽하게 사용할 수 있는 특정 "flow"도 하나 있습니다:
* `password`: 다음 장들에서 이에 대한 예시를 다룹니다.
* `openIdConnect`: OAuth2 인증 데이터를 자동으로 탐색(discover)하는 방법을 정의합니다.
* 이 자동 탐색은 OpenID Connect 명세에서 정의됩니다.
/// tip | 팁
Google, Facebook, X (Twitter), GitHub 등 다른 인증/인가 제공자를 통합하는 것도 가능하며 비교적 쉽습니다.
가장 복잡한 문제는 그런 인증/인가 제공자 자체를 구축하는 것이지만, **FastAPI**는 어려운 작업을 대신 처리해 주면서 이를 쉽게 할 수 있는 도구를 제공합니다.
///
## **FastAPI** 유틸리티 { #fastapi-utilities }
FastAPI는 `fastapi.security` 모듈에서 각 보안 scheme에 대한 여러 도구를 제공하며, 이러한 보안 메커니즘을 더 쉽게 사용할 수 있게 해줍니다.
다음 장들에서는 **FastAPI**가 제공하는 도구를 사용해 API에 보안을 추가하는 방법을 보게 될 것입니다.
또한 대화형 문서 시스템에 어떻게 자동으로 통합되는지도 확인하게 됩니다.
================================================
FILE: docs/ko/docs/tutorial/security/oauth2-jwt.md
================================================
# 패스워드(해싱 포함)를 사용하는 OAuth2, JWT 토큰을 사용하는 Bearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
모든 보안 흐름을 구성했으므로, 이제 JWT 토큰과 안전한 패스워드 해싱을 사용해 애플리케이션을 실제로 안전하게 만들겠습니다.
이 코드는 실제로 애플리케이션에서 사용할 수 있으며, 패스워드 해시를 데이터베이스에 저장하는 등의 작업에 활용할 수 있습니다.
이전 장에서 멈춘 지점부터 시작해 내용을 확장해 나가겠습니다.
## JWT 알아보기 { #about-jwt }
JWT는 "JSON Web Tokens"를 의미합니다.
JSON 객체를 공백이 없는 길고 밀집된 문자열로 부호화하는 표준입니다. 다음과 같은 형태입니다:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
암호화된 것이 아니므로, 누구나 내용에서 정보를 복원할 수 있습니다.
하지만 서명되어 있습니다. 따라서 자신이 발급한 토큰을 받았을 때, 실제로 자신이 발급한 것이 맞는지 검증할 수 있습니다.
예를 들어 만료 기간이 1주일인 토큰을 생성할 수 있습니다. 그리고 사용자가 다음 날 토큰을 가지고 돌아오면, 그 사용자가 시스템에 여전히 로그인되어 있다는 것을 알 수 있습니다.
1주일 뒤에는 토큰이 만료되고 사용자는 인가되지 않으므로 새 토큰을 받기 위해 다시 로그인해야 합니다. 그리고 사용자(또는 제3자)가 만료 시간을 바꾸기 위해 토큰을 수정하려고 하면, 서명이 일치하지 않기 때문에 이를 알아챌 수 있습니다.
JWT 토큰을 직접 다뤄보고 동작 방식을 확인해보고 싶다면 [https://jwt.io](https://jwt.io/)를 확인하십시오.
## `PyJWT` 설치 { #install-pyjwt }
Python에서 JWT 토큰을 생성하고 검증하려면 `PyJWT`를 설치해야 합니다.
[가상환경](../../virtual-environments.md)을 만들고 활성화한 다음 `pyjwt`를 설치하십시오:
```console
$ pip install pyjwt
---> 100%
```
/// info | 정보
RSA나 ECDSA 같은 전자 서명 알고리즘을 사용할 계획이라면, cryptography 라이브러리 의존성인 `pyjwt[crypto]`를 설치해야 합니다.
자세한 내용은 [PyJWT 설치 문서](https://pyjwt.readthedocs.io/en/latest/installation.html)에서 확인할 수 있습니다.
///
## 패스워드 해싱 { #password-hashing }
"해싱(Hashing)"은 어떤 내용(여기서는 패스워드)을 알아볼 수 없는 바이트 시퀀스(그냥 문자열)로 변환하는 것을 의미합니다.
정확히 같은 내용(정확히 같은 패스워드)을 넣으면 정확히 같은 알아볼 수 없는 문자열이 나옵니다.
하지만 그 알아볼 수 없는 문자열에서 다시 패스워드로 되돌릴 수는 없습니다.
### 패스워드 해싱을 사용하는 이유 { #why-use-password-hashing }
데이터베이스를 탈취당하더라도, 침입자는 사용자의 평문 패스워드 대신 해시만 얻게 됩니다.
따라서 침입자는 그 패스워드를 다른 시스템에서 사용해 보려고 시도할 수 없습니다(많은 사용자가 어디서나 같은 패스워드를 사용하므로, 이는 위험합니다).
## `pwdlib` 설치 { #install-pwdlib }
pwdlib는 패스워드 해시를 다루기 위한 훌륭한 Python 패키지입니다.
많은 안전한 해싱 알고리즘과 이를 다루기 위한 유틸리티를 지원합니다.
추천 알고리즘은 "Argon2"입니다.
[가상환경](../../virtual-environments.md)을 만들고 활성화한 다음 Argon2와 함께 pwdlib를 설치하십시오:
/// tip | 팁
`pwdlib`를 사용하면 **Django**, **Flask** 보안 플러그인 또는 다른 여러 도구로 생성한 패스워드를 읽을 수 있도록 설정할 수도 있습니다.
따라서 예를 들어, FastAPI 애플리케이션과 Django 애플리케이션이 같은 데이터베이스에서 동일한 데이터를 공유할 수 있습니다. 또는 같은 데이터베이스를 사용하면서 Django 애플리케이션을 점진적으로 마이그레이션할 수도 있습니다.
그리고 사용자는 Django 앱 또는 **FastAPI** 앱에서 동시에 로그인할 수 있습니다.
///
## 패스워드 해시 및 검증 { #hash-and-verify-the-passwords }
`pwdlib`에서 필요한 도구를 임포트합니다.
권장 설정으로 PasswordHash 인스턴스를 생성합니다. 이는 패스워드를 해싱하고 검증하는 데 사용됩니다.
/// tip | 팁
pwdlib는 bcrypt 해싱 알고리즘도 지원하지만 레거시 알고리즘은 포함하지 않습니다. 오래된 해시로 작업해야 한다면 passlib 라이브러리를 사용하는 것을 권장합니다.
예를 들어, 다른 시스템(Django 같은)에서 생성한 패스워드를 읽고 검증하되, 새 패스워드는 Argon2나 Bcrypt 같은 다른 알고리즘으로 해싱하도록 할 수 있습니다.
그리고 동시에 그 모든 것과 호환되게 만들 수 있습니다.
///
사용자로부터 받은 패스워드를 해싱하는 유틸리티 함수를 생성합니다.
그리고 받은 패스워드가 저장된 해시와 일치하는지 검증하는 또 다른 유틸리티도 생성합니다.
그리고 사용자를 인증하고 반환하는 또 다른 함수도 생성합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
`authenticate_user`가 데이터베이스에 존재하지 않는 사용자이름으로 호출되더라도, 여전히 `verify_password`를 더미 해시에 대해 실행합니다.
이렇게 하면 사용자이름이 유효하든 아니든 엔드포인트가 응답하는 데 걸리는 시간이 대략 동일해져, 기존 사용자이름을 열거하는 데 악용될 수 있는 **타이밍 공격**을 방지합니다.
/// note | 참고
새로운 (가짜) 데이터베이스 `fake_users_db`를 확인하면, 이제 해시 처리된 패스워드가 어떻게 생겼는지 볼 수 있습니다: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## JWT 토큰 처리 { #handle-jwt-tokens }
설치된 모듈을 임포트합니다.
JWT 토큰을 서명하는 데 사용할 임의의 비밀 키를 생성합니다.
안전한 임의의 비밀 키를 생성하려면 다음 명령을 사용하십시오:
그리고 출력 결과를 변수 `SECRET_KEY`에 복사합니다(예제의 값을 사용하지 마십시오).
JWT 토큰을 서명하는 데 사용될 알고리즘을 위한 변수 `ALGORITHM`을 생성하고 `"HS256"`으로 설정합니다.
토큰 만료를 위한 변수를 생성합니다.
응답을 위해 토큰 엔드포인트에서 사용될 Pydantic 모델을 정의합니다.
새 액세스 토큰을 생성하기 위한 유틸리티 함수를 생성합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## 의존성 업데이트 { #update-the-dependencies }
`get_current_user`가 이전과 동일한 토큰을 받도록 업데이트하되, 이번에는 JWT 토큰을 사용하도록 합니다.
받은 토큰을 디코딩하고 검증한 뒤 현재 사용자를 반환합니다.
토큰이 유효하지 않다면 즉시 HTTP 오류를 반환합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## `/token` *경로 처리* 업데이트 { #update-the-token-path-operation }
토큰의 만료 시간으로 `timedelta`를 생성합니다.
실제 JWT 액세스 토큰을 생성하여 반환합니다.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### JWT "주체(subject)" `sub`에 대한 기술 세부사항 { #technical-details-about-the-jwt-subject-sub }
JWT 명세에 따르면 토큰의 주체를 담는 `sub` 키가 있습니다.
선택적으로 사용할 수 있지만, 여기에 사용자 식별 정보를 넣게 되므로 여기서는 이를 사용합니다.
JWT는 사용자를 식별하고 사용자가 API에서 직접 작업을 수행할 수 있도록 허용하는 것 외에도 다른 용도로 사용될 수 있습니다.
예를 들어 "자동차"나 "블로그 게시물"을 식별할 수 있습니다.
그런 다음 해당 엔터티에 대한 권한(자동차의 경우 "drive", 블로그의 경우 "edit" 등)을 추가할 수 있습니다.
그리고 그 JWT 토큰을 사용자(또는 봇)에게 제공하면, 계정이 없어도 API가 생성한 JWT 토큰만으로 그 동작들(자동차 운전, 블로그 편집)을 수행할 수 있습니다.
이러한 아이디어를 활용하면 JWT는 훨씬 더 정교한 시나리오에도 사용될 수 있습니다.
그런 경우 여러 엔터티가 동일한 ID(예: `foo`)를 가질 수도 있습니다(사용자 `foo`, 자동차 `foo`, 블로그 게시물 `foo`).
따라서 ID 충돌을 방지하기 위해, 사용자에 대한 JWT 토큰을 생성할 때 `sub` 키의 값에 접두사를 붙일 수 있습니다. 예를 들어 `username:` 같은 것입니다. 그러면 이 예제에서 `sub` 값은 `username:johndoe`가 될 수 있습니다.
기억해야 할 중요한 점은 `sub` 키가 전체 애플리케이션에서 고유한 식별자여야 하고, 문자열이어야 한다는 것입니다.
## 확인하기 { #check-it }
서버를 실행하고 문서로 이동하십시오: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
다음과 같은 사용자 인터페이스가 보일 것입니다:
이전과 같은 방법으로 애플리케이션을 인가하십시오.
다음 인증 정보를 사용하십시오:
Username: `johndoe`
Password: `secret`
/// check | 확인
코드 어디에도 평문 패스워드 "`secret`"은 없고, 해시된 버전만 있다는 점에 유의하십시오.
///
엔드포인트 `/users/me/`를 호출하면 다음과 같은 응답을 받게 됩니다:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
개발자 도구를 열어보면 전송된 데이터에는 토큰만 포함되어 있고, 패스워드는 사용자를 인증하고 해당 액세스 토큰을 얻기 위한 첫 번째 요청에서만 전송되며 이후에는 전송되지 않는 것을 확인할 수 있습니다:
/// note | 참고
`Bearer `로 시작하는 값을 가진 `Authorization` 헤더에 주목하십시오.
///
## `scopes`의 고급 사용법 { #advanced-usage-with-scopes }
OAuth2에는 "scopes"라는 개념이 있습니다.
이를 사용해 JWT 토큰에 특정 권한 집합을 추가할 수 있습니다.
그런 다음 이 토큰을 사용자에게 직접 제공하거나 제3자에게 제공하여, 특정 제한사항 하에서 API와 상호작용하도록 할 수 있습니다.
어떻게 사용하는지, 그리고 **FastAPI**에 어떻게 통합되는지는 이후 **심화 사용자 안내서**에서 배울 수 있습니다.
## 요약 { #recap }
지금까지 살펴본 내용을 바탕으로, OAuth2와 JWT 같은 표준을 사용해 안전한 **FastAPI** 애플리케이션을 설정할 수 있습니다.
거의 모든 프레임워크에서 보안 처리는 꽤 빠르게 복잡한 주제가 됩니다.
이를 크게 단순화하는 많은 패키지들은 데이터 모델, 데이터베이스, 사용 가능한 기능들에 대해 많은 타협을 해야 합니다. 그리고 지나치게 단순화하는 일부 패키지들은 실제로 내부에 보안 결함이 있기도 합니다.
---
**FastAPI**는 어떤 데이터베이스, 데이터 모델, 도구에도 타협하지 않습니다.
프로젝트에 가장 잘 맞는 것들을 선택할 수 있는 모든 유연성을 제공합니다.
그리고 **FastAPI**는 외부 패키지를 통합하기 위해 복잡한 메커니즘을 요구하지 않기 때문에 `pwdlib`와 `PyJWT` 같은 잘 관리되고 널리 사용되는 패키지들을 바로 사용할 수 있습니다.
하지만 유연성, 견고성, 보안성을 해치지 않으면서 과정을 가능한 한 단순화할 수 있도록 도구들을 제공합니다.
또한 OAuth2 같은 안전한 표준 프로토콜을 비교적 간단한 방식으로 사용하고 구현할 수 있습니다.
더 세분화된 권한 시스템을 위해 OAuth2 "scopes"를 사용하는 방법은, 같은 표준을 따르는 방식으로 **심화 사용자 안내서**에서 더 자세히 배울 수 있습니다. 스코프를 사용하는 OAuth2는 Facebook, Google, GitHub, Microsoft, X (Twitter) 등 많은 대형 인증 제공업체들이 제3자 애플리케이션이 사용자 대신 그들의 API와 상호작용할 수 있도록 인가하는 데 사용하는 메커니즘입니다.
================================================
FILE: docs/ko/docs/tutorial/security/simple-oauth2.md
================================================
# 패스워드와 Bearer를 이용한 간단한 OAuth2 { #simple-oauth2-with-password-and-bearer }
이제 이전 장에서 빌드하고 누락된 부분을 추가하여 완전한 보안 흐름을 갖도록 하겠습니다.
## `username`와 `password` 얻기 { #get-the-username-and-password }
**FastAPI** 보안 유틸리티를 사용하여 `username` 및 `password`를 가져올 것입니다.
OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할 때 클라이언트/유저가 `username` 및 `password` 필드를 폼 데이터로 보내야 함을 지정합니다.
그리고 사양에는 필드의 이름을 그렇게 지정해야 한다고 나와 있습니다. 따라서 `user-name` 또는 `email`은 작동하지 않습니다.
하지만 걱정하지 않아도 됩니다. 프런트엔드에서 최종 사용자에게 원하는 대로 표시할 수 있습니다.
그리고 데이터베이스 모델은 원하는 다른 이름을 사용할 수 있습니다.
그러나 로그인 *경로 처리*의 경우 사양과 호환되도록 이러한 이름을 사용해야 합니다(예를 들어 통합 API 문서 시스템을 사용할 수 있어야 합니다).
사양에는 또한 `username`과 `password`가 폼 데이터로 전송되어야 한다고 명시되어 있습니다(따라서 여기에는 JSON이 없습니다).
### `scope` { #scope }
사양에는 클라이언트가 다른 폼 필드 "`scope`"를 보낼 수 있다고 나와 있습니다.
폼 필드 이름은 `scope`(단수형)이지만 실제로는 공백으로 구분된 "범위"가 있는 긴 문자열입니다.
각 "범위"는 공백이 없는 문자열입니다.
일반적으로 특정 보안 권한을 선언하는 데 사용됩니다. 다음을 봅시다:
* `users:read` 또는 `users:write`는 일반적인 예시입니다.
* `instagram_basic`은 페이스북/인스타그램에서 사용합니다.
* `https://www.googleapis.com/auth/drive`는 Google에서 사용합니다.
/// info | 정보
OAuth2에서 "범위"는 필요한 특정 권한을 선언하는 문자열입니다.
`:`과 같은 다른 문자가 있는지 또는 URL인지는 중요하지 않습니다.
이러한 세부 사항은 구현에 따라 다릅니다.
OAuth2의 경우 문자열일 뿐입니다.
///
## `username`과 `password`를 가져오는 코드 { #code-to-get-the-username-and-password }
이제 **FastAPI**에서 제공하는 유틸리티를 사용하여 이를 처리해 보겠습니다.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
먼저 `OAuth2PasswordRequestForm`을 가져와 `/token`에 대한 *경로 처리*에서 `Depends`의 의존성으로 사용합니다.
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm`은 다음을 사용하여 폼 본문을 선언하는 클래스 의존성입니다:
* `username`.
* `password`.
* `scope`는 선택적인 필드로 공백으로 구분된 문자열로 구성된 큰 문자열입니다.
* `grant_type`(선택적으로 사용).
/// tip | 팁
OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 필드를 *요구*하지만 `OAuth2PasswordRequestForm`은 이를 강요하지 않습니다.
사용해야 한다면 `OAuth2PasswordRequestForm` 대신 `OAuth2PasswordRequestFormStrict`를 사용하면 됩니다.
///
* `client_id`(선택적으로 사용) (예제에서는 필요하지 않습니다).
* `client_secret`(선택적으로 사용) (예제에서는 필요하지 않습니다).
/// info | 정보
`OAuth2PasswordRequestForm`은 `OAuth2PasswordBearer`와 같이 **FastAPI**에 대한 특수 클래스가 아닙니다.
`OAuth2PasswordBearer`는 **FastAPI**가 보안 체계임을 알도록 합니다. 그래서 OpenAPI에 그렇게 추가됩니다.
그러나 `OAuth2PasswordRequestForm`은 직접 작성하거나 `Form` 매개변수를 직접 선언할 수 있는 클래스 의존성일 뿐입니다.
그러나 일반적인 사용 사례이므로 더 쉽게 하기 위해 **FastAPI**에서 직접 제공합니다.
///
### 폼 데이터 사용하기 { #use-the-form-data }
/// tip | 팁
종속성 클래스 `OAuth2PasswordRequestForm`의 인스턴스에는 공백으로 구분된 긴 문자열이 있는 `scope` 속성이 없고 대신 전송된 각 범위에 대한 실제 문자열 목록이 있는 `scopes` 속성이 있습니다.
이 예제에서는 `scopes`를 사용하지 않지만 필요한 경우, 기능이 있습니다.
///
이제 폼 필드의 `username`을 사용하여 (가짜) 데이터베이스에서 유저 데이터를 가져옵니다.
해당 사용자가 없으면 "잘못된 사용자 이름 또는 패스워드"라는 오류가 반환됩니다.
오류의 경우 `HTTPException` 예외를 사용합니다:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### 패스워드 확인하기 { #check-the-password }
이 시점에서 데이터베이스의 사용자 데이터 형식을 확인했지만 암호를 확인하지 않았습니다.
먼저 데이터를 Pydantic `UserInDB` 모델에 넣겠습니다.
일반 텍스트 암호를 저장하면 안 되니 (가짜) 암호 해싱 시스템을 사용합니다.
두 패스워드가 일치하지 않으면 동일한 오류가 반환됩니다.
#### 패스워드 해싱 { #password-hashing }
"해싱"은 일부 콘텐츠(이 경우 패스워드)를 횡설수설하는 것처럼 보이는 일련의 바이트(문자열)로 변환하는 것을 의미합니다.
정확히 동일한 콘텐츠(정확히 동일한 패스워드)를 전달할 때마다 정확히 동일한 횡설수설이 발생합니다.
그러나 횡설수설에서 암호로 다시 변환할 수는 없습니다.
##### 패스워드 해싱을 사용해야 하는 이유 { #why-use-password-hashing }
데이터베이스가 유출된 경우 해커는 사용자의 일반 텍스트 암호가 아니라 해시만 갖게 됩니다.
따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### `**user_dict`에 대해 { #about-user-dict }
`UserInDB(**user_dict)`는 다음을 의미한다:
*`user_dict`의 키와 값을 다음과 같은 키-값 인수로 직접 전달합니다:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | 정보
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user-in-dict)를 다시 확인해보세요.
///
## 토큰 반환하기 { #return-the-token }
`token` 엔드포인트의 응답은 JSON 객체여야 합니다.
`token_type`이 있어야 합니다. 여기서는 "Bearer" 토큰을 사용하므로 토큰 유형은 "`bearer`"여야 합니다.
그리고 액세스 토큰을 포함하는 문자열과 함께 `access_token`이 있어야 합니다.
이 간단한 예제에서는 완전히 안전하지 않고, 동일한 `username`을 토큰으로 반환합니다.
/// tip | 팁
다음 장에서는 패스워드 해싱 및 JWT 토큰을 사용하여 실제 보안 구현을 볼 수 있습니다.
하지만 지금은 필요한 세부 정보에 집중하겠습니다.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | 팁
사양에 따라 이 예제와 동일하게 `access_token` 및 `token_type`이 포함된 JSON을 반환해야 합니다.
이는 코드에서 직접 수행해야 하며 해당 JSON 키를 사용해야 합니다.
사양을 준수하기 위해 스스로 올바르게 수행하기 위해 거의 유일하게 기억해야 하는 것입니다.
나머지는 **FastAPI**가 처리합니다.
///
## 의존성 업데이트하기 { #update-the-dependencies }
이제 의존성을 업데이트를 할 겁니다.
이 사용자가 활성화되어 있는 *경우에만* `current_user`를 가져올 겁니다.
따라서 `get_current_user`를 의존성으로 사용하는 추가 의존성 `get_current_active_user`를 만듭니다.
이러한 의존성 모두, 사용자가 존재하지 않거나 비활성인 경우 HTTP 오류를 반환합니다.
따라서 엔드포인트에서는 사용자가 존재하고 올바르게 인증되었으며 활성 상태인 경우에만 사용자를 얻습니다:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | 정보
여기서 반환하는 값이 `Bearer`인 추가 헤더 `WWW-Authenticate`도 사양의 일부입니다.
모든 HTTP(오류) 상태 코드 401 "UNAUTHORIZED"는 `WWW-Authenticate` 헤더도 반환해야 합니다.
베어러 토큰의 경우(지금의 경우) 해당 헤더의 값은 `Bearer`여야 합니다.
실제로 추가 헤더를 건너뛸 수 있으며 여전히 작동합니다.
그러나 여기에서는 사양을 준수하도록 제공됩니다.
또한 이를 예상하고 (현재 또는 미래에) 사용하는 도구가 있을 수 있으며, 현재 또는 미래에 자신 혹은 자신의 유저들에게 유용할 것입니다.
그것이 표준의 이점입니다 ...
///
## 확인하기 { #see-it-in-action }
대화형 문서 열기: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### 인증하기 { #authenticate }
"Authorize" 버튼을 눌러봅시다.
자격 증명을 사용합니다.
유저명: `johndoe`
패스워드: `secret`
시스템에서 인증하면 다음과 같이 표시됩니다:
### 자신의 유저 데이터 가져오기 { #get-your-own-user-data }
이제 `/users/me` 경로에 `GET` 작업을 진행합시다.
다음과 같은 사용자 데이터를 얻을 수 있습니다:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
잠금 아이콘을 클릭하고 로그아웃한 다음 동일한 작업을 다시 시도하면 다음과 같은 HTTP 401 오류가 발생합니다.
```JSON
{
"detail": "Not authenticated"
}
```
### 비활성된 유저 { #inactive-user }
이제 비활성된 사용자로 시도하고, 인증해봅시다:
유저명: `alice`
패스워드: `secret2`
그리고 `/users/me` 경로와 함께 `GET` 작업을 사용해 봅시다.
다음과 같은 "Inactive user" 오류가 발생합니다:
```JSON
{
"detail": "Inactive user"
}
```
## 요약 { #recap }
이제 API에 대한 `username` 및 `password`를 기반으로 완전한 보안 시스템을 구현할 수 있는 도구가 있습니다.
이러한 도구를 사용하여 보안 시스템을 모든 데이터베이스 및 모든 사용자 또는 데이터 모델과 호환되도록 만들 수 있습니다.
유일한 오점은 아직 실제로 "안전"하지 않다는 것입니다.
다음 장에서는 안전한 패스워드 해싱 라이브러리와 JWT 토큰을 사용하는 방법을 살펴보겠습니다.
================================================
FILE: docs/ko/docs/tutorial/server-sent-events.md
================================================
# 서버 전송 이벤트(SSE) { #server-sent-events-sse }
브라우저 클라이언트로 데이터를 스트리밍하려면 **Server-Sent Events**(SSE)를 사용할 수 있습니다.
이는 [JSON Lines 스트리밍](stream-json-lines.md)과 비슷하지만, 브라우저가 기본적으로 [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource)를 통해 지원하는 `text/event-stream` 형식을 사용합니다.
/// info | 정보
FastAPI 0.135.0에 추가되었습니다.
///
## Server-Sent Events란 { #what-are-server-sent-events }
SSE는 서버에서 클라이언트로 HTTP를 통해 데이터를 스트리밍하기 위한 표준입니다.
각 이벤트는 `data`, `event`, `id`, `retry`와 같은 "필드"를 가진 작은 텍스트 블록이며, 빈 줄로 구분됩니다.
다음과 같습니다:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE는 AI 채팅 스트리밍, 실시간 알림, 로그와 관측성, 그리고 서버가 클라이언트로 업데이트를 푸시하는 여러 경우에 흔히 사용됩니다.
/// tip | 팁
비디오나 오디오처럼 바이너리 데이터를 스트리밍하려면 고급 가이드: [데이터 스트리밍](../advanced/stream-data.md)을 확인하세요.
///
## FastAPI로 SSE 스트리밍 { #stream-sse-with-fastapi }
FastAPI에서 SSE를 스트리밍하려면, 경로 처리 함수에서 `yield`를 사용하고 `response_class=EventSourceResponse`를 설정하세요.
`EventSourceResponse`는 `fastapi.sse`에서 임포트합니다:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
각 `yield`된 항목은 JSON으로 인코딩되어 SSE 이벤트의 `data:` 필드로 전송됩니다.
반환 타입을 `AsyncIterable[Item]`으로 선언하면 FastAPI가 이를 사용해 데이터를 Pydantic으로 **검증**, **문서화**, **직렬화**합니다.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | 팁
Pydantic이 **Rust** 쪽에서 직렬화하므로, 반환 타입을 선언하지 않았을 때보다 훨씬 더 높은 **성능**을 얻을 수 있습니다.
///
### 비 async *경로 처리 함수* { #non-async-path-operation-functions }
`async`가 없는 일반 `def` 함수도 사용할 수 있으며, 동일하게 `yield`를 사용할 수 있습니다.
FastAPI가 이벤트 루프를 막지 않도록 올바르게 실행을 보장합니다.
이 경우 함수가 async가 아니므로 적절한 반환 타입은 `Iterable[Item]`입니다:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### 반환 타입 없음 { #no-return-type }
반환 타입을 생략할 수도 있습니다. FastAPI는 [`jsonable_encoder`](./encoder.md)를 사용해 데이터를 변환하고 전송합니다.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
`event`, `id`, `retry`, `comment` 같은 SSE 필드를 설정해야 한다면, 일반 데이터 대신 `ServerSentEvent` 객체를 `yield`할 수 있습니다.
`ServerSentEvent`는 `fastapi.sse`에서 임포트합니다:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
`data` 필드는 항상 JSON으로 인코딩됩니다. Pydantic 모델을 포함해 JSON으로 직렬화할 수 있는 값을 모두 전달할 수 있습니다.
## 원시 데이터 { #raw-data }
JSON 인코딩 없이 데이터를 보내야 한다면, `data` 대신 `raw_data`를 사용하세요.
미리 포맷된 텍스트, 로그 라인, 또는 `[DONE]`과 같은 특수한 "센티널" 값을 보낼 때 유용합니다.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | 참고
`data`와 `raw_data`는 상호 배타적입니다. 각 `ServerSentEvent`에는 이 둘 중 하나만 설정할 수 있습니다.
///
## `Last-Event-ID`로 재개하기 { #resuming-with-last-event-id }
브라우저가 연결이 끊긴 후 재연결할 때, 마지막으로 받은 `id`를 `Last-Event-ID` 헤더에 담아 보냅니다.
헤더 파라미터로 이를 읽어와 클라이언트가 중단한 지점부터 스트림을 재개할 수 있습니다:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## POST로 SSE 사용하기 { #sse-with-post }
SSE는 `GET`뿐만 아니라 **모든 HTTP 메서드**와 함께 동작합니다.
이는 `POST`로 SSE를 스트리밍하는 [MCP](https://modelcontextprotocol.io) 같은 프로토콜에 유용합니다:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## 기술 세부사항 { #technical-details }
FastAPI는 일부 SSE 모범 사례를 기본으로 구현합니다.
- 메시지가 없을 때는 15초마다 **"keep alive" `ping` 주석**을 보내 일부 프록시가 연결을 종료하지 않도록 합니다. [HTML 사양: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes)에서 권장합니다.
- 스트림이 **캐시되지 않도록** `Cache-Control: no-cache` 헤더를 설정합니다.
- Nginx 같은 일부 프록시에서 **버퍼링을 방지**하기 위해 특수 헤더 `X-Accel-Buffering: no`를 설정합니다.
여러분이 따로 할 일은 없습니다. 기본값으로 동작합니다. 🤓
================================================
FILE: docs/ko/docs/tutorial/sql-databases.md
================================================
# SQL (관계형) 데이터베이스 { #sql-relational-databases }
**FastAPI**에서 SQL(관계형) 데이터베이스 사용은 필수가 아닙니다. 하지만 여러분이 원하는 **어떤 데이터베이스든** 사용할 수 있습니다.
여기서는 [SQLModel](https://sqlmodel.tiangolo.com/)을 사용하는 예제를 살펴보겠습니다.
**SQLModel**은 [SQLAlchemy](https://www.sqlalchemy.org/)와 Pydantic을 기반으로 구축되었습니다. **SQL 데이터베이스**를 사용해야 하는 FastAPI 애플리케이션에 완벽히 어울리도록 **FastAPI**와 같은 제작자가 만든 도구입니다.
/// tip | 팁
다른 SQL 또는 NoSQL 데이터베이스 라이브러리를 사용할 수도 있습니다 (일부는 "ORMs"이라고도 불립니다), FastAPI는 특정 라이브러리의 사용을 강요하지 않습니다. 😎
///
SQLModel은 SQLAlchemy를 기반으로 하므로, SQLAlchemy에서 **지원하는 모든 데이터베이스**를 손쉽게 사용할 수 있습니다(이것들은 SQLModel에서도 지원됩니다). 예를 들면:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server 등.
이 예제에서는 **SQLite**를 사용합니다. SQLite는 단일 파일을 사용하고 Python에서 통합 지원하기 때문입니다. 따라서 이 예제를 그대로 복사하여 실행할 수 있습니다.
나중에 프로덕션 애플리케이션에서는 **PostgreSQL**과 같은 데이터베이스 서버를 사용하는 것이 좋습니다.
/// tip | 팁
프론트엔드와 더 많은 도구를 포함하여 **FastAPI**와 **PostgreSQL**을 포함한 공식 프로젝트 생성기가 있습니다: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
이 튜토리얼은 매우 간단하고 짧습니다. 데이터베이스 기본 개념, SQL, 또는 더 고급 기능에 대해 배우고 싶다면, [SQLModel 문서](https://sqlmodel.tiangolo.com/)를 참고하세요.
## `SQLModel` 설치하기 { #install-sqlmodel }
먼저, [가상 환경](../virtual-environments.md)을 생성하고 활성화한 다음, `sqlmodel`을 설치하세요:
```console
$ pip install sqlmodel
---> 100%
```
## 단일 모델로 애플리케이션 생성하기 { #create-the-app-with-a-single-model }
우선 단일 **SQLModel** 모델을 사용하여 애플리케이션의 가장 간단한 첫 번째 버전을 생성해보겠습니다.
이후 아래에서 **여러 모델**로 보안과 유연성을 강화하며 개선하겠습니다. 🤓
### 모델 생성하기 { #create-models }
`SQLModel`을 가져오고 데이터베이스 모델을 생성합니다:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
`Hero` 클래스는 Pydantic 모델과 매우 유사합니다 (실제로 내부적으로 *Pydantic 모델이기도 합니다*).
몇 가지 차이점이 있습니다:
* `table=True`는 SQLModel에 이 모델이 *테이블 모델*이며, SQL 데이터베이스의 **테이블**을 나타내야 한다는 것을 알려줍니다. (다른 일반적인 Pydantic 클래스처럼) 단순한 *데이터 모델*이 아닙니다.
* `Field(primary_key=True)`는 SQLModel에 `id`가 SQL 데이터베이스의 **기본 키**임을 알려줍니다 (SQL 기본 키에 대한 자세한 내용은 SQLModel 문서를 참고하세요).
**참고:** 기본 키 필드에 `int | None`을 사용하는 이유는, Python 코드에서 *`id` 없이 객체를 생성*할 수 있게 하기 위해서입니다(`id=None`). 데이터베이스가 *저장할 때 생성해 줄 것*이라고 가정합니다. SQLModel은 데이터베이스가 `id`를 제공한다는 것을 이해하고, 데이터베이스 스키마에서 *해당 열을 null이 아닌 `INTEGER`*로 정의합니다. 자세한 내용은 [기본 키에 대한 SQLModel 문서](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id)를 참고하세요.
* `Field(index=True)`는 SQLModel에 해당 열에 대해 **SQL 인덱스**를 생성하도록 지시합니다. 이를 통해 데이터베이스에서 이 열로 필터링된 데이터를 읽을 때 더 빠르게 조회할 수 있습니다.
SQLModel은 `str`으로 선언된 항목이 SQL 데이터베이스에서 `TEXT` (또는 데이터베이스에 따라 `VARCHAR`) 유형의 열로 저장된다는 것을 인식합니다.
### 엔진 생성하기 { #create-an-engine }
SQLModel의 `engine` (내부적으로는 SQLAlchemy `engine`)은 데이터베이스에 대한 **연결을 유지**하는 역할을 합니다.
코드 전체에서 동일한 데이터베이스에 연결하기 위해 **하나의 단일 `engine` 객체**를 사용합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
`check_same_thread=False`를 사용하면 FastAPI에서 여러 스레드에서 동일한 SQLite 데이터베이스를 사용할 수 있습니다. 이는 **하나의 단일 요청**이 **둘 이상의 스레드**를 사용할 수 있기 때문에 필요합니다(예: 의존성에서 사용되는 경우).
걱정하지 마세요. 코드가 구조화된 방식으로 인해, 이후에 **각 요청마다 단일 SQLModel *세션*을 사용**하도록 보장할 것입니다. 실제로 이것이 `check_same_thread`가 하려는 것입니다.
### 테이블 생성하기 { #create-the-tables }
그 다음 `SQLModel.metadata.create_all(engine)`을 사용하여 모든 *테이블 모델*의 **테이블을 생성**하는 함수를 추가합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### 세션 의존성 생성하기 { #create-a-session-dependency }
**`Session`**은 **메모리에 객체**를 저장하고 데이터에 필요한 모든 변경 사항을 추적한 후, **`engine`을 통해** 데이터베이스와 통신합니다.
`yield`를 사용해 FastAPI의 **의존성**을 생성하여 각 요청마다 새로운 `Session`을 제공합니다. 이는 요청당 하나의 세션만 사용되도록 보장합니다. 🤓
그런 다음 이 의존성을 사용하는 나머지 코드를 간소화하기 위해 `Annotated` 의존성 `SessionDep`을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### 시작 시 데이터베이스 테이블 생성하기 { #create-database-tables-on-startup }
애플리케이션 시작 시 데이터베이스 테이블을 생성합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
여기서는 애플리케이션 시작 이벤트 시 테이블을 생성합니다.
프로덕션 환경에서는 애플리케이션을 시작하기 전에 실행되는 마이그레이션 스크립트를 사용할 가능성이 높습니다. 🤓
/// tip | 팁
SQLModel은 Alembic을 감싸는 마이그레이션 유틸리티를 제공할 예정입니다. 하지만 현재 [Alembic](https://alembic.sqlalchemy.org/en/latest/)을 직접 사용할 수 있습니다.
///
### Hero 생성하기 { #create-a-hero }
각 SQLModel 모델은 Pydantic 모델이기도 하므로, Pydantic 모델을 사용할 수 있는 동일한 **타입 어노테이션**에서 사용할 수 있습니다.
예를 들어, 파라미터를 `Hero` 타입으로 선언하면 **JSON 본문**에서 값을 읽어옵니다.
마찬가지로, 함수의 **반환 타입**으로 선언하면 해당 데이터의 구조가 자동으로 생성되는 API 문서의 UI에 나타납니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
여기서는 `SessionDep` 의존성(`Session`)을 사용하여 새로운 `Hero`를 `Session` 인스턴스에 추가하고, 데이터베이스에 변경 사항을 커밋하고, `hero` 데이터의 최신 상태를 갱신한 다음 이를 반환합니다.
### Heroes 조회하기 { #read-heroes }
`select()`를 사용하여 데이터베이스에서 `Hero`를 **조회**할 수 있습니다. 결과에 페이지네이션을 적용하기 위해 `limit`와 `offset`을 포함할 수 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### 단일 Hero 조회하기 { #read-one-hero }
단일 `Hero`를 **조회**할 수도 있습니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Hero 삭제하기 { #delete-a-hero }
`Hero`를 **삭제**하는 것도 가능합니다.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### 애플리케이션 실행하기 { #run-the-app }
애플리케이션을 실행할 수 있습니다:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
그런 다음 `/docs` UI로 이동하면, **FastAPI**가 이 **모델**들을 사용해 API를 **문서화**하고, 데이터를 **직렬화**하고 **검증**하는 데에도 사용하는 것을 확인할 수 있습니다.
## 여러 모델로 애플리케이션 업데이트 { #update-the-app-with-multiple-models }
이제 이 애플리케이션을 약간 **리팩터링**하여 **보안**과 **유연성**을 개선해 보겠습니다.
이전 애플리케이션을 확인해 보면, 지금까지는 UI에서 클라이언트가 생성할 `Hero`의 `id`를 결정할 수 있게 되어 있는 것을 볼 수 있습니다. 😱
이렇게 해서는 안 됩니다. 클라이언트가 DB에 이미 할당되어 있는 `id`를 덮어쓸 수 있기 때문입니다. `id`를 결정하는 것은 **백엔드** 또는 **데이터베이스**가 해야 하며, **클라이언트**가 해서는 안 됩니다.
또한 hero에 대한 `secret_name`을 생성하지만, 지금까지는 이 값을 어디에서나 반환하고 있습니다. 이는 그다지 **비밀스럽지** 않습니다... 😅
이러한 문제는 몇 가지 **추가 모델**을 추가해 해결하겠습니다. 바로 여기서 SQLModel이 빛을 발하게 됩니다. ✨
### 여러 모델 생성하기 { #create-multiple-models }
**SQLModel**에서 `table=True`가 설정된 모델 클래스는 **테이블 모델**입니다.
그리고 `table=True`가 없는 모델 클래스는 **데이터 모델**인데, 이것들은 실제로는 (몇 가지 작은 추가 기능이 있는) Pydantic 모델일 뿐입니다. 🤓
SQLModel을 사용하면 **상속**을 통해 모든 경우에 필드를 **중복 선언하지 않아도** 됩니다.
#### `HeroBase` - 기본 클래스 { #herobase-the-base-class }
모든 모델에서 **공유되는 필드**를 가진 `HeroBase` 모델을 시작해 봅시다:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *테이블 모델* { #hero-the-table-model }
다음으로 실제 *테이블 모델*인 `Hero`를 생성합니다. 이 모델은 다른 모델에는 항상 포함되는 건 아닌 **추가 필드**를 포함합니다:
* `id`
* `secret_name`
`Hero`는 `HeroBase`를 상속하므로 `HeroBase`에 선언된 필드도 **또한** 포함합니다. 따라서 `Hero`의 모든 필드는 다음과 같습니다:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - 공개 *데이터 모델* { #heropublic-the-public-data-model }
다음으로 `HeroPublic` 모델을 생성합니다. 이 모델은 API 클라이언트에 **반환**되는 모델입니다.
`HeroPublic`은 `HeroBase`와 동일한 필드를 가지므로, `secret_name`은 포함하지 않습니다.
마침내 우리의 heroes의 정체가 보호됩니다! 🥷
또한 `id: int`를 다시 선언합니다. 이를 통해, API 클라이언트와 **계약**을 맺어 `id`가 항상 존재하며 항상 `int` 타입이라는 것을 보장합니다(`None`이 될 수 없습니다).
/// tip | 팁
반환 모델이 값이 항상 존재하고 항상 `int`(`None`이 아님)를 보장하는 것은 API 클라이언트에게 매우 유용합니다. 이를 통해 API 클라이언트는 이런 확신을 바탕으로 훨씬 더 간단한 코드를 작성할 수 있습니다.
또한 **자동으로 생성된 클라이언트**는 더 단순한 인터페이스를 제공하므로, API와 소통하는 개발자들이 API를 사용하면서 훨씬 더 좋은 경험을 할 수 있습니다. 😎
///
`HeroPublic`의 모든 필드는 `HeroBase`와 동일하며, `id`는 `int`로 선언됩니다(`None`이 아님):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - hero 생성용 *데이터 모델* { #herocreate-the-data-model-to-create-a-hero }
이제 `HeroCreate` 모델을 생성합니다. 이 모델은 클라이언트로부터 받은 데이터를 **검증**하는 역할을 합니다.
`HeroCreate`는 `HeroBase`와 동일한 필드를 가지며, `secret_name`도 포함합니다.
이제 클라이언트가 **새 hero를 생성**할 때 `secret_name`을 보내면, 데이터베이스에 저장되지만, 그 비밀 이름은 API를 통해 클라이언트에게 반환되지 않습니다.
/// tip | 팁
이 방식은 **비밀번호**를 처리하는 방법과 동일합니다. 비밀번호를 받지만, 이를 API에서 반환하지는 않습니다.
또한 비밀번호 값을 저장하기 전에 **해싱**하여 저장하고, **평문으로 저장하지 마십시오**.
///
`HeroCreate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - hero 수정용 *데이터 모델* { #heroupdate-the-data-model-to-update-a-hero }
이전 버전의 애플리케이션에서는 **hero를 수정**할 방법이 없었지만, 이제 **여러 모델**로 이를 할 수 있습니다. 🎉
`HeroUpdate` *데이터 모델*은 약간 특별한데, 새 hero를 생성할 때 필요한 **모든 동일한 필드**를 가지지만, 모든 필드가 **선택적**(기본값이 있음)입니다. 이렇게 하면 hero를 수정할 때 수정하려는 필드만 보낼 수 있습니다.
모든 **필드가 실제로 변경**되기 때문에(타입이 이제 `None`을 포함하고, 기본값도 이제 `None`이 됨), 우리는 필드를 **다시 선언**해야 합니다.
`HeroBase`를 상속할 필요는 없습니다. 모든 필드를 다시 선언하기 때문입니다. 일관성을 위해 상속을 유지하긴 했지만, 필수는 아닙니다. 이는 개인적인 취향의 문제입니다. 🤷
`HeroUpdate`의 필드는 다음과 같습니다:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### `HeroCreate`로 생성하고 `HeroPublic` 반환하기 { #create-with-herocreate-and-return-a-heropublic }
이제 **여러 모델**을 사용하므로 애플리케이션의 관련 부분을 업데이트할 수 있습니다.
요청에서 `HeroCreate` *데이터 모델*을 받아 이를 기반으로 `Hero` *테이블 모델*을 생성합니다.
이 새 *테이블 모델* `Hero`는 클라이언트에서 보낸 필드를 가지며, 데이터베이스에서 생성된 `id`도 포함합니다.
그런 다음 함수를 통해 동일한 *테이블 모델* `Hero`를 그대로 반환합니다. 하지만 `response_model`로 `HeroPublic` *데이터 모델*을 선언했기 때문에, **FastAPI**는 `HeroPublic`을 사용하여 데이터를 검증하고 직렬화합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | 팁
이제 **반환 타입 어노테이션** `-> HeroPublic` 대신 `response_model=HeroPublic`을 사용합니다. 반환하는 값이 실제로 `HeroPublic`이 *아니기* 때문입니다.
만약 `-> HeroPublic`으로 선언했다면, 에디터와 린터에서 `HeroPublic` 대신 `Hero`를 반환한다고 (당연히) 불평할 것입니다.
`response_model`에 선언함으로써 **FastAPI**가 처리하도록 하고, 타입 어노테이션과 에디터 및 다른 도구의 도움에는 영향을 미치지 않도록 합니다.
///
### `HeroPublic`으로 Heroes 조회하기 { #read-heroes-with-heropublic }
이전과 동일하게 `Hero`를 **조회**할 수 있습니다. 이번에도 `response_model=list[HeroPublic]`을 사용하여 데이터가 올바르게 검증되고 직렬화되도록 보장합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### `HeroPublic`으로 단일 Hero 조회하기 { #read-one-hero-with-heropublic }
단일 hero를 **조회**할 수도 있습니다:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### `HeroUpdate`로 Hero 수정하기 { #update-a-hero-with-heroupdate }
**hero를 수정**할 수도 있습니다. 이를 위해 HTTP `PATCH` 작업을 사용합니다.
그리고 코드에서는 클라이언트가 보낸 모든 데이터가 담긴 `dict`를 가져오는데, **클라이언트가 보낸 데이터만** 포함하고, 기본값이어서 들어가 있는 값은 제외합니다. 이를 위해 `exclude_unset=True`를 사용합니다. 이것이 주요 핵심입니다. 🪄
그런 다음, `hero_db.sqlmodel_update(hero_data)`를 사용하여 `hero_data`의 데이터로 `hero_db`를 업데이트합니다.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Hero 다시 삭제하기 { #delete-a-hero-again }
hero **삭제**는 이전과 거의 동일합니다.
이번에는 모든 것을 리팩터링하고 싶은 욕구를 만족시키지 못하겠습니다. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### 애플리케이션 다시 실행하기 { #run-the-app-again }
애플리케이션을 다시 실행할 수 있습니다:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
`/docs` API UI로 이동하면 이제 업데이트되어 있고, hero를 생성할 때 클라이언트가 `id`를 보낼 것이라고 기대하지 않는 것 등을 확인할 수 있습니다.
## 요약 { #recap }
[**SQLModel**](https://sqlmodel.tiangolo.com/)을 사용하여 SQL 데이터베이스와 상호작용하고, *데이터 모델* 및 *테이블 모델*로 코드를 간소화할 수 있습니다.
더 많은 내용을 배우려면 **SQLModel** 문서를 참고하세요. **FastAPI**와 함께 SQLModel을 사용하는 더 긴 미니 [튜토리얼](https://sqlmodel.tiangolo.com/tutorial/fastapi/)도 있습니다. 🚀
================================================
FILE: docs/ko/docs/tutorial/static-files.md
================================================
# 정적 파일 { #static-files }
`StaticFiles`를 사용하면 디렉터리에서 정적 파일을 자동으로 제공할 수 있습니다.
## `StaticFiles` 사용 { #use-staticfiles }
* `StaticFiles`를 임포트합니다.
* 특정 경로에 `StaticFiles()` 인스턴스를 "마운트"합니다.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | 기술 세부사항
`from starlette.staticfiles import StaticFiles`를 사용할 수도 있습니다.
**FastAPI**는 개발자인 여러분의 편의를 위해 `fastapi.staticfiles`로 `starlette.staticfiles`와 동일한 것을 제공합니다. 하지만 실제로는 Starlette에서 직접 가져온 것입니다.
///
### "마운팅"이란 { #what-is-mounting }
"마운팅"은 특정 경로에 완전한 "독립적인" 애플리케이션을 추가하고, 그 애플리케이션이 모든 하위 경로를 처리하도록 하는 것을 의미합니다.
마운트된 애플리케이션은 완전히 독립적이므로 `APIRouter`를 사용하는 것과는 다릅니다. 메인 애플리케이션의 OpenAPI 및 문서에는 마운트된 애플리케이션의 내용 등이 포함되지 않습니다.
자세한 내용은 [고급 사용자 가이드](../advanced/index.md)에서 확인할 수 있습니다.
## 세부사항 { #details }
첫 번째 `"/static"`은 이 "하위 애플리케이션"이 "마운트"될 하위 경로를 가리킵니다. 따라서 `"/static"`으로 시작하는 모든 경로는 이 애플리케이션이 처리합니다.
`directory="static"`은 정적 파일이 들어 있는 디렉터리의 이름을 나타냅니다.
`name="static"`은 **FastAPI**에서 내부적으로 사용할 수 있는 이름을 제공합니다.
이 모든 매개변수는 "`static`"과 다를 수 있으며, 여러분의 애플리케이션 요구 사항 및 구체적인 세부 정보에 맞게 조정하세요.
## 추가 정보 { #more-info }
자세한 내용과 옵션은 [Starlette의 정적 파일 문서](https://www.starlette.dev/staticfiles/)를 확인하세요.
================================================
FILE: docs/ko/docs/tutorial/stream-json-lines.md
================================================
# JSON Lines 스트리밍 { #stream-json-lines }
연속된 데이터를 "**스트림**"으로 보내고 싶다면 **JSON Lines**를 사용할 수 있습니다.
/// info
FastAPI 0.134.0에 추가되었습니다.
///
## 스트림이란 { #what-is-a-stream }
데이터를 "**스트리밍**"한다는 것은 애플리케이션이 전체 항목 시퀀스가 모두 준비될 때까지 기다리지 않고 클라이언트로 데이터 항목을 보내기 시작한다는 뜻입니다.
즉, 첫 번째 항목을 보내면 클라이언트는 그것을 받아 처리하기 시작하고, 그동안 애플리케이션은 다음 항목을 계속 생성할 수 있습니다.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
데이터를 계속 보내는 무한 스트림일 수도 있습니다.
## JSON Lines { #json-lines }
이런 경우에는 한 줄에 하나의 JSON 객체를 보내는 형식인 "**JSON Lines**"를 사용하는 것이 일반적입니다.
응답의 콘텐츠 타입은 `application/json` 대신 `application/jsonl`이고, 본문은 다음과 같습니다:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
JSON 배열(Python의 list에 해당)과 매우 비슷하지만, 항목들을 `[]`로 감싸고 항목 사이에 `,`를 넣는 대신, 줄마다 하나의 JSON 객체가 있고, 새 줄 문자로 구분됩니다.
/// info
핵심은 애플리케이션이 각 줄을 차례로 생성하는 동안, 클라이언트는 이전 줄을 소비할 수 있다는 점입니다.
///
/// note | 기술 세부사항
각 JSON 객체는 새 줄로 구분되므로, 내용에 실제 줄바꿈 문자를 포함할 수는 없습니다. 하지만 JSON 표준의 일부인 이스케이프된 줄바꿈(`\n`)은 포함할 수 있습니다.
보통은 신경 쓸 필요가 없습니다. 자동으로 처리되니 계속 읽어 주세요. 🤓
///
## 사용 예 { #use-cases }
이 방법을 사용해 **AI LLM** 서비스, **로그** 또는 **telemetry**에서 오는 데이터, 혹은 **JSON** 항목으로 구조화할 수 있는 다른 유형의 데이터를 스트리밍할 수 있습니다.
/// tip
비디오나 오디오처럼 바이너리 데이터를 스트리밍하려면 고급 가이드를 확인하세요: [스트림 데이터](../advanced/stream-data.md).
///
## FastAPI로 JSON Lines 스트리밍 { #stream-json-lines-with-fastapi }
FastAPI에서 JSON Lines를 스트리밍하려면, *경로 처리 함수*에서 `return`을 사용하는 대신 `yield`로 각 항목을 차례로 생성하면 됩니다.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
보내려는 각 JSON 항목의 타입이 `Item`(Pydantic 모델)이고 함수가 async라면, 반환 타입을 `AsyncIterable[Item]`로 선언할 수 있습니다:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
반환 타입을 선언하면 FastAPI가 이를 사용해 데이터를 **검증**하고, OpenAPI에 **문서화**하고, **필터링**하고, Pydantic으로 **직렬화**합니다.
/// tip
Pydantic이 **Rust** 측에서 직렬화하므로, 반환 타입을 선언하지 않았을 때보다 훨씬 높은 **성능**을 얻게 됩니다.
///
### 비동기 아님 *경로 처리 함수* { #non-async-path-operation-functions }
일반 `def` 함수(`async` 없이)도 사용할 수 있으며, 동일하게 `yield`를 사용할 수 있습니다.
FastAPI가 이벤트 루프를 막지 않도록 올바르게 실행되게 보장합니다.
이 경우 함수가 async가 아니므로, 올바른 반환 타입은 `Iterable[Item]`입니다:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### 반환 타입 생략 { #no-return-type }
반환 타입을 생략할 수도 있습니다. 그러면 FastAPI가 [`jsonable_encoder`](./encoder.md)를 사용해 데이터를 JSON으로 직렬화 가능한 형태로 변환한 뒤 JSON Lines로 전송합니다.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## 서버 전송 이벤트(SSE) { #server-sent-events-sse }
FastAPI는 Server-Sent Events(SSE)도 일급으로 지원합니다. 매우 비슷하지만 몇 가지 추가 세부사항이 있습니다. 다음 장에서 자세히 알아보세요: [Server-Sent Events (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/ko/docs/tutorial/testing.md
================================================
# 테스팅 { #testing }
[Starlette](https://www.starlette.dev/testclient/) 덕분에 **FastAPI** 애플리케이션을 테스트하는 일은 쉽고 즐거운 일이 되었습니다.
이는 [HTTPX](https://www.python-httpx.org)를 기반으로 하며, 이는 Requests를 기반으로 설계되었기 때문에 매우 친숙하고 직관적입니다.
이를 사용하면 **FastAPI**에서 [pytest](https://docs.pytest.org/)를 직접 사용할 수 있습니다.
## `TestClient` 사용하기 { #using-testclient }
/// info | 정보
`TestClient` 사용하려면, 우선 [`httpx`](https://www.python-httpx.org)를 설치해야 합니다.
[가상 환경](../virtual-environments.md)을 만들고, 활성화한 뒤 설치하세요. 예시:
```console
$ pip install httpx
```
///
`TestClient`를 임포트하세요.
**FastAPI** 애플리케이션을 전달하여 `TestClient`를 만드세요.
이름이 `test_`로 시작하는 함수를 만드세요(`pytest`의 표준적인 관례입니다).
`httpx`를 사용하는 것과 같은 방식으로 `TestClient` 객체를 사용하세요.
표준적인 파이썬 표현식으로 확인이 필요한 곳에 간단한 `assert` 문장을 작성하세요(역시 표준적인 `pytest` 관례입니다).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | 팁
테스트를 위한 함수는 `async def`가 아니라 `def`로 작성됨에 주의하세요.
그리고 클라이언트에 대한 호출도 `await`를 사용하지 않는 일반 호출입니다.
이렇게 하여 복잡한 과정 없이 `pytest`를 직접적으로 사용할 수 있습니다.
///
/// note | 기술 세부사항
`from starlette.testclient import TestClient` 역시 사용할 수 있습니다.
**FastAPI**는 개발자의 편의를 위해 `starlette.testclient`를 `fastapi.testclient`로도 제공할 뿐입니다. 하지만 이는 Starlette에서 직접 가져옵니다.
///
/// tip | 팁
FastAPI 애플리케이션에 요청을 보내는 것 외에도 테스트에서 `async` 함수를 호출하고 싶다면 (예: 비동기 데이터베이스 함수), 심화 튜토리얼의 [비동기 테스트](../advanced/async-tests.md)를 참조하세요.
///
## 테스트 분리하기 { #separating-tests }
실제 애플리케이션에서는 테스트를 별도의 파일로 나누는 경우가 많습니다.
그리고 **FastAPI** 애플리케이션도 여러 파일이나 모듈 등으로 구성될 수 있습니다.
### **FastAPI** app 파일 { #fastapi-app-file }
[더 큰 애플리케이션](bigger-applications.md)에 묘사된 파일 구조를 가지고 있는 것으로 가정해봅시다.
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
`main.py` 파일 안에 **FastAPI** app 을 만들었습니다:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### 테스트 파일 { #testing-file }
테스트를 위해 `test_main.py`라는 파일을 생성할 수 있습니다. 이 파일은 동일한 Python 패키지(즉, `__init__.py` 파일이 있는 동일한 디렉터리)에 위치할 수 있습니다.
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
파일들이 동일한 패키지에 위치해 있으므로, 상대 임포트를 사용하여 `main` 모듈(`main.py`)에서 `app` 객체를 임포트 해올 수 있습니다.
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...그리고 이전에 작성했던 것과 같은 테스트 코드를 작성할 수 있습니다.
## 테스트: 확장된 예시 { #testing-extended-example }
이제 위의 예시를 확장하고 더 많은 세부 사항을 추가하여 다양한 부분을 어떻게 테스트하는지 살펴보겠습니다.
### 확장된 **FastAPI** app 파일 { #extended-fastapi-app-file }
이전과 같은 파일 구조를 계속 사용해 보겠습니다.
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
이제 **FastAPI** 앱이 있는 `main.py` 파일에 몇 가지 다른 **경로 처리**가 추가된 경우를 생각해봅시다.
오류를 반환할 수 있는 `GET` 작업이 있습니다.
여러 다른 오류를 반환할 수 있는 `POST` 작업이 있습니다.
두 *경로 처리* 모두 `X-Token` 헤더를 요구합니다.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### 확장된 테스트 파일 { #extended-testing-file }
이제는 `test_main.py`를 확장된 테스트들로 수정할 수 있습니다:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
클라이언트가 요청에 정보를 전달해야 하는데 방법을 모르겠다면, Requests의 디자인을 기반으로 설계된 HTTPX처럼 `httpx`에서 해당 작업을 수행하는 방법을 검색(Google)하거나, `requests`에서의 방법을 검색해보세요.
그 후, 테스트에서도 동일하게 적용하면 됩니다.
예시:
* *경로* 혹은 *쿼리* 매개변수를 전달하려면, URL 자체에 추가한다.
* JSON 본문을 전달하려면, 파이썬 객체 (예를들면 `dict`)를 `json` 파라미터로 전달한다.
* JSON 대신 *폼 데이터*를 보내야한다면, `data` 파라미터를 대신 전달한다.
* *헤더*를 전달하려면, `headers` 파라미터에 `dict`를 전달한다.
* *쿠키*를 전달하려면, `cookies` 파라미터에 `dict`를 전달한다.
백엔드로 데이터를 어떻게 보내는지 정보를 더 얻으려면 (`httpx` 혹은 `TestClient`를 이용해서) [HTTPX 문서](https://www.python-httpx.org)를 확인하세요.
/// info | 정보
`TestClient`는 Pydantic 모델이 아니라 JSON으로 변환될 수 있는 데이터를 받습니다.
만약 테스트 중 Pydantic 모델을 가지고 있고 테스트 중에 애플리케이션으로 해당 데이터를 보내고 싶다면, [JSON 호환 인코더](encoder.md)에 설명되어 있는 `jsonable_encoder`를 사용할 수 있습니다.
///
## 실행하기 { #run-it }
그 후에는 `pytest`를 설치하기만 하면 됩니다.
[가상 환경](../virtual-environments.md)을 만들고, 활성화 시킨 뒤에 설치하세요. 예시:
```console
$ pip install pytest
---> 100%
```
`pytest`는 파일과 테스트를 자동으로 감지하고 실행한 다음, 결과를 보고할 것입니다.
테스트를 다음 명령어로 실행하세요.
================================================
FILE: docs/ko/docs/virtual-environments.md
================================================
# 가상 환경 { #virtual-environments }
Python 프로젝트를 작업할 때는 **가상 환경**(또는 이와 유사한 메커니즘)을 사용해 각 프로젝트마다 설치하는 패키지를 분리하는 것이 좋습니다.
/// info
이미 가상 환경에 대해 알고 있고, 어떻게 생성하고 사용하는지도 알고 있다면, 이 섹션은 건너뛰어도 괜찮습니다. 🤓
///
/// tip
**가상 환경**은 **환경 변수**와 다릅니다.
**환경 변수**는 시스템에 존재하며, 프로그램이 사용할 수 있는 변수입니다.
**가상 환경**은 몇몇 파일로 구성된 하나의 디렉터리입니다.
///
/// info
이 페이지에서는 **가상 환경**을 사용하는 방법과 작동 방식을 알려드립니다.
Python 설치까지 포함해 **모든 것을 관리해주는 도구**를 도입할 준비가 되었다면 [uv](https://github.com/astral-sh/uv)를 사용해 보세요.
///
## 프로젝트 생성 { #create-a-project }
먼저, 프로젝트를 위한 디렉터리를 하나 생성합니다.
제가 보통 하는 방법은 사용자 홈/유저 디렉터리 안에 `code`라는 디렉터리를 만드는 것입니다.
그리고 그 안에 프로젝트마다 디렉터리를 하나씩 만듭니다.
```console
// 홈 디렉터리로 이동
$ cd
// 모든 코드 프로젝트를 위한 디렉터리 생성
$ mkdir code
// 그 code 디렉터리로 이동
$ cd code
// 이 프로젝트를 위한 디렉터리 생성
$ mkdir awesome-project
// 그 프로젝트 디렉터리로 이동
$ cd awesome-project
```
## 가상 환경 생성 { #create-a-virtual-environment }
Python 프로젝트를 **처음 시작할 때**, 가상 환경을 **프로젝트 내부**에 생성하세요.
/// tip
이 작업은 **프로젝트당 한 번만** 하면 되며, 작업할 때마다 할 필요는 없습니다.
///
//// tab | `venv`
가상 환경을 만들려면 Python에 포함된 `venv` 모듈을 사용할 수 있습니다.
```console
$ python -m venv .venv
```
/// details | 명령어 의미
* `python`: `python`이라는 프로그램을 사용합니다
* `-m`: 모듈을 스크립트로 호출합니다. 다음에 어떤 모듈인지 지정합니다
* `venv`: 보통 Python에 기본으로 설치되어 있는 `venv` 모듈을 사용합니다
* `.venv`: 새 디렉터리인 `.venv`에 가상 환경을 생성합니다
///
////
//// tab | `uv`
[`uv`](https://github.com/astral-sh/uv)가 설치되어 있다면, 이를 사용해 가상 환경을 생성할 수 있습니다.
```console
$ uv venv
```
/// tip
기본적으로 `uv`는 `.venv`라는 디렉터리에 가상 환경을 생성합니다.
하지만 디렉터리 이름을 추가 인자로 전달해 이를 커스터마이즈할 수 있습니다.
///
////
해당 명령어는 `.venv`라는 디렉터리에 새로운 가상 환경을 생성합니다.
/// details | `.venv` 또는 다른 이름
가상 환경을 다른 디렉터리에 생성할 수도 있지만, 관례적으로 `.venv`라는 이름을 사용합니다.
///
## 가상 환경 활성화 { #activate-the-virtual-environment }
이후 실행하는 Python 명령어와 설치하는 패키지가 새 가상 환경을 사용하도록, 새 가상 환경을 활성화하세요.
/// tip
프로젝트 작업을 위해 **새 터미널 세션**을 시작할 때마다 **매번** 이 작업을 하세요.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
또는 Windows에서 Bash(예: [Git Bash](https://gitforwindows.org/))를 사용하는 경우:
```console
$ source .venv/Scripts/activate
```
////
/// tip
해당 환경에 **새 패키지**를 설치할 때마다, 환경을 다시 **활성화**하세요.
이렇게 하면 해당 패키지가 설치한 **터미널(CLI) 프로그램**을 사용할 때, 전역으로 설치되어 있을 수도 있는(아마 필요한 버전과는 다른 버전인) 다른 프로그램이 아니라 가상 환경에 있는 것을 사용하게 됩니다.
///
## 가상 환경 활성화 여부 확인 { #check-the-virtual-environment-is-active }
가상 환경이 활성화되어 있는지(이전 명령어가 작동했는지) 확인합니다.
/// tip
이 단계는 **선택 사항**이지만, 모든 것이 예상대로 작동하고 있는지, 그리고 의도한 가상 환경을 사용하고 있는지 **확인**하는 좋은 방법입니다.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
프로젝트 내부(이 경우 `awesome-project`)의 `.venv/bin/python`에 있는 `python` 바이너리가 표시된다면, 정상적으로 작동한 것입니다. 🎉
////
//// tab | Windows PowerShell
프로젝트 내부(이 경우 `awesome-project`)의 `.venv\Scripts\python`에 있는 `python` 바이너리가 표시된다면, 정상적으로 작동한 것입니다. 🎉
////
## `pip` 업그레이드 { #upgrade-pip }
/// tip
[`uv`](https://github.com/astral-sh/uv)를 사용한다면, `pip` 대신 `uv`로 설치하게 되므로 `pip`을 업그레이드할 필요가 없습니다. 😎
///
`pip`로 패키지를 설치한다면(Python에 기본으로 포함되어 있습니다) 최신 버전으로 **업그레이드**하는 것이 좋습니다.
패키지 설치 중 발생하는 다양한 특이한 오류는 먼저 `pip`를 업그레이드하는 것만으로 해결되는 경우가 많습니다.
/// tip
보통 이 작업은 가상 환경을 만든 직후 **한 번만** 하면 됩니다.
///
가상 환경이 활성화된 상태인지 확인한 다음(위의 명령어 사용) 아래를 실행하세요:
이 명령어는 pip가 아직 설치되어 있지 않다면 설치하며, 설치된 pip 버전이 `ensurepip`에서 제공 가능한 버전만큼 최신임을 보장합니다.
///
## `.gitignore` 추가하기 { #add-gitignore }
**Git**을 사용하고 있다면(사용하는 것이 좋습니다), `.venv`의 모든 내용을 Git에서 제외하도록 `.gitignore` 파일을 추가하세요.
/// tip
[`uv`](https://github.com/astral-sh/uv)로 가상 환경을 만들었다면, 이미 자동으로 처리되어 있으므로 이 단계는 건너뛰어도 됩니다. 😎
///
/// tip
가상 환경을 만든 직후 **한 번만** 하면 됩니다.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | 명령어 의미
* `echo "*"`: 터미널에 `*` 텍스트를 "출력"합니다(다음 부분이 이를 약간 변경합니다)
* `>`: `>` 왼쪽 명령어가 터미널에 출력한 내용을 터미널에 출력하지 않고, `>` 오른쪽에 있는 파일에 기록하라는 의미입니다
* `.gitignore`: 텍스트가 기록될 파일 이름입니다
그리고 Git에서 `*`는 "모든 것"을 의미합니다. 따라서 `.venv` 디렉터리 안의 모든 것을 무시합니다.
이 명령어는 다음 내용을 가진 `.gitignore` 파일을 생성합니다:
```gitignore
*
```
///
## 패키지 설치 { #install-packages }
환경을 활성화한 뒤, 그 안에 패키지를 설치할 수 있습니다.
/// tip
프로젝트에 필요한 패키지를 설치하거나 업그레이드할 때는 **한 번**만 하면 됩니다.
버전을 업그레이드하거나 새 패키지를 추가해야 한다면 **다시 이 작업을** 하게 됩니다.
///
### 패키지 직접 설치 { #install-packages-directly }
급하게 작업 중이고 프로젝트의 패키지 요구사항을 선언하는 파일을 사용하고 싶지 않다면, 패키지를 직접 설치할 수 있습니다.
/// tip
프로그램에 필요한 패키지와 버전을 파일(예: `requirements.txt` 또는 `pyproject.toml`)에 적어두는 것은 (매우) 좋은 생각입니다.
///
//// tab | `pip`
////
/// details | `requirements.txt`
일부 패키지가 있는 `requirements.txt`는 다음과 같이 생겼을 수 있습니다:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## 프로그램 실행 { #run-your-program }
가상 환경을 활성화한 뒤에는 프로그램을 실행할 수 있으며, 설치한 패키지가 들어있는 가상 환경 내부의 Python을 사용하게 됩니다.
```console
$ python main.py
Hello World
```
## 에디터 설정 { #configure-your-editor }
아마 에디터를 사용할 텐데, 자동 완성과 인라인 오류 표시를 받을 수 있도록 생성한 가상 환경을 사용하도록 설정하세요(대부분 자동 감지합니다).
예를 들면:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip
보통 이 설정은 가상 환경을 만들 때 **한 번만** 하면 됩니다.
///
## 가상 환경 비활성화 { #deactivate-the-virtual-environment }
프로젝트 작업을 마쳤다면 가상 환경을 **비활성화**할 수 있습니다.
```console
$ deactivate
```
이렇게 하면 `python`을 실행할 때, 해당 가상 환경과 그 안에 설치된 패키지에서 실행하려고 하지 않습니다.
## 작업할 준비 완료 { #ready-to-work }
이제 프로젝트 작업을 시작할 준비가 되었습니다.
/// tip
위의 내용이 무엇인지 더 이해하고 싶으신가요?
계속 읽어보세요. 👇🤓
///
## 가상 환경을 왜 사용하나요 { #why-virtual-environments }
FastAPI로 작업하려면 [Python](https://www.python.org/)을 설치해야 합니다.
그 다음 FastAPI와 사용하려는 다른 **패키지**를 **설치**해야 합니다.
패키지를 설치할 때는 보통 Python에 포함된 `pip` 명령어(또는 유사한 대안)를 사용합니다.
하지만 `pip`를 그대로 직접 사용하면, 패키지는 **전역 Python 환경**(전역 Python 설치)에 설치됩니다.
### 문제점 { #the-problem }
그렇다면, 전역 Python 환경에 패키지를 설치하면 어떤 문제가 있을까요?
어느 시점이 되면 **서로 다른 패키지**에 의존하는 다양한 프로그램을 작성하게 될 것입니다. 그리고 작업하는 프로젝트 중 일부는 같은 패키지의 **서로 다른 버전**에 의존할 수도 있습니다. 😱
예를 들어 `philosophers-stone`이라는 프로젝트를 만들 수 있습니다. 이 프로그램은 **`harry`라는 다른 패키지의 버전 `1`**에 의존합니다. 그래서 `harry`를 설치해야 합니다.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
그다음, 나중에 `prisoner-of-azkaban`이라는 또 다른 프로젝트를 만들고, 이 프로젝트도 `harry`에 의존하지만, 이 프로젝트는 **`harry` 버전 `3`**이 필요합니다.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
하지만 이제 문제가 생깁니다. 로컬 **가상 환경**이 아니라 전역(전역 환경)에 패키지를 설치한다면, 어떤 버전의 `harry`를 설치할지 선택해야 합니다.
`philosophers-stone`을 실행하고 싶다면, 먼저 `harry` 버전 `1`을 다음과 같이 설치해야 합니다:
```console
$ pip install "harry==1"
```
그리고 전역 Python 환경에 `harry` 버전 `1`이 설치된 상태가 됩니다.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
하지만 `prisoner-of-azkaban`을 실행하려면 `harry` 버전 `1`을 제거하고 `harry` 버전 `3`을 설치해야 합니다(또는 버전 `3`을 설치하기만 해도 버전 `1`이 자동으로 제거됩니다).
```console
$ pip install "harry==3"
```
그러면 전역 Python 환경에 `harry` 버전 `3`이 설치된 상태가 됩니다.
그리고 `philosophers-stone`을 다시 실행하려고 하면, `harry` 버전 `1`이 필요하기 때문에 **작동하지 않을** 가능성이 있습니다.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip
Python 패키지에서는 **새 버전**에서 **호환성을 깨뜨리는 변경(breaking changes)**을 **피하려고** 최선을 다하는 것이 매우 일반적이지만, 안전을 위해 더 최신 버전은 의도적으로 설치하고, 테스트를 실행해 모든 것이 올바르게 작동하는지 확인할 수 있을 때 설치하는 것이 좋습니다.
///
이제 이런 일이 여러분의 **모든 프로젝트가 의존하는** **많은** 다른 **패키지**에서도 일어난다고 상상해 보세요. 이는 관리하기가 매우 어렵습니다. 그리고 결국 일부 프로젝트는 패키지의 **호환되지 않는 버전**으로 실행하게 될 가능성이 높으며, 왜 무언가가 작동하지 않는지 알지 못하게 될 수 있습니다.
또한 운영체제(Linux, Windows, macOS 등)에 따라 Python이 이미 설치되어 있을 수도 있습니다. 그런 경우에는 시스템에 **필요한 특정 버전**의 패키지가 일부 미리 설치되어 있을 가능성이 큽니다. 전역 Python 환경에 패키지를 설치하면, 운영체제에 포함된 프로그램 일부가 **깨질** 수 있습니다.
## 패키지는 어디에 설치되나요 { #where-are-packages-installed }
Python을 설치하면 컴퓨터에 몇몇 파일이 들어 있는 디렉터리가 생성됩니다.
이 디렉터리 중 일부는 설치한 모든 패키지를 담는 역할을 합니다.
다음을 실행하면:
FastAPI 코드를 담은 압축 파일을 다운로드합니다. 보통 [PyPI](https://pypi.org/project/fastapi/)에서 받습니다.
또한 FastAPI가 의존하는 다른 패키지들의 파일도 **다운로드**합니다.
그 다음 모든 파일을 **압축 해제**하고 컴퓨터의 한 디렉터리에 넣습니다.
기본적으로, 다운로드하고 압축 해제한 파일들은 Python 설치와 함께 제공되는 디렉터리, 즉 **전역 환경**에 저장됩니다.
## 가상 환경이란 무엇인가요 { #what-are-virtual-environments }
전역 환경에 모든 패키지를 두는 문제에 대한 해결책은 작업하는 **각 프로젝트마다 가상 환경**을 사용하는 것입니다.
가상 환경은 전역 환경과 매우 유사한 하나의 **디렉터리**이며, 프로젝트의 패키지를 설치할 수 있습니다.
이렇게 하면 각 프로젝트는 자체 가상 환경(`.venv` 디렉터리)과 자체 패키지를 갖게 됩니다.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## 가상 환경을 활성화한다는 것은 무엇을 의미하나요 { #what-does-activating-a-virtual-environment-mean }
가상 환경을 활성화한다는 것은, 예를 들어 다음과 같은 명령어로:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
또는 Windows에서 Bash(예: [Git Bash](https://gitforwindows.org/))를 사용하는 경우:
```console
$ source .venv/Scripts/activate
```
////
다음 명령어들에서 사용할 수 있는 몇몇 [환경 변수](environment-variables.md)를 생성하거나 수정하는 것을 의미합니다.
그 변수 중 하나가 `PATH` 변수입니다.
/// tip
`PATH` 환경 변수에 대해 더 알아보려면 [환경 변수](environment-variables.md#path-environment-variable) 섹션을 참고하세요.
///
가상 환경을 활성화하면 가상 환경의 경로인 `.venv/bin`(Linux와 macOS) 또는 `.venv\Scripts`(Windows)를 `PATH` 환경 변수에 추가합니다.
가령 환경을 활성화하기 전에는 `PATH` 변수가 다음과 같았다고 해보겠습니다:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
이는 시스템이 다음 위치에서 프로그램을 찾는다는 뜻입니다:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
이는 시스템이 다음 위치에서 프로그램을 찾는다는 뜻입니다:
* `C:\Windows\System32`
////
가상 환경을 활성화한 뒤에는 `PATH` 변수가 다음과 같이 보일 수 있습니다:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
이는 시스템이 이제 다음 위치에서 프로그램을 가장 먼저 찾기 시작한다는 뜻입니다:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
그리고 나서 다른 디렉터리들을 탐색합니다.
따라서 터미널에 `python`을 입력하면, 시스템은 다음 위치에서 Python 프로그램을 찾고:
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
그것을 사용하게 됩니다.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
이는 시스템이 이제 다음 위치에서 프로그램을 가장 먼저 찾기 시작한다는 뜻입니다:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
그리고 나서 다른 디렉터리들을 탐색합니다.
따라서 터미널에 `python`을 입력하면, 시스템은 다음 위치에서 Python 프로그램을 찾고:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
그것을 사용하게 됩니다.
////
중요한 세부 사항은 가상 환경 경로가 `PATH` 변수의 **맨 앞**에 들어간다는 점입니다. 시스템은 다른 어떤 Python보다도 **먼저** 이를 찾게 됩니다. 이렇게 하면 `python`을 실행할 때, 다른 어떤 `python`(예: 전역 환경의 `python`)이 아니라 **가상 환경의 Python**을 사용하게 됩니다.
가상 환경을 활성화하면 다른 몇 가지도 변경되지만, 이것이 그중 가장 중요한 것 중 하나입니다.
## 가상 환경 확인하기 { #checking-a-virtual-environment }
가상 환경이 활성화되어 있는지 확인할 때는, 예를 들어 다음을 사용합니다:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
이는 사용될 `python` 프로그램이 **가상 환경 내부에 있는 것**이라는 뜻입니다.
Linux와 macOS에서는 `which`, Windows PowerShell에서는 `Get-Command`를 사용합니다.
이 명령어는 `PATH` 환경 변수에 있는 경로를 **순서대로** 확인하면서 `python`이라는 프로그램을 찾습니다. 찾는 즉시, 그 프로그램의 **경로를 보여줍니다**.
가장 중요한 부분은 `python`을 호출했을 때, 실행될 정확한 "`python`"이 무엇인지 알 수 있다는 점입니다.
따라서 올바른 가상 환경에 있는지 확인할 수 있습니다.
/// tip
가상 환경을 하나 활성화해서 Python을 사용한 다음, **다른 프로젝트로 이동**하기 쉽습니다.
그리고 두 번째 프로젝트는 다른 프로젝트의 가상 환경에서 온 **잘못된 Python**을 사용하고 있기 때문에 **작동하지 않을** 수 있습니다.
어떤 `python`이 사용되고 있는지 확인할 수 있으면 유용합니다. 🤓
///
## 가상 환경을 왜 비활성화하나요 { #why-deactivate-a-virtual-environment }
예를 들어 `philosophers-stone` 프로젝트에서 작업하면서, **그 가상 환경을 활성화**하고, 패키지를 설치하고, 그 환경으로 작업하고 있다고 해보겠습니다.
그런데 이제 **다른 프로젝트**인 `prisoner-of-azkaban`에서 작업하고 싶습니다.
해당 프로젝트로 이동합니다:
```console
$ cd ~/code/prisoner-of-azkaban
```
`philosophers-stone`의 가상 환경을 비활성화하지 않으면, 터미널에서 `python`을 실행할 때 `philosophers-stone`의 Python을 사용하려고 할 것입니다.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// sirius 임포트 오류, 설치되어 있지 않습니다 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
하지만 가상 환경을 비활성화하고 `prisoner-of-askaban`에 대한 새 가상 환경을 활성화하면, `python`을 실행할 때 `prisoner-of-azkaban`의 가상 환경에 있는 Python을 사용하게 됩니다.
```console
$ cd ~/code/prisoner-of-azkaban
// 비활성화를 위해 이전 디렉터리에 있을 필요는 없습니다. 어디서든, 다른 프로젝트로 이동한 뒤에도 할 수 있습니다 😎
$ deactivate
// prisoner-of-azkaban/.venv의 가상 환경을 활성화하세요 🚀
$ source .venv/bin/activate
// 이제 python을 실행하면, 이 가상 환경에 설치된 sirius 패키지를 찾습니다 ✨
$ python main.py
I solemnly swear 🐺
```
## 대안들 { #alternatives }
이 문서는 시작을 돕고, 내부에서 모든 것이 어떻게 작동하는지 알려주는 간단한 가이드입니다.
가상 환경, 패키지 의존성(requirements), 프로젝트를 관리하는 방법에는 많은 **대안**이 있습니다.
준비가 되었고 **프로젝트 전체**, 패키지 의존성, 가상 환경 등을 **관리**하는 도구를 사용하고 싶다면 [uv](https://github.com/astral-sh/uv)를 사용해 보시길 권합니다.
`uv`는 많은 일을 할 수 있습니다. 예를 들어:
* 여러 버전을 포함해 **Python을 설치**
* 프로젝트의 **가상 환경** 관리
* **패키지** 설치
* 프로젝트의 패키지 **의존성과 버전** 관리
* 의존성을 포함해 설치할 패키지와 버전의 **정확한** 세트를 보장하여, 개발 중인 컴퓨터와 동일하게 프로덕션에서 실행할 수 있도록 합니다. 이를 **locking**이라고 합니다
* 그 외에도 많은 기능이 있습니다
## 결론 { #conclusion }
여기까지 모두 읽고 이해했다면, 이제 많은 개발자들보다 가상 환경에 대해 **훨씬 더 많이** 알게 된 것입니다. 🤓
이 세부 사항을 알고 있으면, 나중에 복잡해 보이는 무언가를 디버깅할 때 아마도 도움이 될 것입니다. **내부에서 어떻게 작동하는지** 알고 있기 때문입니다. 😎
================================================
FILE: docs/ko/llm-prompt.md
================================================
### Target language
Translate to Korean (한국어).
Language code: ko.
### Grammar and tone
- Use polite, instructional Korean (e.g. 합니다/하세요 style).
- Keep the tone consistent with the existing Korean FastAPI docs.
- Do not translate “You” literally as “당신”. Use “여러분” where appropriate, or omit the subject if it sounds more natural in Korean.
### Headings
- Follow existing Korean heading style (short, action-oriented headings like “확인하기”).
- Do not add trailing punctuation to headings.
### Quotes
- Keep quote style consistent with the existing Korean docs.
- Never change quotes inside inline code, code blocks, URLs, or file paths.
### Ellipsis
- Keep ellipsis style consistent with existing Korean docs (often `...`).
- Never change `...` in code, URLs, or CLI examples.
### Preferred translations / glossary
Use the following preferred translations when they apply in documentation prose:
- request (HTTP): 요청
- response (HTTP): 응답
- path operation: 경로 처리
- path operation function: 경로 처리 함수
- app: 애플리케이션
- command: 명령어
- burger: 햄버거 (NOT 버거)
### `///` admonitions
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.).
2) If a title is present, prefer these canonical titles:
- `/// note | 참고`
- `/// tip | 팁`
- `/// warning | 경고`
- `/// info | 정보`
- `/// danger | 위험`
- `/// note Technical Details | 기술 세부사항`
- `/// check | 확인`
Notes:
- `details` blocks exist in Korean docs; keep `/// details` as-is and translate only the title after `|`.
- Example canonical title used: `/// details | 상세 설명`
================================================
FILE: docs/ko/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/language_names.yml
================================================
aa: Afaraf
ab: аҧсуа бызшәа
ae: avesta
af: Afrikaans
ak: Akan
am: አማርኛ
an: aragonés
ar: اللغة العربية
as: অসমীয়া
av: авар мацӀ
ay: aymar aru
az: azərbaycan dili
ba: башҡорт теле
be: беларуская мова
bg: български език
bh: भोजपुरी
bi: Bislama
bm: bamanankan
bn: বাংলা
bo: བོད་ཡིག
br: brezhoneg
bs: bosanski jezik
ca: Català
ce: нохчийн мотт
ch: Chamoru
co: corsu
cr: ᓀᐦᐃᔭᐍᐏᐣ
cs: čeština
cu: ѩзыкъ словѣньскъ
cv: чӑваш чӗлхи
cy: Cymraeg
da: dansk
de: Deutsch
dv: Dhivehi
dz: རྫོང་ཁ
ee: Eʋegbe
el: Ελληνικά
en: English
eo: Esperanto
es: español
et: eesti
eu: euskara
fa: فارسی
ff: Fulfulde
fi: suomi
fj: Vakaviti
fo: føroyskt
fr: français
fy: Frysk
ga: Gaeilge
gd: Gàidhlig
gl: galego
gu: ગુજરાતી
gv: Gaelg
ha: هَوُسَ
he: עברית
hi: हिन्दी
ho: Hiri Motu
hr: Hrvatski
ht: Kreyòl ayisyen
hu: magyar
hy: Հայերեն
hz: Otjiherero
ia: Interlingua
id: Bahasa Indonesia
ie: Interlingue
ig: Asụsụ Igbo
ii: ꆈꌠ꒿ Nuosuhxop
ik: Iñupiaq
io: Ido
is: Íslenska
it: italiano
iu: ᐃᓄᒃᑎᑐᑦ
ja: 日本語
jv: basa Jawa
ka: ქართული
kg: Kikongo
ki: Gĩkũyũ
kj: Kuanyama
kk: қазақ тілі
kl: kalaallisut
km: ខេមរភាសា
kn: ಕನ್ನಡ
ko: 한국어
kr: Kanuri
ks: कश्मीरी
ku: Kurdî
kv: коми кыв
kw: Kernewek
ky: Кыргызча
la: latine
lb: Lëtzebuergesch
lg: Luganda
li: Limburgs
ln: Lingála
lo: ພາສາ
lt: lietuvių kalba
lu: Tshiluba
lv: latviešu valoda
mg: fiteny malagasy
mh: Kajin M̧ajeļ
mi: te reo Māori
mk: македонски јазик
ml: മലയാളം
mn: Монгол хэл
mr: मराठी
ms: Bahasa Malaysia
mt: Malti
my: ဗမာစာ
na: Ekakairũ Naoero
nb: Norsk bokmål
nd: isiNdebele
ne: नेपाली
ng: Owambo
nl: Nederlands
nn: Norsk nynorsk
'no': Norsk
nr: isiNdebele
nv: Diné bizaad
ny: chiCheŵa
oc: occitan
oj: ᐊᓂᔑᓈᐯᒧᐎᓐ
om: Afaan Oromoo
or: ଓଡ଼ିଆ
os: ирон æвзаг
pa: ਪੰਜਾਬੀ
pi: पाऴि
pl: Polski
ps: پښتو
pt: português
qu: Runa Simi
rm: rumantsch grischun
rn: Ikirundi
ro: Română
ru: русский язык
rw: Ikinyarwanda
sa: संस्कृतम्
sc: sardu
sd: सिन्धी
se: Davvisámegiella
sg: yângâ tî sängö
si: සිංහල
sk: slovenčina
sl: slovenščina
sn: chiShona
so: Soomaaliga
sq: shqip
sr: српски језик
ss: SiSwati
st: Sesotho
su: Basa Sunda
sv: svenska
sw: Kiswahili
ta: தமிழ்
te: తెలుగు
tg: тоҷикӣ
th: ไทย
ti: ትግርኛ
tk: Türkmen
tl: Wikang Tagalog
tn: Setswana
to: faka Tonga
tr: Türkçe
ts: Xitsonga
tt: татар теле
tw: Twi
ty: Reo Tahiti
ug: ئۇيغۇرچە
uk: українська мова
ur: اردو
uz: Ўзбек
ve: Tshivenḓa
vi: Tiếng Việt
vo: Volapük
wa: walon
wo: Wollof
xh: isiXhosa
yi: ייִדיש
yo: Yorùbá
za: Saɯ cueŋƅ
zh: 简体中文
zh-hant: 繁體中文
zu: isiZulu
================================================
FILE: docs/missing-translation.md
================================================
/// warning
This page hasn’t been translated into your language yet. 🌍
We’re currently switching to an automated translation system 🤖, which will help keep all translations complete and up to date.
Learn more: [Contributing - Translations](https://fastapi.tiangolo.com/contributing/#translations){.internal-link target=_blank}
///
================================================
FILE: docs/pt/docs/_llm-test.md
================================================
# Arquivo de teste de LLM { #llm-test-file }
Este documento testa se o LLM, que traduz a documentação, entende o `general_prompt` em `scripts/translate.py` e o prompt específico do idioma em `docs/{language code}/llm-prompt.md`. O prompt específico do idioma é anexado ao `general_prompt`.
Os testes adicionados aqui serão vistos por todos os designers dos prompts específicos de idioma.
Use da seguinte forma:
* Tenha um prompt específico do idioma – `docs/{language code}/llm-prompt.md`.
* Faça uma tradução nova deste documento para o seu idioma de destino (veja, por exemplo, o comando `translate-page` do `translate.py`). Isso criará a tradução em `docs/{language code}/docs/_llm-test.md`.
* Verifique se está tudo certo na tradução.
* Se necessário, melhore seu prompt específico do idioma, o prompt geral ou o documento em inglês.
* Em seguida, corrija manualmente os problemas restantes na tradução, para que fique uma boa tradução.
* Retraduzir, tendo a boa tradução no lugar. O resultado ideal seria que o LLM não fizesse mais mudanças na tradução. Isso significa que o prompt geral e o seu prompt específico do idioma estão tão bons quanto possível (às vezes fará algumas mudanças aparentemente aleatórias, a razão é que [LLMs não são algoritmos determinísticos](https://doublespeak.chat/#/handbook#deterministic-output)).
Os testes:
## Trechos de código { #code-snippets }
//// tab | Teste
Este é um trecho de código: `foo`. E este é outro trecho de código: `bar`. E mais um: `baz quux`.
////
//// tab | Informação
O conteúdo dos trechos de código deve ser deixado como está.
Veja a seção `### Content of code snippets` no prompt geral em `scripts/translate.py`.
////
## Citações { #quotes }
//// tab | Teste
Ontem, meu amigo escreveu: "Se você soletrar incorretamente corretamente, você a soletrou incorretamente". Ao que respondi: "Correto, mas 'incorrectly' está incorretamente não '"incorrectly"'".
/// note | Nota
O LLM provavelmente vai traduzir isso errado. O interessante é apenas se ele mantém a tradução corrigida ao retraduzir.
///
////
//// tab | Informação
O designer do prompt pode escolher se quer converter aspas neutras em aspas tipográficas. Também é aceitável deixá-las como estão.
Veja, por exemplo, a seção `### Quotes` em `docs/de/llm-prompt.md`.
////
## Citações em trechos de código { #quotes-in-code-snippets }
//// tab | Teste
`pip install "foo[bar]"`
Exemplos de literais de string em trechos de código: `"this"`, `'that'`.
Um exemplo difícil de literais de string em trechos de código: `f"I like {'oranges' if orange else "apples"}"`
Pesado: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Informação
... No entanto, as aspas dentro de trechos de código devem permanecer como estão.
////
## Blocos de código { #code-blocks }
//// tab | Teste
Um exemplo de código Bash...
```bash
# Imprimir uma saudação ao universo
echo "Hello universe"
```
...e um exemplo de código de console...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...e outro exemplo de código de console...
```console
// Criar um diretório "Code"
$ mkdir code
// Mudar para esse diretório
$ cd code
```
...e um exemplo de código Python...
```Python
wont_work() # Isto não vai funcionar 😱
works(foo="bar") # Isto funciona 🎉
```
...e é isso.
////
//// tab | Informação
O código em blocos de código não deve ser modificado, com exceção dos comentários.
Veja a seção `### Content of code blocks` no prompt geral em `scripts/translate.py`.
////
## Abas e caixas coloridas { #tabs-and-colored-boxes }
//// tab | Teste
/// info | Informação
Algum texto
///
/// note | Nota
Algum texto
///
/// note | Detalhes Técnicos
Algum texto
///
/// check | Verifique
Algum texto
///
/// tip | Dica
Algum texto
///
/// warning | Atenção
Algum texto
///
/// danger | Cuidado
Algum texto
///
////
//// tab | Informação
Abas e blocos `Info`/`Note`/`Warning`/etc. devem ter a tradução do seu título adicionada após uma barra vertical (`|`).
Veja as seções `### Special blocks` e `### Tab blocks` no prompt geral em `scripts/translate.py`.
////
## Links da Web e internos { #web-and-internal-links }
//// tab | Teste
O texto do link deve ser traduzido, o endereço do link deve permanecer inalterado:
* [Link para o título acima](#code-snippets)
* [Link interno](index.md#installation)
* [Link externo](https://sqlmodel.tiangolo.com/)
* [Link para um estilo](https://fastapi.tiangolo.com/css/styles.css)
* [Link para um script](https://fastapi.tiangolo.com/js/logic.js)
* [Link para uma imagem](https://fastapi.tiangolo.com/img/foo.jpg)
O texto do link deve ser traduzido, o endereço do link deve apontar para a tradução:
* [Link do FastAPI](https://fastapi.tiangolo.com/pt/)
////
//// tab | Informação
Os links devem ser traduzidos, mas seus endereços devem permanecer inalterados. Uma exceção são links absolutos para páginas da documentação do FastAPI. Nesse caso, devem apontar para a tradução.
Veja a seção `### Links` no prompt geral em `scripts/translate.py`.
////
## Elementos HTML "abbr" { #html-abbr-elements }
//// tab | Teste
Aqui estão algumas coisas envolvidas em elementos HTML "abbr" (algumas são inventadas):
### O abbr fornece uma frase completa { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### O abbr fornece uma frase completa e uma explicação { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Informação
Os atributos "title" dos elementos "abbr" são traduzidos seguindo algumas instruções específicas.
As traduções podem adicionar seus próprios elementos "abbr" que o LLM não deve remover. Por exemplo, para explicar palavras em inglês.
Veja a seção `### HTML abbr elements` no prompt geral em `scripts/translate.py`.
////
## Elementos HTML "dfn" { #html-dfn-elements }
* cluster
* Deep Learning
## Títulos { #headings }
//// tab | Teste
### Desenvolver uma webapp - um tutorial { #develop-a-webapp-a-tutorial }
Olá.
### Anotações de tipo e -anotações { #type-hints-and-annotations }
Olá novamente.
### Super- e subclasses { #super-and-subclasses }
Olá novamente.
////
//// tab | Informação
A única regra rígida para títulos é que o LLM deixe a parte do hash dentro de chaves inalterada, o que garante que os links não quebrem.
Veja a seção `### Headings` no prompt geral em `scripts/translate.py`.
Para algumas instruções específicas do idioma, veja, por exemplo, a seção `### Headings` em `docs/de/llm-prompt.md`.
////
## Termos usados na documentação { #terms-used-in-the-docs }
//// tab | Teste
* você
* seu
* por exemplo
* etc.
* `foo` como um `int`
* `bar` como uma `str`
* `baz` como uma `list`
* o Tutorial - Guia do Usuário
* o Guia do Usuário Avançado
* a documentação do SQLModel
* a documentação da API
* a documentação automática
* Ciência de Dados
* Deep Learning
* Aprendizado de Máquina
* Injeção de Dependências
* autenticação HTTP Basic
* HTTP Digest
* formato ISO
* o padrão JSON Schema
* o JSON schema
* a definição do schema
* Fluxo de Senha
* Mobile
* descontinuado
* projetado
* inválido
* dinamicamente
* padrão
* padrão predefinido
* sensível a maiúsculas e minúsculas
* não sensível a maiúsculas e minúsculas
* servir a aplicação
* servir a página
* o app
* a aplicação
* a requisição
* a resposta
* a resposta de erro
* a operação de rota
* o decorador de operação de rota
* a função de operação de rota
* o corpo
* o corpo da requisição
* o corpo da resposta
* o corpo JSON
* o corpo do formulário
* o corpo do arquivo
* o corpo da função
* o parâmetro
* o parâmetro de corpo
* o parâmetro de path
* o parâmetro de query
* o parâmetro de cookie
* o parâmetro de header
* o parâmetro de formulário
* o parâmetro da função
* o evento
* o evento de inicialização
* a inicialização do servidor
* o evento de encerramento
* o evento de lifespan
* o manipulador
* o manipulador de eventos
* o manipulador de exceções
* tratar
* o modelo
* o modelo Pydantic
* o modelo de dados
* o modelo de banco de dados
* o modelo de formulário
* o objeto de modelo
* a classe
* a classe base
* a classe pai
* a subclasse
* a classe filha
* a classe irmã
* o método de classe
* o cabeçalho
* os cabeçalhos
* o cabeçalho de autorização
* o cabeçalho `Authorization`
* o cabeçalho encaminhado
* o sistema de injeção de dependências
* a dependência
* o dependable
* o dependant
* limitado por I/O
* limitado por CPU
* concorrência
* paralelismo
* multiprocessamento
* a env var
* a variável de ambiente
* o `PATH`
* a variável `PATH`
* a autenticação
* o provedor de autenticação
* a autorização
* o formulário de autorização
* o provedor de autorização
* o usuário se autentica
* o sistema autentica o usuário
* a CLI
* a interface de linha de comando
* o servidor
* o cliente
* o provedor de nuvem
* o serviço de nuvem
* o desenvolvimento
* as etapas de desenvolvimento
* o dict
* o dicionário
* a enumeração
* o enum
* o membro do enum
* o codificador
* o decodificador
* codificar
* decodificar
* a exceção
* lançar
* a expressão
* a instrução
* o frontend
* o backend
* a discussão do GitHub
* a issue do GitHub
* o desempenho
* a otimização de desempenho
* o tipo de retorno
* o valor de retorno
* a segurança
* o esquema de segurança
* a tarefa
* a tarefa em segundo plano
* a função da tarefa
* o template
* o mecanismo de template
* a anotação de tipo
* a anotação de tipo
* o worker de servidor
* o worker do Uvicorn
* o Worker do Gunicorn
* o processo worker
* a classe de worker
* a carga de trabalho
* a implantação
* implantar
* o SDK
* o kit de desenvolvimento de software
* o `APIRouter`
* o `requirements.txt`
* o Bearer Token
* a alteração com quebra de compatibilidade
* o bug
* o botão
* o chamável
* o código
* o commit
* o gerenciador de contexto
* a corrotina
* a sessão do banco de dados
* o disco
* o domínio
* o mecanismo
* o X falso
* o método HTTP GET
* o item
* a biblioteca
* o lifespan
* o bloqueio
* o middleware
* a aplicação mobile
* o módulo
* a montagem
* a rede
* a origem
* a sobrescrita
* a carga útil
* o processador
* a propriedade
* o proxy
* o pull request
* a consulta
* a RAM
* a máquina remota
* o código de status
* a string
* a tag
* o framework web
* o curinga
* retornar
* validar
////
//// tab | Informação
Esta é uma lista não completa e não normativa de termos (principalmente) técnicos vistos na documentação. Pode ser útil para o designer do prompt descobrir para quais termos o LLM precisa de uma ajudinha. Por exemplo, quando ele continua revertendo uma boa tradução para uma tradução subótima. Ou quando tem problemas para conjugar/declinar um termo no seu idioma.
Veja, por exemplo, a seção `### List of English terms and their preferred German translations` em `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/pt/docs/about/index.md
================================================
# Sobre { #about }
Sobre o FastAPI, seu design, inspiração e mais. 🤓
================================================
FILE: docs/pt/docs/advanced/additional-responses.md
================================================
# Retornos Adicionais no OpenAPI { #additional-responses-in-openapi }
/// warning | Atenção
Este é um tema bem avançado.
Se você está começando com o **FastAPI**, provavelmente você não precisa disso.
///
Você pode declarar retornos adicionais, com códigos de status adicionais, media types, descrições, etc.
Essas respostas adicionais serão incluídas no esquema do OpenAPI, e também aparecerão na documentação da API.
Porém para as respostas adicionais, você deve garantir que está retornando um `Response` como por exemplo o `JSONResponse` diretamente, junto com o código de status e o conteúdo.
## Retorno Adicional com `model` { #additional-response-with-model }
Você pode fornecer o parâmetro `responses` aos seus *decoradores de caminho*.
Este parâmetro recebe um `dict`, as chaves são os códigos de status para cada retorno, como por exemplo `200`, e os valores são um outro `dict` com a informação de cada um deles.
Cada um desses `dict` de retorno pode ter uma chave `model`, contendo um modelo do Pydantic, assim como o `response_model`.
O **FastAPI** pegará este modelo, gerará o esquema JSON dele e incluirá no local correto do OpenAPI.
Por exemplo, para declarar um outro retorno com o status code `404` e um modelo do Pydantic chamado `Message`, você pode escrever:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Nota
Lembre-se que você deve retornar o `JSONResponse` diretamente.
///
/// info | Informação
A chave `model` não é parte do OpenAPI.
O **FastAPI** pegará o modelo do Pydantic, gerará o `JSON Schema`, e adicionará no local correto.
O local correto é:
* Na chave `content`, que tem como valor um outro objeto JSON (`dict`) que contém:
* Uma chave com o media type, como por exemplo `application/json`, que contém como valor um outro objeto JSON, contendo::
* Uma chave `schema`, que contém como valor o JSON Schema do modelo, sendo este o local correto.
* O **FastAPI** adiciona aqui a referência dos esquemas JSON globais que estão localizados em outro lugar, ao invés de incluí-lo diretamente. Deste modo, outras aplicações e clientes podem utilizar estes esquemas JSON diretamente, fornecer melhores ferramentas de geração de código, etc.
///
O retorno gerado no OpenAPI para esta *operação de rota* será:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Os esquemas são referenciados em outro local dentro do esquema OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Media types adicionais para o retorno principal { #additional-media-types-for-the-main-response }
Você pode utilizar o mesmo parâmetro `responses` para adicionar diferentes media types para o mesmo retorno principal.
Por exemplo, você pode adicionar um media type adicional de `image/png`, declarando que a sua *operação de rota* pode retornar um objeto JSON (com o media type `application/json`) ou uma imagem PNG:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Nota
Note que você deve retornar a imagem utilizando um `FileResponse` diretamente.
///
/// info | Informação
A menos que você especifique um media type diferente explicitamente em seu parâmetro `responses`, o FastAPI assumirá que o retorno possui o mesmo media type contido na classe principal de retorno (padrão `application/json`).
Porém se você especificou uma classe de retorno com o valor `None` como media type, o FastAPI utilizará `application/json` para qualquer retorno adicional que possui um modelo associado.
///
## Combinando informações { #combining-information }
Você também pode combinar informações de diferentes lugares, incluindo os parâmetros `response_model`, `status_code`, e `responses`.
Você pode declarar um `response_model`, utilizando o código de status padrão `200` (ou um customizado caso você precise), e depois adicionar informações adicionais para esse mesmo retorno em `responses`, diretamente no esquema OpenAPI.
O **FastAPI** manterá as informações adicionais do `responses`, e combinará com o esquema JSON do seu modelo.
Por exemplo, você pode declarar um retorno com o código de status `404` que utiliza um modelo do Pydantic e tem uma `description` customizada.
E um retorno com o código de status `200` que utiliza o seu `response_model`, porém inclui um `example` customizado:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API:
## Combinar retornos predefinidos e personalizados { #combine-predefined-responses-and-custom-ones }
Você pode querer possuir alguns retornos predefinidos que são aplicados para diversas *operações de rota*, porém você deseja combinar com retornos personalizados que são necessários para cada *operação de rota*.
Para estes casos, você pode utilizar a técnica do Python de "desempacotamento" de um `dict` utilizando `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Aqui, o `new_dict` terá todos os pares de chave-valor do `old_dict` mais o novo par de chave-valor:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Você pode utilizar essa técnica para reutilizar alguns retornos predefinidos nas suas *operações de rota* e combiná-las com personalizações adicionais.
Por exemplo:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Mais informações sobre retornos OpenAPI { #more-information-about-openapi-responses }
Para verificar exatamente o que você pode incluir nos retornos, você pode conferir estas seções na especificação do OpenAPI:
* [Objeto de Retornos do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), inclui o `Response Object`.
* [Objeto de Retorno do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), você pode incluir qualquer coisa dele diretamente em cada retorno dentro do seu parâmetro `responses`. Incluindo `description`, `headers`, `content` (dentro dele que você declara diferentes media types e esquemas JSON), e `links`.
================================================
FILE: docs/pt/docs/advanced/additional-status-codes.md
================================================
# Códigos de status adicionais { #additional-status-codes }
Por padrão, o **FastAPI** retornará as respostas utilizando o `JSONResponse`, adicionando o conteúdo do retorno da sua *operação de rota* dentro do `JSONResponse`.
Ele usará o código de status padrão ou o que você definir na sua *operação de rota*.
## Códigos de status adicionais { #additional-status-codes_1 }
Caso você queira retornar códigos de status adicionais além do código principal, você pode fazer isso retornando um `Response` diretamente, como por exemplo um `JSONResponse`, e definir os códigos de status adicionais diretamente.
Por exemplo, vamos dizer que você deseja ter uma *operação de rota* que permita atualizar itens, e retornar um código de status HTTP 200 "OK" quando for bem sucedido.
Mas você também deseja aceitar novos itens. E quando os itens não existiam, ele os cria, e retorna o código de status HTTP 201 "Created".
Para conseguir isso, importe `JSONResponse` e retorne o seu conteúdo diretamente, definindo o `status_code` que você deseja:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Atenção
Quando você retorna um `Response` diretamente, como no exemplo acima, ele será retornado diretamente.
Ele não será serializado com um modelo, etc.
Garanta que ele tenha toda informação que você deseja, e que os valores sejam um JSON válido (caso você esteja usando `JSONResponse`).
///
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import JSONResponse`.
O **FastAPI** disponibiliza o `starlette.responses` como `fastapi.responses` apenas por conveniência para você, o programador. Porém a maioria dos retornos disponíveis vem diretamente do Starlette. O mesmo com `status`.
///
## OpenAPI e documentação da API { #openapi-and-api-docs }
Se você retorna códigos de status adicionais e retornos diretamente, eles não serão incluídos no esquema do OpenAPI (a documentação da API), porque o FastAPI não tem como saber de antemão o que será retornado.
Mas você pode documentar isso no seu código, utilizando: [Retornos Adicionais](additional-responses.md).
================================================
FILE: docs/pt/docs/advanced/advanced-dependencies.md
================================================
# Dependências avançadas { #advanced-dependencies }
## Dependências parametrizadas { #parameterized-dependencies }
Todas as dependências que vimos até agora são funções ou classes fixas.
Mas podem ocorrer casos onde você deseja ser capaz de definir parâmetros na dependência, sem ter a necessidade de declarar diversas funções ou classes.
Vamos imaginar que queremos ter uma dependência que verifica se o parâmetro de consulta `q` possui um valor fixo.
Porém nós queremos poder parametrizar o conteúdo fixo.
## Uma instância "chamável" { #a-callable-instance }
Em Python existe uma maneira de fazer com que uma instância de uma classe seja um "chamável".
Não propriamente a classe (que já é um chamável), mas a instância desta classe.
Para fazer isso, nós declaramos o método `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
Neste caso, o `__call__` é o que o **FastAPI** utilizará para verificar parâmetros adicionais e sub dependências, e isso é o que será chamado para passar o valor ao parâmetro na sua *função de operação de rota* posteriormente.
## Parametrizar a instância { #parameterize-the-instance }
E agora, nós podemos utilizar o `__init__` para declarar os parâmetros da instância que podemos utilizar para "parametrizar" a dependência:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
Neste caso, o **FastAPI** nunca tocará ou se importará com o `__init__`, nós vamos utilizar diretamente em nosso código.
## Crie uma instância { #create-an-instance }
Nós poderíamos criar uma instância desta classe com:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
E deste modo nós podemos "parametrizar" a nossa dependência, que agora possui `"bar"` dentro dele, como o atributo `checker.fixed_content`.
## Utilize a instância como dependência { #use-the-instance-as-a-dependency }
Então, nós podemos utilizar este `checker` em um `Depends(checker)`, no lugar de `Depends(FixedContentQueryChecker)`, porque a dependência é a instância, `checker`, e não a própria classe.
E quando a dependência for resolvida, o **FastAPI** chamará este `checker` como:
```Python
checker(q="somequery")
```
...e passar o que quer que isso retorne como valor da dependência em nossa *função de operação de rota* como o parâmetro `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Dica
Tudo isso parece não ser natural. E pode não estar muito claro ou aparentar ser útil ainda.
Estes exemplos são intencionalmente simples, porém mostram como tudo funciona.
Nos capítulos sobre segurança, existem funções utilitárias que são implementadas desta maneira.
Se você entendeu tudo isso, você já sabe como essas funções utilitárias para segurança funcionam por debaixo dos panos.
///
## Dependências com `yield`, `HTTPException`, `except` e Tarefas em Segundo Plano { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Atenção
Muito provavelmente você não precisa desses detalhes técnicos.
Esses detalhes são úteis principalmente se você tinha uma aplicação FastAPI anterior à versão 0.121.0 e está enfrentando problemas com dependências com `yield`.
///
Dependências com `yield` evoluíram ao longo do tempo para contemplar diferentes casos de uso e corrigir alguns problemas, aqui está um resumo do que mudou.
### Dependências com `yield` e `scope` { #dependencies-with-yield-and-scope }
Na versão 0.121.0, o FastAPI adicionou suporte a `Depends(scope="function")` para dependências com `yield`.
Usando `Depends(scope="function")`, o código de saída após o `yield` é executado logo depois que a *função de operação de rota* termina, antes de a response ser enviada de volta ao cliente.
E ao usar `Depends(scope="request")` (o padrão), o código de saída após o `yield` é executado depois que a response é enviada.
Você pode ler mais na documentação em [Dependências com `yield` - Saída antecipada e `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Dependências com `yield` e `StreamingResponse`, Detalhes Técnicos { #dependencies-with-yield-and-streamingresponse-technical-details }
Antes do FastAPI 0.118.0, se você usasse uma dependência com `yield`, o código de saída (após o `yield`) rodaria depois que a *função de operação de rota* retornasse, mas logo antes de enviar a resposta.
A intenção era evitar manter recursos por mais tempo que o necessário, esperando a resposta percorrer a rede.
Essa mudança também significava que, se você retornasse um `StreamingResponse`, o código de saída da dependência com `yield` já teria sido executado.
Por exemplo, se você tivesse uma sessão de banco de dados em uma dependência com `yield`, o `StreamingResponse` não conseguiria usar essa sessão enquanto transmite dados, porque a sessão já teria sido fechada no código de saída após o `yield`.
Esse comportamento foi revertido na versão 0.118.0, para que o código de saída após o `yield` seja executado depois que a resposta for enviada.
/// info | Informação
Como você verá abaixo, isso é muito semelhante ao comportamento antes da versão 0.106.0, mas com várias melhorias e correções de bugs para casos extremos.
///
#### Casos de uso com código de saída antecipado { #use-cases-with-early-exit-code }
Há alguns casos de uso, com condições específicas, que poderiam se beneficiar do comportamento antigo de executar o código de saída das dependências com `yield` antes de enviar a resposta.
Por exemplo, imagine que você tem código que usa uma sessão de banco de dados em uma dependência com `yield` apenas para verificar um usuário, mas a sessão de banco de dados nunca é usada novamente na *função de operação de rota*, somente na dependência, e a resposta demora a ser enviada, como um `StreamingResponse` que envia dados lentamente, mas por algum motivo não usa o banco de dados.
Nesse caso, a sessão de banco de dados seria mantida até que a resposta termine de ser enviada, mas se você não a usa, então não seria necessário mantê-la.
Veja como poderia ser:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
O código de saída, o fechamento automático da `Session` em:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...seria executado depois que a resposta terminar de enviar os dados lentos:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Mas como `generate_stream()` não usa a sessão do banco de dados, não é realmente necessário manter a sessão aberta enquanto envia a resposta.
Se você tiver esse caso específico usando SQLModel (ou SQLAlchemy), você poderia fechar explicitamente a sessão depois que não precisar mais dela:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Dessa forma a sessão liberaria a conexão com o banco de dados, para que outras requisições pudessem usá-la.
Se você tiver um caso diferente que precise sair antecipadamente de uma dependência com `yield`, por favor crie uma [Pergunta no GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions) com o seu caso específico e por que você se beneficiaria de ter o fechamento antecipado para dependências com `yield`.
Se houver casos de uso convincentes para fechamento antecipado em dependências com `yield`, considerarei adicionar uma nova forma de optar por esse fechamento antecipado.
### Dependências com `yield` e `except`, Detalhes Técnicos { #dependencies-with-yield-and-except-technical-details }
Antes do FastAPI 0.110.0, se você usasse uma dependência com `yield`, e então capturasse uma exceção com `except` nessa dependência, e você não relançasse a exceção, a exceção seria automaticamente levantada/encaminhada para quaisquer tratadores de exceção ou para o tratador de erro interno do servidor.
Isso foi alterado na versão 0.110.0 para corrigir consumo de memória não tratado decorrente de exceções encaminhadas sem um tratador (erros internos do servidor), e para torná-lo consistente com o comportamento do código Python regular.
### Tarefas em Segundo Plano e Dependências com `yield`, Detalhes Técnicos { #background-tasks-and-dependencies-with-yield-technical-details }
Antes do FastAPI 0.106.0, lançar exceções após o `yield` não era possível, o código de saída em dependências com `yield` era executado depois que a resposta era enviada, então [Tratadores de Exceções](../tutorial/handling-errors.md#install-custom-exception-handlers) já teriam sido executados.
Isso foi projetado assim principalmente para permitir o uso dos mesmos objetos "yielded" por dependências dentro de tarefas em segundo plano, porque o código de saída seria executado depois que as tarefas em segundo plano fossem concluídas.
Isso foi alterado no FastAPI 0.106.0 com a intenção de não manter recursos enquanto se espera a response percorrer a rede.
/// tip | Dica
Além disso, uma tarefa em segundo plano normalmente é um conjunto de lógica independente que deve ser tratado separadamente, com seus próprios recursos (por exemplo, sua própria conexão de banco de dados).
Assim, desta forma você provavelmente terá um código mais limpo.
///
Se você costumava depender desse comportamento, agora você deve criar os recursos para tarefas em segundo plano dentro da própria tarefa em segundo plano, e usar internamente apenas dados que não dependam dos recursos de dependências com `yield`.
Por exemplo, em vez de usar a mesma sessão de banco de dados, você criaria uma nova sessão de banco de dados dentro da tarefa em segundo plano, e obteria os objetos do banco de dados usando essa nova sessão. E então, em vez de passar o objeto do banco de dados como parâmetro para a função da tarefa em segundo plano, você passaria o ID desse objeto e então obteria o objeto novamente dentro da função da tarefa em segundo plano.
================================================
FILE: docs/pt/docs/advanced/advanced-python-types.md
================================================
# Tipos Avançados de Python { #advanced-python-types }
Aqui estão algumas ideias adicionais que podem ser úteis ao trabalhar com tipos em Python.
## Usando `Union` ou `Optional` { #using-union-or-optional }
Se, por algum motivo, seu código não puder usar `|`, por exemplo, se não for em uma anotação de tipo, mas em algo como `response_model=`, em vez de usar a barra vertical (`|`) você pode usar `Union` do `typing`.
Por exemplo, você poderia declarar que algo pode ser `str` ou `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
O `typing` também tem um atalho para declarar que algo pode ser `None`, com `Optional`.
Aqui vai uma dica do meu ponto de vista bem subjetivo:
* 🚨 Evite usar `Optional[SomeType]`
* Em vez disso ✨ use **`Union[SomeType, None]`** ✨.
Ambos são equivalentes e, por baixo, são a mesma coisa, mas eu recomendaria `Union` em vez de `Optional` porque a palavra "opcional" sugere que o valor é opcional; na verdade, significa "pode ser `None`", mesmo quando não é opcional e continua sendo obrigatório.
Acho que `Union[SomeType, None]` é mais explícito quanto ao significado.
É apenas uma questão de palavras e nomes. Mas essas palavras podem influenciar como você e sua equipe pensam sobre o código.
Como exemplo, veja esta função:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
O parâmetro `name` é definido como `Optional[str]`, mas não é opcional; não é possível chamar a função sem o parâmetro:
```Python
say_hi() # Ah, não, isso gera um erro! 😱
```
O parâmetro `name` continua obrigatório (não é opcional) porque não tem valor padrão. Ainda assim, `name` aceita `None` como valor:
```Python
say_hi(name=None) # Isso funciona, None é válido 🎉
```
A boa notícia é que, na maioria dos casos, você poderá simplesmente usar `|` para definir uniões de tipos:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Então, normalmente você não precisa se preocupar com nomes como `Optional` e `Union`. 😎
================================================
FILE: docs/pt/docs/advanced/async-tests.md
================================================
# Testes Assíncronos { #async-tests }
Você já viu como testar as suas aplicações **FastAPI** utilizando o `TestClient` que é fornecido. Até agora, você viu apenas como escrever testes síncronos, sem utilizar funções `async`.
Ser capaz de utilizar funções assíncronas em seus testes pode ser útil, por exemplo, quando você está realizando uma consulta em seu banco de dados de maneira assíncrona. Imagine que você deseja testar realizando requisições para a sua aplicação FastAPI e depois verificar que a sua aplicação inseriu corretamente as informações no banco de dados, ao utilizar uma biblioteca assíncrona para banco de dados.
Vamos ver como nós podemos fazer isso funcionar.
## pytest.mark.anyio { #pytest-mark-anyio }
Se quisermos chamar funções assíncronas em nossos testes, as nossas funções de teste precisam ser assíncronas. O AnyIO oferece um plugin bem legal para isso, que nos permite especificar que algumas das nossas funções de teste precisam ser chamadas de forma assíncrona.
## HTTPX { #httpx }
Mesmo que a sua aplicação **FastAPI** utilize funções normais com `def` no lugar de `async def`, ela ainda é uma aplicação `async` por baixo dos panos.
O `TestClient` faz algumas mágicas para invocar a aplicação FastAPI assíncrona em suas funções `def` normais, utilizando o pytest padrão. Porém a mágica não acontece mais quando nós estamos utilizando dentro de funções assíncronas. Ao executar os nossos testes de forma assíncrona, nós não podemos mais utilizar o `TestClient` dentro das nossas funções de teste.
O `TestClient` é baseado no [HTTPX](https://www.python-httpx.org), e felizmente nós podemos utilizá-lo diretamente para testar a API.
## Exemplo { #example }
Para um exemplos simples, vamos considerar uma estrutura de arquivos semelhante ao descrito em [Aplicações Maiores](../tutorial/bigger-applications.md) e [Testes](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
O arquivo `main.py` teria:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
O arquivo `test_main.py` teria os testes para para o arquivo `main.py`, ele poderia ficar assim:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Executá-lo { #run-it }
Você pode executar os seus testes normalmente via:
```console
$ pytest
---> 100%
```
## Em Detalhes { #in-detail }
O marcador `@pytest.mark.anyio` informa ao pytest que esta função de teste deve ser invocada de maneira assíncrona:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Dica
Note que a função de teste é `async def` agora, no lugar de apenas `def` como quando estávamos utilizando o `TestClient` anteriormente.
///
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Isso é equivalente a:
```Python
response = client.get('/')
```
...que nós utilizamos para fazer as nossas requisições utilizando o `TestClient`.
/// tip | Dica
Note que nós estamos utilizando async/await com o novo `AsyncClient` - a requisição é assíncrona.
///
/// warning | Atenção
Se a sua aplicação depende de eventos de lifespan, o `AsyncClient` não acionará estes eventos. Para garantir que eles são acionados, utilize o `LifespanManager` do [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Outras Chamadas de Funções Assíncronas { #other-asynchronous-function-calls }
Como a função de teste agora é assíncrona, você pode chamar (e `await`) outras funções `async` além de enviar requisições para a sua aplicação FastAPI em seus testes, exatamente como você as chamaria em qualquer outro lugar do seu código.
/// tip | Dica
Se você se deparar com um `RuntimeError: Task attached to a different loop` ao integrar funções assíncronas em seus testes (e.g. ao utilizar o [MotorClient do MongoDB](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)) Lembre-se de instanciar objetos que precisam de um loop de eventos (*event loop*) apenas em funções assíncronas, e.g. um callback `@app.on_event("startup")`.
///
================================================
FILE: docs/pt/docs/advanced/behind-a-proxy.md
================================================
# Atrás de um Proxy { #behind-a-proxy }
Em muitas situações, você usaria um **proxy** como Traefik ou Nginx na frente da sua aplicação FastAPI.
Esses proxies podem lidar com certificados HTTPS e outras coisas.
## Headers Encaminhados pelo Proxy { #proxy-forwarded-headers }
Um **proxy** na frente da sua aplicação normalmente definiria alguns headers dinamicamente antes de enviar as requisições para o seu **servidor**, para informar ao servidor que a requisição foi **encaminhada** pelo proxy, informando a URL original (pública), incluindo o domínio, que está usando HTTPS, etc.
O programa do **servidor** (por exemplo, **Uvicorn** via **CLI do FastAPI**) é capaz de interpretar esses headers e então repassar essas informações para a sua aplicação.
Mas, por segurança, como o servidor não sabe que está atrás de um proxy confiável, ele não interpretará esses headers.
/// note | Detalhes Técnicos
Os headers do proxy são:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Ativar headers encaminhados pelo proxy { #enable-proxy-forwarded-headers }
Você pode iniciar a CLI do FastAPI com a opção de linha de comando `--forwarded-allow-ips` e informar os endereços IP que devem ser confiáveis para ler esses headers encaminhados.
Se você definir como `--forwarded-allow-ips="*"`, ele confiará em todos os IPs de entrada.
Se o seu **servidor** estiver atrás de um **proxy** confiável e somente o proxy falar com ele, isso fará com que ele aceite seja qual for o IP desse **proxy**.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Redirecionamentos com HTTPS { #redirects-with-https }
Por exemplo, suponha que você defina uma *operação de rota* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Se o cliente tentar ir para `/items`, por padrão, ele seria redirecionado para `/items/`.
Mas antes de definir a opção de linha de comando `--forwarded-allow-ips`, poderia redirecionar para `http://localhost:8000/items/`.
Mas talvez sua aplicação esteja hospedada em `https://mysuperapp.com`, e o redirecionamento deveria ser para `https://mysuperapp.com/items/`.
Ao definir `--proxy-headers`, agora o FastAPI conseguirá redirecionar para o local correto. 😎
```
https://mysuperapp.com/items/
```
/// tip | Dica
Se você quiser saber mais sobre HTTPS, confira o tutorial [Sobre HTTPS](../deployment/https.md).
///
### Como funcionam os headers encaminhados pelo proxy { #how-proxy-forwarded-headers-work }
Aqui está uma representação visual de como o **proxy** adiciona headers encaminhados entre o cliente e o **servidor da aplicação**:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
O **proxy** intercepta a requisição original do cliente e adiciona os headers especiais de encaminhamento (`X-Forwarded-*`) antes de repassar a requisição para o **servidor da aplicação**.
Esses headers preservam informações sobre a requisição original que, de outra forma, seriam perdidas:
* **X-Forwarded-For**: o endereço IP original do cliente
* **X-Forwarded-Proto**: o protocolo original (`https`)
* **X-Forwarded-Host**: o host original (`mysuperapp.com`)
Quando a **CLI do FastAPI** é configurada com `--forwarded-allow-ips`, ela confia nesses headers e os utiliza, por exemplo, para gerar as URLs corretas em redirecionamentos.
## Proxy com um prefixo de path removido { #proxy-with-a-stripped-path-prefix }
Você pode ter um proxy que adiciona um prefixo de path à sua aplicação.
Nesses casos, você pode usar `root_path` para configurar sua aplicação.
O `root_path` é um mecanismo fornecido pela especificação ASGI (na qual o FastAPI é construído, através do Starlette).
O `root_path` é usado para lidar com esses casos específicos.
E também é usado internamente ao montar sub-aplicações.
Ter um proxy com um prefixo de path removido, nesse caso, significa que você poderia declarar um path em `/app` no seu código, mas então você adiciona uma camada no topo (o proxy) que colocaria sua aplicação **FastAPI** sob um path como `/api/v1`.
Nesse caso, o path original `/app` seria servido em `/api/v1/app`.
Embora todo o seu código esteja escrito assumindo que existe apenas `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
E o proxy estaria **"removendo"** o **prefixo de path** dinamicamente antes de transmitir a solicitação para o servidor da aplicação (provavelmente Uvicorn via CLI do FastAPI), mantendo sua aplicação convencida de que está sendo servida em `/app`, para que você não precise atualizar todo o seu código para incluir o prefixo `/api/v1`.
Até aqui, tudo funcionaria normalmente.
Mas então, quando você abre a interface de documentação integrada (o frontend), ela esperaria obter o OpenAPI schema em `/openapi.json`, em vez de `/api/v1/openapi.json`.
Então, o frontend (que roda no navegador) tentaria acessar `/openapi.json` e não conseguiria obter o OpenAPI schema.
Como temos um proxy com um prefixo de path de `/api/v1` para nossa aplicação, o frontend precisa buscar o OpenAPI schema em `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Dica
O IP `0.0.0.0` é comumente usado para significar que o programa escuta em todos os IPs disponíveis naquela máquina/servidor.
///
A interface de documentação também precisaria do OpenAPI schema para declarar que este `server` da API está localizado em `/api/v1` (atrás do proxy). Por exemplo:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Mais coisas aqui
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Mais coisas aqui
}
}
```
Neste exemplo, o "Proxy" poderia ser algo como **Traefik**. E o servidor seria algo como a CLI do FastAPI com **Uvicorn**, executando sua aplicação FastAPI.
### Fornecendo o `root_path` { #providing-the-root-path }
Para conseguir isso, você pode usar a opção de linha de comando `--root-path` assim:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Se você usar Hypercorn, ele também tem a opção `--root-path`.
/// note | Detalhes Técnicos
A especificação ASGI define um `root_path` para esse caso de uso.
E a opção de linha de comando `--root-path` fornece esse `root_path`.
///
### Verificando o `root_path` atual { #checking-the-current-root-path }
Você pode obter o `root_path` atual usado pela sua aplicação para cada solicitação, ele faz parte do dicionário `scope` (que faz parte da especificação ASGI).
Aqui estamos incluindo-o na mensagem apenas para fins de demonstração.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Então, se você iniciar o Uvicorn com:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
A resposta seria algo como:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Configurando o `root_path` na aplicação FastAPI { #setting-the-root-path-in-the-fastapi-app }
Alternativamente, se você não tiver uma maneira de fornecer uma opção de linha de comando como `--root-path` ou equivalente, você pode definir o parâmetro `root_path` ao criar sua aplicação FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Passar o `root_path` para `FastAPI` seria o equivalente a passar a opção de linha de comando `--root-path` para Uvicorn ou Hypercorn.
### Sobre `root_path` { #about-root-path }
Tenha em mente que o servidor (Uvicorn) não usará esse `root_path` para nada além de passá-lo para a aplicação.
Mas se você acessar com seu navegador [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) você verá a resposta normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Portanto, ele não esperará ser acessado em `http://127.0.0.1:8000/api/v1/app`.
O Uvicorn esperará que o proxy acesse o Uvicorn em `http://127.0.0.1:8000/app`, e então seria responsabilidade do proxy adicionar o prefixo extra `/api/v1` no topo.
## Sobre proxies com um prefixo de path removido { #about-proxies-with-a-stripped-path-prefix }
Tenha em mente que um proxy com prefixo de path removido é apenas uma das maneiras de configurá-lo.
Provavelmente, em muitos casos, o padrão será que o proxy não tenha um prefixo de path removido.
Em um caso como esse (sem um prefixo de path removido), o proxy escutaria em algo como `https://myawesomeapp.com`, e então, se o navegador acessar `https://myawesomeapp.com/api/v1/app` e seu servidor (por exemplo, Uvicorn) escutar em `http://127.0.0.1:8000`, o proxy (sem um prefixo de path removido) acessaria o Uvicorn no mesmo path: `http://127.0.0.1:8000/api/v1/app`.
## Testando localmente com Traefik { #testing-locally-with-traefik }
Você pode facilmente executar o experimento localmente com um prefixo de path removido usando [Traefik](https://docs.traefik.io/).
[Faça o download do Traefik](https://github.com/containous/traefik/releases), ele é um único binário, você pode extrair o arquivo compactado e executá-lo diretamente do terminal.
Então, crie um arquivo `traefik.toml` com:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Isso diz ao Traefik para escutar na porta 9999 e usar outro arquivo `routes.toml`.
/// tip | Dica
Estamos usando a porta 9999 em vez da porta padrão HTTP 80 para que você não precise executá-lo com privilégios de administrador (`sudo`).
///
Agora crie esse outro arquivo `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Esse arquivo configura o Traefik para usar o prefixo de path `/api/v1`.
E então o Traefik redirecionará suas solicitações para seu Uvicorn rodando em `http://127.0.0.1:8000`.
Agora inicie o Traefik:
E agora inicie sua aplicação, usando a opção `--root-path`:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Verifique as respostas { #check-the-responses }
Agora, se você for ao URL com a porta para o Uvicorn: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), você verá a resposta normal:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Dica
Perceba que, mesmo acessando em `http://127.0.0.1:8000/app`, ele mostra o `root_path` de `/api/v1`, retirado da opção `--root-path`.
///
E agora abra o URL com a porta para o Traefik, incluindo o prefixo de path: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Obtemos a mesma resposta:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
mas desta vez no URL com o prefixo de path fornecido pelo proxy: `/api/v1`.
Claro, a ideia aqui é que todos acessariam a aplicação através do proxy, então a versão com o prefixo de path `/api/v1` é a "correta".
E a versão sem o prefixo de path (`http://127.0.0.1:8000/app`), fornecida diretamente pelo Uvicorn, seria exclusivamente para o _proxy_ (Traefik) acessá-la.
Isso demonstra como o Proxy (Traefik) usa o prefixo de path e como o servidor (Uvicorn) usa o `root_path` da opção `--root-path`.
### Verifique a interface de documentação { #check-the-docs-ui }
Mas aqui está a parte divertida. ✨
A maneira "oficial" de acessar a aplicação seria através do proxy com o prefixo de path que definimos. Então, como esperaríamos, se você tentar a interface de documentação servida diretamente pelo Uvicorn, sem o prefixo de path no URL, ela não funcionará, porque espera ser acessada através do proxy.
Você pode verificar em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
Mas se acessarmos a interface de documentação no URL "oficial" usando o proxy com a porta `9999`, em `/api/v1/docs`, ela funciona corretamente! 🎉
Você pode verificar em [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Exatamente como queríamos. ✔️
Isso porque o FastAPI usa esse `root_path` para criar o `server` padrão no OpenAPI com o URL fornecido pelo `root_path`.
## Servidores adicionais { #additional-servers }
/// warning | Atenção
Este é um caso de uso mais avançado. Sinta-se à vontade para pular.
///
Por padrão, o **FastAPI** criará um `server` no OpenAPI schema com o URL para o `root_path`.
Mas você também pode fornecer outros `servers` alternativos, por exemplo, se quiser que a mesma interface de documentação interaja com ambientes de staging e produção.
Se você passar uma lista personalizada de `servers` e houver um `root_path` (porque sua API está atrás de um proxy), o **FastAPI** inserirá um "server" com esse `root_path` no início da lista.
Por exemplo:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Gerará um OpenAPI schema como:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Mais coisas aqui
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Mais coisas aqui
}
}
```
/// tip | Dica
Perceba o servidor gerado automaticamente com um valor `url` de `/api/v1`, retirado do `root_path`.
///
Na interface de documentação em [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) parecerá:
/// tip | Dica
A interface de documentação interagirá com o servidor que você selecionar.
///
/// note | Detalhes Técnicos
A propriedade `servers` na especificação OpenAPI é opcional.
Se você não especificar o parâmetro `servers` e `root_path` for igual a `/`, a propriedade `servers` no OpenAPI gerado será totalmente omitida por padrão, o que equivale a um único servidor com valor de `url` igual a `/`.
///
### Desabilitar servidor automático de `root_path` { #disable-automatic-server-from-root-path }
Se você não quiser que o **FastAPI** inclua um servidor automático usando o `root_path`, você pode usar o parâmetro `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
e então ele não será incluído no OpenAPI schema.
## Montando uma sub-aplicação { #mounting-a-sub-application }
Se você precisar montar uma sub-aplicação (como descrito em [Sub-aplicações - Montagens](sub-applications.md)) enquanto também usa um proxy com `root_path`, você pode fazer isso normalmente, como esperaria.
O FastAPI usará internamente o `root_path` de forma inteligente, então tudo funcionará. ✨
================================================
FILE: docs/pt/docs/advanced/custom-response.md
================================================
# Resposta Personalizada - HTML, Stream, File e outras { #custom-response-html-stream-file-others }
Por padrão, o **FastAPI** retornará respostas JSON.
Você pode sobrescrever isso retornando uma `Response` diretamente, como visto em [Retornando uma Resposta Diretamente](response-directly.md).
Mas se você retornar uma `Response` diretamente (ou qualquer subclasse, como `JSONResponse`), os dados não serão convertidos automaticamente (mesmo que você declare um `response_model`), e a documentação não será gerada automaticamente (por exemplo, incluindo o "media type" específico, no cabeçalho HTTP `Content-Type` como parte do OpenAPI gerado).
Mas você também pode declarar a `Response` que deseja utilizar (e.g. qualquer subclasse de `Response`), no *decorador de operação de rota* usando o parâmetro `response_class`.
O conteúdo que você retorna da sua *função de operação de rota* será colocado dentro dessa `Response`.
/// note | Nota
Se você utilizar uma classe de resposta sem media type, o FastAPI esperará que sua resposta não tenha conteúdo, então ele não irá documentar o formato da resposta na documentação OpenAPI gerada.
///
## Respostas JSON { #json-responses }
Por padrão, o FastAPI retorna respostas JSON.
Se você declarar um [Modelo de Resposta](../tutorial/response-model.md), o FastAPI irá usá-lo para serializar os dados para JSON, usando Pydantic.
Se você não declarar um modelo de resposta, o FastAPI usará o `jsonable_encoder` explicado em [Codificador Compatível com JSON](../tutorial/encoder.md) e o colocará em uma `JSONResponse`.
Se você declarar uma `response_class` com um media type JSON (`application/json`), como no caso de `JSONResponse`, os dados que você retorna serão automaticamente convertidos (e filtrados) com qualquer `response_model` do Pydantic que você declarou no *decorador de operação de rota*. Mas os dados não serão serializados para bytes JSON com Pydantic; em vez disso, serão convertidos com o `jsonable_encoder` e então passados para a classe `JSONResponse`, que fará a serialização para bytes usando a biblioteca padrão de JSON do Python.
### Performance com JSON { #json-performance }
Resumindo, se você quer o máximo de performance, use um [Modelo de Resposta](../tutorial/response-model.md) e não declare uma `response_class` no *decorador de operação de rota*.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## Resposta HTML { #html-response }
Para retornar uma resposta com HTML diretamente do **FastAPI**, utilize `HTMLResponse`.
* Importe `HTMLResponse`.
* Passe `HTMLResponse` como o parâmetro de `response_class` do seu *decorador de operação de rota*.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Informação
O parâmetro `response_class` também será usado para definir o "media type" da resposta.
Neste caso, o cabeçalho HTTP `Content-Type` será definido como `text/html`.
E será documentado como tal no OpenAPI.
///
### Retornando uma `Response` { #return-a-response }
Como visto em [Retornando uma Resposta Diretamente](response-directly.md), você também pode sobrescrever a resposta diretamente na sua *operação de rota*, ao retornar ela.
O mesmo exemplo de antes, retornando uma `HTMLResponse`, poderia parecer com:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Atenção
Uma `Response` retornada diretamente em sua *função de operação de rota* não será documentada no OpenAPI (por exemplo, o `Content-Type` não será documentado) e não será visível na documentação interativa automática.
///
/// info | Informação
Obviamente, o cabeçalho `Content-Type`, o código de status, etc, virão do objeto `Response` que você retornou.
///
### Documentar no OpenAPI e sobrescrever `Response` { #document-in-openapi-and-override-response }
Se você deseja sobrescrever a resposta dentro de uma função, mas ao mesmo tempo documentar o "media type" no OpenAPI, você pode utilizar o parâmetro `response_class` E retornar um objeto `Response`.
A `response_class` será usada apenas para documentar o OpenAPI da *operação de rota*, mas sua `Response` será usada como foi definida.
#### Retornando uma `HTMLResponse` diretamente { #return-an-htmlresponse-directly }
Por exemplo, poderia ser algo como:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
Neste exemplo, a função `generate_html_response()` já cria e retorna uma `Response` em vez de retornar o HTML em uma `str`.
Ao retornar o resultado chamando `generate_html_response()`, você já está retornando uma `Response` que irá sobrescrever o comportamento padrão do **FastAPI**.
Mas como você passou `HTMLResponse` em `response_class` também, o **FastAPI** saberá como documentar isso no OpenAPI e na documentação interativa como um HTML com `text/html`:
## Respostas disponíveis { #available-responses }
Aqui estão algumas das respostas disponíveis.
Lembre-se que você pode utilizar `Response` para retornar qualquer outra coisa, ou até mesmo criar uma subclasse personalizada.
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import HTMLResponse`.
O **FastAPI** provê a mesma `starlette.responses` como `fastapi.responses` apenas como uma facilidade para você, desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette.
///
### `Response` { #response }
A classe principal de respostas, todas as outras respostas herdam dela.
Você pode retorná-la diretamente.
Ela aceita os seguintes parâmetros:
* `content` - Uma `str` ou `bytes`.
* `status_code` - Um código de status HTTP do tipo `int`.
* `headers` - Um `dict` de strings.
* `media_type` - Uma `str` informando o media type. E.g. `"text/html"`.
O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você leu acima.
### `PlainTextResponse` { #plaintextresponse }
Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Pega alguns dados e retorna uma resposta com codificação `application/json`.
É a resposta padrão utilizada no **FastAPI**, como você leu acima.
/// note | Detalhes Técnicos
Mas se você declarar um modelo de resposta ou tipo de retorno, isso será usado diretamente para serializar os dados para JSON, e uma resposta com o media type correto para JSON será retornada diretamente, sem usar a classe `JSONResponse`.
Esta é a forma ideal para obter a melhor performance.
///
### `RedirectResponse` { #redirectresponse }
Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecionamento Temporário) por padrão.
Você pode retornar uma `RedirectResponse` diretamente:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Ou você pode utilizá-la no parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*.
Neste caso, o `status_code` utilizado será o padrão de `RedirectResponse`, que é `307`.
---
Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Recebe um gerador assíncrono ou um gerador/iterador comum (uma função com `yield`) e transmite (stream) o corpo da resposta.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Detalhes Técnicos
Uma tarefa `async` só pode ser cancelada quando alcança um `await`. Se não houver `await`, o gerador (função com `yield`) não pode ser cancelado adequadamente e pode continuar executando mesmo após o cancelamento ser solicitado.
Como este pequeno exemplo não precisa de nenhuma instrução `await`, adicionamos um `await anyio.sleep(0)` para dar ao event loop a chance de lidar com o cancelamento.
Isso seria ainda mais importante com streams grandes ou infinitos.
///
/// tip | Dica
Em vez de retornar uma `StreamingResponse` diretamente, você deveria provavelmente seguir o estilo em [Transmitir Dados](./stream-data.md), é muito mais conveniente e lida com cancelamento nos bastidores para você.
Se você estiver transmitindo JSON Lines, siga o tutorial [Transmitir JSON Lines](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Envia um arquivo de forma assíncrona e contínua (stream).
Recebe um conjunto de argumentos do construtor diferente dos outros tipos de resposta:
* `path` - O caminho do arquivo que será transmitido.
* `headers` - Quaisquer cabeçalhos personalizados a serem incluídos, como um dicionário.
* `media_type` - Uma string com o media type. Se não for definida, o nome do arquivo ou path será usado para inferir um media type.
* `filename` - Se definido, será incluído no cabeçalho `Content-Disposition`.
Respostas de arquivos incluirão os cabeçalhos apropriados `Content-Length`, `Last-Modified` e `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Você também pode usar o parâmetro `response_class`:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
Nesse caso, você pode retornar o path do arquivo diretamente da sua *função de operação de rota*.
## Classe de resposta personalizada { #custom-response-class }
Você pode criar sua própria classe de resposta personalizada, herdando de `Response` e usando-a.
Por exemplo, vamos supor que você queira usar [`orjson`](https://github.com/ijl/orjson) com algumas configurações.
Vamos supor que você queira retornar um JSON indentado e formatado, então você quer utilizar a opção `orjson.OPT_INDENT_2` do orjson.
Você poderia criar uma `CustomORJSONResponse`. A principal coisa que você tem que fazer é criar um método `Response.render(content)` que retorne o conteúdo como `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Agora em vez de retornar:
```json
{"message": "Hello World"}
```
...essa resposta retornará:
```json
{
"message": "Hello World"
}
```
Obviamente, você provavelmente vai encontrar maneiras muito melhores de se aproveitar disso do que a formatação de JSON. 😉
### `orjson` ou Modelo de Resposta { #orjson-or-response-model }
Se o que você procura é performance, provavelmente é melhor usar um [Modelo de Resposta](../tutorial/response-model.md) do que uma resposta com `orjson`.
Com um modelo de resposta, o FastAPI usará o Pydantic para serializar os dados para JSON, sem passos intermediários, como convertê-los com `jsonable_encoder`, o que aconteceria em qualquer outro caso.
E, por baixo dos panos, o Pydantic usa os mesmos mecanismos em Rust que o `orjson` para serializar para JSON, então você já terá a melhor performance com um modelo de resposta.
## Classe de resposta padrão { #default-response-class }
Quando você criar uma instância da classe **FastAPI** ou um `APIRouter` você pode especificar qual classe de resposta utilizar por padrão.
O parâmetro que define isso é o `default_response_class`.
No exemplo abaixo, o **FastAPI** utilizará `HTMLResponse` por padrão, em todas as *operações de rota*, em vez de JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Dica
Você ainda pode substituir `response_class` em *operações de rota* como antes.
///
## Documentação adicional { #additional-documentation }
Você também pode declarar o media type e muitos outros detalhes no OpenAPI utilizando `responses`: [Respostas Adicionais no OpenAPI](additional-responses.md).
================================================
FILE: docs/pt/docs/advanced/dataclasses.md
================================================
# Usando Dataclasses { #using-dataclasses }
FastAPI é construído em cima do **Pydantic**, e eu tenho mostrado como usar modelos Pydantic para declarar requisições e respostas.
Mas o FastAPI também suporta o uso de [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) da mesma forma:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Isso ainda é suportado graças ao **Pydantic**, pois ele tem [suporte interno para `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Então, mesmo com o código acima que não usa Pydantic explicitamente, o FastAPI está usando Pydantic para converter essas dataclasses padrão para a versão do Pydantic.
E claro, ele suporta o mesmo:
* validação de dados
* serialização de dados
* documentação de dados, etc.
Isso funciona da mesma forma que com os modelos Pydantic. E na verdade é alcançado da mesma maneira por baixo dos panos, usando Pydantic.
/// info | Informação
Lembre-se de que dataclasses não podem fazer tudo o que os modelos Pydantic podem fazer.
Então, você ainda pode precisar usar modelos Pydantic.
Mas se você tem um monte de dataclasses por aí, este é um truque legal para usá-las para alimentar uma API web usando FastAPI. 🤓
///
## Dataclasses em `response_model` { #dataclasses-in-response-model }
Você também pode usar `dataclasses` no parâmetro `response_model`:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
A dataclass será automaticamente convertida para uma dataclass Pydantic.
Dessa forma, seu esquema aparecerá na interface de documentação da API:
## Dataclasses em Estruturas de Dados Aninhadas { #dataclasses-in-nested-data-structures }
Você também pode combinar `dataclasses` com outras anotações de tipo para criar estruturas de dados aninhadas.
Em alguns casos, você ainda pode ter que usar a versão do Pydantic das `dataclasses`. Por exemplo, se você tiver erros com a documentação da API gerada automaticamente.
Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Ainda importamos `field` das `dataclasses` padrão.
2. `pydantic.dataclasses` é um substituto direto para `dataclasses`.
3. A dataclass `Author` inclui uma lista de dataclasses `Item`.
4. A dataclass `Author` é usada como o parâmetro `response_model`.
5. Você pode usar outras anotações de tipo padrão com dataclasses como o corpo da requisição.
Neste caso, é uma lista de dataclasses `Item`.
6. Aqui estamos retornando um dicionário que contém `items`, que é uma lista de dataclasses.
O FastAPI ainda é capaz de serializar os dados para JSON.
7. Aqui o `response_model` está usando uma anotação de tipo de uma lista de dataclasses `Author`.
Novamente, você pode combinar `dataclasses` com anotações de tipo padrão.
8. Note que esta *função de operação de rota* usa `def` regular em vez de `async def`.
Como sempre, no FastAPI você pode combinar `def` e `async def` conforme necessário.
Se você precisar de uma atualização sobre quando usar qual, confira a seção _"Com pressa?"_ na documentação sobre [`async` e `await`](../async.md#in-a-hurry).
9. Esta *função de operação de rota* não está retornando dataclasses (embora pudesse), mas uma lista de dicionários com dados internos.
O FastAPI usará o parâmetro `response_model` (que inclui dataclasses) para converter a resposta.
Você pode combinar `dataclasses` com outras anotações de tipo em muitas combinações diferentes para formar estruturas de dados complexas.
Confira as dicas de anotação no código acima para ver mais detalhes específicos.
## Saiba Mais { #learn-more }
Você também pode combinar `dataclasses` com outros modelos Pydantic, herdar deles, incluí-los em seus próprios modelos, etc.
Para saber mais, confira a [documentação do Pydantic sobre dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Versão { #version }
Isso está disponível desde a versão `0.67.0` do FastAPI. 🔖
================================================
FILE: docs/pt/docs/advanced/events.md
================================================
# Eventos de lifespan { #lifespan-events }
Você pode definir a lógica (código) que deve ser executada antes da aplicação **inicializar**. Isso significa que esse código será executado **uma vez**, **antes** de a aplicação **começar a receber requisições**.
Da mesma forma, você pode definir a lógica (código) que deve ser executada quando a aplicação estiver **encerrando**. Nesse caso, esse código será executado **uma vez**, **depois** de possivelmente ter tratado **várias requisições**.
Como esse código é executado antes de a aplicação **começar** a receber requisições e logo depois que ela **termina** de lidar com as requisições, ele cobre todo o **lifespan** da aplicação (a palavra "lifespan" será importante em um segundo 😉).
Isso pode ser muito útil para configurar **recursos** que você precisa usar por toda a aplicação, e que são **compartilhados** entre as requisições e/ou que você precisa **limpar** depois. Por exemplo, um pool de conexões com o banco de dados ou o carregamento de um modelo de Aprendizado de Máquina compartilhado.
## Caso de uso { #use-case }
Vamos começar com um exemplo de **caso de uso** e então ver como resolvê-lo com isso.
Vamos imaginar que você tem alguns **modelos de Aprendizado de Máquina** que deseja usar para lidar com as requisições. 🤖
Os mesmos modelos são compartilhados entre as requisições, então não é um modelo por requisição, ou um por usuário, ou algo parecido.
Vamos imaginar que o carregamento do modelo pode **demorar bastante tempo**, porque ele precisa ler muitos **dados do disco**. Então você não quer fazer isso a cada requisição.
Você poderia carregá-lo no nível mais alto do módulo/arquivo, mas isso também significaria **carregar o modelo** mesmo se você estivesse executando um teste automatizado simples; então esse teste poderia ser **lento** porque teria que esperar o carregamento do modelo antes de conseguir executar uma parte independente do código.
É isso que vamos resolver: vamos carregar o modelo antes de as requisições serem tratadas, mas apenas um pouco antes de a aplicação começar a receber requisições, não enquanto o código estiver sendo carregado.
## Lifespan { #lifespan }
Você pode definir essa lógica de *inicialização* e *encerramento* usando o parâmetro `lifespan` da aplicação `FastAPI`, e um "gerenciador de contexto" (vou mostrar o que é isso em um segundo).
Vamos começar com um exemplo e depois ver em detalhes.
Nós criamos uma função assíncrona `lifespan()` com `yield` assim:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Aqui estamos simulando a operação de *inicialização* custosa de carregar o modelo, colocando a (falsa) função do modelo no dicionário com modelos de Aprendizado de Máquina antes do `yield`. Esse código será executado **antes** de a aplicação **começar a receber requisições**, durante a *inicialização*.
E então, logo após o `yield`, descarregamos o modelo. Esse código será executado **depois** de a aplicação **terminar de lidar com as requisições**, pouco antes do *encerramento*. Isso poderia, por exemplo, liberar recursos como memória ou uma GPU.
/// tip | Dica
O `shutdown` aconteceria quando você estivesse **encerrando** a aplicação.
Talvez você precise iniciar uma nova versão, ou apenas cansou de executá-la. 🤷
///
### Função lifespan { #lifespan-function }
A primeira coisa a notar é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante a Dependências com `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
A primeira parte da função, antes do `yield`, será executada **antes** de a aplicação iniciar.
E a parte posterior ao `yield` será executada **depois** de a aplicação ter terminado.
### Gerenciador de contexto assíncrono { #async-context-manager }
Se você verificar, a função está decorada com um `@asynccontextmanager`.
Isso converte a função em algo chamado "**gerenciador de contexto assíncrono**".
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
```Python
with open("file.txt") as file:
file.read()
```
Em versões mais recentes do Python, há também um **gerenciador de contexto assíncrono**. Você o usaria com `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Quando você cria um gerenciador de contexto ou um gerenciador de contexto assíncrono como acima, o que ele faz é: antes de entrar no bloco `with`, ele executa o código antes do `yield`, e após sair do bloco `with`, ele executa o código depois do `yield`.
No nosso exemplo de código acima, não o usamos diretamente, mas passamos para o FastAPI para que ele o use.
O parâmetro `lifespan` da aplicação `FastAPI` aceita um **gerenciador de contexto assíncrono**, então podemos passar para ele nosso novo gerenciador de contexto assíncrono `lifespan`.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Eventos alternativos (descontinuados) { #alternative-events-deprecated }
/// warning | Atenção
A forma recomendada de lidar com a *inicialização* e o *encerramento* é usando o parâmetro `lifespan` da aplicação `FastAPI`, como descrito acima. Se você fornecer um parâmetro `lifespan`, os manipuladores de eventos `startup` e `shutdown` não serão mais chamados. É tudo `lifespan` ou tudo por eventos, não ambos.
Você provavelmente pode pular esta parte.
///
Existe uma forma alternativa de definir essa lógica para ser executada durante a *inicialização* e durante o *encerramento*.
Você pode definir manipuladores de eventos (funções) que precisam ser executados antes de a aplicação iniciar ou quando a aplicação estiver encerrando.
Essas funções podem ser declaradas com `async def` ou `def` normal.
### Evento `startup` { #startup-event }
Para adicionar uma função que deve rodar antes de a aplicação iniciar, declare-a com o evento `"startup"`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
Nesse caso, a função de manipulador do evento `startup` inicializará os itens do "banco de dados" (apenas um `dict`) com alguns valores.
Você pode adicionar mais de uma função de manipulador de eventos.
E sua aplicação não começará a receber requisições até que todos os manipuladores de eventos `startup` sejam concluídos.
### Evento `shutdown` { #shutdown-event }
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare-a com o evento `"shutdown"`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Aqui, a função de manipulador do evento `shutdown` escreverá uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
/// info | Informação
Na função `open()`, o `mode="a"` significa "acrescentar", então a linha será adicionada depois do que já estiver naquele arquivo, sem sobrescrever o conteúdo anterior.
///
/// tip | Dica
Perceba que, nesse caso, estamos usando a função padrão do Python `open()` que interage com um arquivo.
Então, isso envolve I/O (input/output), que requer "esperar" que as coisas sejam escritas em disco.
Mas `open()` não usa `async` e `await`.
Assim, declaramos a função de manipulador de evento com `def` padrão em vez de `async def`.
///
### `startup` e `shutdown` juntos { #startup-and-shutdown-together }
Há uma grande chance de que a lógica para sua *inicialização* e *encerramento* esteja conectada, você pode querer iniciar alguma coisa e então finalizá-la, adquirir um recurso e então liberá-lo, etc.
Fazer isso em funções separadas que não compartilham lógica ou variáveis entre si é mais difícil, pois você precisaria armazenar valores em variáveis globais ou truques semelhantes.
Por causa disso, agora é recomendado usar o `lifespan`, como explicado acima.
## Detalhes técnicos { #technical-details }
Apenas um detalhe técnico para nerds curiosos. 🤓
Por baixo, na especificação técnica do ASGI, isso é parte do [Protocolo Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), e define eventos chamados `startup` e `shutdown`.
/// info | Informação
Você pode ler mais sobre os manipuladores de `lifespan` do Starlette na [Documentação do Lifespan do Starlette](https://www.starlette.dev/lifespan/).
Incluindo como lidar com estado do lifespan que pode ser usado em outras áreas do seu código.
///
## Sub Aplicações { #sub-applications }
🚨 Tenha em mente que esses eventos de lifespan (inicialização e encerramento) serão executados apenas para a aplicação principal, não para [Sub Aplicações - Montagem](sub-applications.md).
================================================
FILE: docs/pt/docs/advanced/generate-clients.md
================================================
# Gerando SDKs { #generating-sdks }
Como o **FastAPI** é baseado na especificação **OpenAPI**, suas APIs podem ser descritas em um formato padrão que muitas ferramentas entendem.
Isso facilita gerar **documentação** atualizada, bibliotecas clientes (**SDKs**) em várias linguagens e **testes** ou **fluxos de trabalho de automação** que permanecem em sincronia com o seu código.
Neste guia, você aprenderá como gerar um **SDK em TypeScript** para o seu backend FastAPI.
## Geradores de SDK de código aberto { #open-source-sdk-generators }
Uma opção versátil é o [OpenAPI Generator](https://openapi-generator.tech/), que suporta **muitas linguagens de programação** e pode gerar SDKs a partir da sua especificação OpenAPI.
Para **clientes TypeScript**, o [Hey API](https://heyapi.dev/) é uma solução feita sob medida, oferecendo uma experiência otimizada para o ecossistema TypeScript.
Você pode descobrir mais geradores de SDK em [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Dica
O FastAPI gera automaticamente especificações **OpenAPI 3.1**, então qualquer ferramenta que você usar deve suportar essa versão.
///
## Geradores de SDK dos patrocinadores do FastAPI { #sdk-generators-from-fastapi-sponsors }
Esta seção destaca soluções **financiadas por investimento** e **com suporte de empresas** que patrocinam o FastAPI. Esses produtos fornecem **funcionalidades adicionais** e **integrações** além de SDKs gerados com alta qualidade.
Ao ✨ [**patrocinar o FastAPI**](../help-fastapi.md#sponsor-the-author) ✨, essas empresas ajudam a garantir que o framework e seu **ecossistema** continuem saudáveis e **sustentáveis**.
O patrocínio também demonstra um forte compromisso com a **comunidade** FastAPI (você), mostrando que elas se importam não apenas em oferecer um **ótimo serviço**, mas também em apoiar um **framework robusto e próspero**, o FastAPI. 🙇
Por exemplo, você pode querer experimentar:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Algumas dessas soluções também podem ser open source ou oferecer planos gratuitos, para que você possa testá-las sem compromisso financeiro. Outros geradores comerciais de SDK estão disponíveis e podem ser encontrados online. 🤓
## Crie um SDK em TypeScript { #create-a-typescript-sdk }
Vamos começar com uma aplicação FastAPI simples:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Note que as *operações de rota* definem os modelos que usam para o corpo da requisição e o corpo da resposta, usando os modelos `Item` e `ResponseMessage`.
### Documentação da API { #api-docs }
Se você for para `/docs`, verá que ela tem os **schemas** para os dados a serem enviados nas requisições e recebidos nas respostas:
Você pode ver esses schemas porque eles foram declarados com os modelos no app.
Essas informações estão disponíveis no **schema OpenAPI** do app e são mostradas na documentação da API.
E essas mesmas informações dos modelos que estão incluídas no OpenAPI são o que pode ser usado para **gerar o código do cliente**.
### Hey API { #hey-api }
Depois que tivermos uma aplicação FastAPI com os modelos, podemos usar o Hey API para gerar um cliente TypeScript. A forma mais rápida é via npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Isso gerará um SDK TypeScript em `./src/client`.
Você pode aprender como [instalar `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started) e ler sobre o [resultado gerado](https://heyapi.dev/openapi-ts/output) no site deles.
### Usando o SDK { #using-the-sdk }
Agora você pode importar e usar o código do cliente. Poderia ser assim, observe que você obtém preenchimento automático para os métodos:
Você também obterá preenchimento automático para o corpo a ser enviado:
/// tip | Dica
Observe o preenchimento automático para `name` e `price`, que foi definido na aplicação FastAPI, no modelo `Item`.
///
Você terá erros em linha para os dados que você envia:
O objeto de resposta também terá preenchimento automático:
## Aplicação FastAPI com Tags { #fastapi-app-with-tags }
Em muitos casos, sua aplicação FastAPI será maior, e você provavelmente usará tags para separar diferentes grupos de *operações de rota*.
Por exemplo, você poderia ter uma seção para **items** e outra seção para **users**, e elas poderiam ser separadas por tags:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Gere um cliente TypeScript com Tags { #generate-a-typescript-client-with-tags }
Se você gerar um cliente para uma aplicação FastAPI usando tags, normalmente também separará o código do cliente com base nas tags.
Dessa forma, você poderá ter as coisas ordenadas e agrupadas corretamente para o código do cliente:
Nesse caso você tem:
* `ItemsService`
* `UsersService`
### Nomes dos métodos do cliente { #client-method-names }
Agora os nomes dos métodos gerados como `createItemItemsPost` não parecem muito “limpos”:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...isso ocorre porque o gerador de clientes usa o **ID de operação** interno do OpenAPI para cada *operação de rota*.
O OpenAPI exige que cada ID de operação seja único em todas as *operações de rota*, então o FastAPI usa o **nome da função**, o **path** e o **método/operação HTTP** para gerar esse ID de operação, porque dessa forma ele pode garantir que os IDs de operação sejam únicos.
Mas eu vou te mostrar como melhorar isso a seguir. 🤓
## IDs de operação personalizados e nomes de métodos melhores { #custom-operation-ids-and-better-method-names }
Você pode **modificar** a maneira como esses IDs de operação são **gerados** para torná-los mais simples e ter **nomes de método mais simples** nos clientes.
Neste caso, você terá que garantir que cada ID de operação seja **único** de alguma outra maneira.
Por exemplo, você poderia garantir que cada *operação de rota* tenha uma tag, e então gerar o ID de operação com base na **tag** e no **nome** da *operação de rota* (o nome da função).
### Função personalizada para gerar IDs exclusivos { #custom-generate-unique-id-function }
O FastAPI usa um **ID exclusivo** para cada *operação de rota*, ele é usado para o **ID de operação** e também para os nomes de quaisquer modelos personalizados necessários, para requisições ou respostas.
Você pode personalizar essa função. Ela recebe uma `APIRoute` e retorna uma string.
Por exemplo, aqui está usando a primeira tag (Você provavelmente terá apenas uma tag) e o nome da *operação de rota* (o nome da função).
Você pode então passar essa função personalizada para o **FastAPI** como o parâmetro `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Gere um cliente TypeScript com IDs de operação personalizados { #generate-a-typescript-client-with-custom-operation-ids }
Agora, se você gerar o cliente novamente, verá que ele tem os nomes dos métodos melhorados:
Como você pode ver, os nomes dos métodos agora têm a tag e, em seguida, o nome da função. Agora eles não incluem informações do path da URL e da operação HTTP.
### Pré-processar a especificação OpenAPI para o gerador de clientes { #preprocess-the-openapi-specification-for-the-client-generator }
O código gerado ainda tem algumas **informações duplicadas**.
Nós já sabemos que esse método está relacionado aos **items** porque essa palavra está no `ItemsService` (retirada da tag), mas ainda temos o nome da tag prefixado no nome do método também. 😕
Provavelmente ainda queremos mantê-lo para o OpenAPI em geral, pois isso garantirá que os IDs de operação sejam **únicos**.
Mas para o cliente gerado, poderíamos **modificar** os IDs de operação do OpenAPI logo antes de gerar os clientes, apenas para tornar esses nomes de método mais agradáveis e **limpos**.
Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Com isso, os IDs de operação seriam renomeados de coisas como `items-get_items` para apenas `get_items`, dessa forma o gerador de clientes pode gerar nomes de métodos mais simples.
### Gere um cliente TypeScript com o OpenAPI pré-processado { #generate-a-typescript-client-with-the-preprocessed-openapi }
Como o resultado final está agora em um arquivo `openapi.json`, você precisa atualizar o local de entrada:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Depois de gerar o novo cliente, você terá agora **nomes de métodos “limpos”**, com todo o **preenchimento automático**, **erros em linha**, etc:
## Benefícios { #benefits }
Ao usar os clientes gerados automaticamente, você terá **preenchimento automático** para:
* Métodos.
* Corpos de requisições, parâmetros de query, etc.
* Corpos de respostas.
Você também terá **erros em linha** para tudo.
E sempre que você atualizar o código do backend e **regenerar** o frontend, ele terá quaisquer novas *operações de rota* disponíveis como métodos, as antigas removidas, e qualquer outra alteração será refletida no código gerado. 🤓
Isso também significa que, se algo mudou, será **refletido** no código do cliente automaticamente. E se você **construir** o cliente, ele falhará caso haja qualquer **incompatibilidade** nos dados usados.
Assim, você **detectará muitos erros** muito cedo no ciclo de desenvolvimento, em vez de ter que esperar que os erros apareçam para seus usuários finais em produção e então tentar depurar onde está o problema. ✨
================================================
FILE: docs/pt/docs/advanced/index.md
================================================
# Guia de Usuário Avançado { #advanced-user-guide }
## Recursos Adicionais { #additional-features }
O [Tutorial - Guia de Usuário](../tutorial/index.md) principal deve ser o suficiente para dar a você um tour por todos os principais recursos do **FastAPI**.
Nas próximas seções você verá outras opções, configurações, e recursos adicionais.
/// tip | Dica
As próximas seções **não são necessariamente "avançadas"**.
E é possível que para seu caso de uso, a solução esteja em uma delas.
///
## Leia o Tutorial primeiro { #read-the-tutorial-first }
Você ainda pode usar a maior parte dos recursos no **FastAPI** com o conhecimento do [Tutorial - Guia de Usuário](../tutorial/index.md) principal.
E as próximas seções assumem que você já leu ele, e que você conhece suas ideias principais.
================================================
FILE: docs/pt/docs/advanced/json-base64-bytes.md
================================================
# JSON com bytes em Base64 { #json-with-bytes-as-base64 }
Se sua aplicação precisa receber e enviar dados JSON, mas você precisa incluir dados binários nele, você pode codificá-los em base64.
## Base64 vs Arquivos { #base64-vs-files }
Primeiro, considere se você pode usar [Arquivos na request](../tutorial/request-files.md) para fazer upload de dados binários e [Response personalizada - FileResponse](./custom-response.md#fileresponse--fileresponse-) para enviar dados binários, em vez de codificá-los em JSON.
JSON só pode conter strings codificadas em UTF-8, portanto não pode conter bytes puros.
Base64 pode codificar dados binários em strings, mas, para isso, precisa usar mais caracteres do que os dados binários originais; assim, normalmente é menos eficiente do que arquivos comuns.
Use base64 apenas se realmente precisar incluir dados binários em JSON e não puder usar arquivos para isso.
## Pydantic `bytes` { #pydantic-bytes }
Você pode declarar um modelo Pydantic com campos `bytes` e então usar `val_json_bytes` na configuração do modelo para indicar que deve usar base64 para *validar* os dados JSON de entrada; como parte dessa validação, ele decodificará a string base64 em bytes.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Se você verificar a `/docs`, verá que o campo `data` espera bytes codificados em base64:
Você poderia enviar uma request assim:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Dica
`aGVsbG8=` é a codificação base64 de `hello`.
///
Em seguida, o Pydantic decodificará a string base64 e fornecerá os bytes originais no campo `data` do modelo.
Você receberá uma response assim:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` para dados de saída { #pydantic-bytes-for-output-data }
Você também pode usar campos `bytes` com `ser_json_bytes` na configuração do modelo para dados de saída, e o Pydantic irá *serializar* os bytes como base64 ao gerar a response JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` para dados de entrada e saída { #pydantic-bytes-for-input-and-output-data }
E, claro, você pode usar o mesmo modelo configurado para usar base64 para lidar tanto com a entrada (*validar*) com `val_json_bytes` quanto com a saída (*serializar*) com `ser_json_bytes` ao receber e enviar dados JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/pt/docs/advanced/middleware.md
================================================
# Middleware Avançado { #advanced-middleware }
No tutorial principal você leu como adicionar [Middleware Personalizado](../tutorial/middleware.md) à sua aplicação.
E então você também leu como lidar com [CORS com o `CORSMiddleware`](../tutorial/cors.md).
Nesta seção, veremos como usar outros middlewares.
## Adicionando middlewares ASGI { #adding-asgi-middlewares }
Como o **FastAPI** é baseado no Starlette e implementa a especificação ASGI, você pode usar qualquer middleware ASGI.
O middleware não precisa ser feito para o FastAPI ou Starlette para funcionar, desde que siga a especificação ASGI.
No geral, os middlewares ASGI são classes que esperam receber um aplicativo ASGI como o primeiro argumento.
Então, na documentação de middlewares ASGI de terceiros, eles provavelmente dirão para você fazer algo como:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Mas, o FastAPI (na verdade, o Starlette) fornece uma maneira mais simples de fazer isso que garante que os middlewares internos lidem com erros do servidor e que os manipuladores de exceções personalizados funcionem corretamente.
Para isso, você usa `app.add_middleware()` (como no exemplo para CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` recebe uma classe de middleware como o primeiro argumento e quaisquer argumentos adicionais a serem passados para o middleware.
## Middlewares Integrados { #integrated-middlewares }
**FastAPI** inclui vários middlewares para casos de uso comuns, veremos a seguir como usá-los.
/// note | Detalhes Técnicos
Para os próximos exemplos, você também poderia usar `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vem diretamente do Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Garante que todas as requisições devem ser `https` ou `wss`.
Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Os seguintes argumentos são suportados:
* `allowed_hosts` - Uma lista de nomes de domínio que são permitidos como nomes de host. Domínios com curingas, como `*.example.com`, são suportados para corresponder a subdomínios. Para permitir qualquer nome de host, use `allowed_hosts=["*"]` ou omita o middleware.
* `www_redirect` - Se definido como True, as requisições para versões sem www dos hosts permitidos serão redirecionadas para suas versões com www. O padrão é `True`.
Se uma requisição recebida não for validada corretamente, uma resposta `400` será enviada.
## `GZipMiddleware` { #gzipmiddleware }
Gerencia respostas GZip para qualquer requisição que inclua `"gzip"` no cabeçalho `Accept-Encoding`.
O middleware lidará com respostas padrão e de streaming.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Os seguintes argumentos são suportados:
* `minimum_size` - Não comprima respostas menores que este tamanho mínimo em bytes. O padrão é `500`.
* `compresslevel` - Usado durante a compressão GZip. É um inteiro variando de 1 a 9. O padrão é `9`. Um valor menor resulta em uma compressão mais rápida, mas em arquivos maiores, enquanto um valor maior resulta em uma compressão mais lenta, mas em arquivos menores.
## Outros middlewares { #other-middlewares }
Há muitos outros middlewares ASGI.
Por exemplo:
* [`ProxyHeadersMiddleware` do Uvicorn](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Para checar outros middlewares disponíveis, confira [Documentação de Middlewares do Starlette](https://www.starlette.dev/middleware/) e a [Lista Incrível do ASGI](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/pt/docs/advanced/openapi-callbacks.md
================================================
# Callbacks na OpenAPI { #openapi-callbacks }
Você poderia criar uma API com uma *operação de rota* que poderia acionar um request a uma *API externa* criada por outra pessoa (provavelmente o mesmo desenvolvedor que estaria *usando* sua API).
O processo que acontece quando sua aplicação de API chama a *API externa* é chamado de "callback". Porque o software que o desenvolvedor externo escreveu envia um request para sua API e então sua API *chama de volta*, enviando um request para uma *API externa* (que provavelmente foi criada pelo mesmo desenvolvedor).
Nesse caso, você poderia querer documentar como essa API externa *deveria* ser. Que *operação de rota* ela deveria ter, que corpo ela deveria esperar, que resposta ela deveria retornar, etc.
## Um aplicativo com callbacks { #an-app-with-callbacks }
Vamos ver tudo isso com um exemplo.
Imagine que você desenvolve um aplicativo que permite criar faturas.
Essas faturas terão um `id`, `title` (opcional), `customer` e `total`.
O usuário da sua API (um desenvolvedor externo) criará uma fatura na sua API com um request POST.
Então sua API irá (vamos imaginar):
* Enviar a fatura para algum cliente do desenvolvedor externo.
* Coletar o dinheiro.
* Enviar a notificação de volta para o usuário da API (o desenvolvedor externo).
* Isso será feito enviando um request POST (de *sua API*) para alguma *API externa* fornecida por esse desenvolvedor externo (este é o "callback").
## O aplicativo **FastAPI** normal { #the-normal-fastapi-app }
Vamos primeiro ver como o aplicativo da API normal se pareceria antes de adicionar o callback.
Ele terá uma *operação de rota* que receberá um corpo `Invoice`, e um parâmetro de consulta `callback_url` que conterá a URL para o callback.
Essa parte é bastante normal, a maior parte do código provavelmente já é familiar para você:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Dica
O parâmetro de consulta `callback_url` usa um tipo Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).
///
A única novidade é o `callbacks=invoices_callback_router.routes` como argumento do decorador da *operação de rota*. Veremos o que é isso a seguir.
## Documentando o callback { #documenting-the-callback }
O código real do callback dependerá muito da sua própria aplicação de API.
E provavelmente variará muito de um aplicativo para o outro.
Poderia ser apenas uma ou duas linhas de código, como:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Mas possivelmente a parte mais importante do callback é garantir que o usuário da sua API (o desenvolvedor externo) implemente a *API externa* corretamente, de acordo com os dados que *sua API* vai enviar no corpo do request do callback, etc.
Então, o que faremos a seguir é adicionar o código para documentar como essa *API externa* deve ser para receber o callback de *sua API*.
A documentação aparecerá na Swagger UI em `/docs` na sua API, e permitirá que os desenvolvedores externos saibam como construir a *API externa*.
Esse exemplo não implementa o callback em si (que poderia ser apenas uma linha de código), apenas a parte da documentação.
/// tip | Dica
O callback real é apenas um request HTTP.
Ao implementar o callback por conta própria, você pode usar algo como [HTTPX](https://www.python-httpx.org) ou [Requests](https://requests.readthedocs.io/).
///
## Escreva o código de documentação do callback { #write-the-callback-documentation-code }
Esse código não será executado em seu aplicativo, nós só precisamos dele para *documentar* como essa *API externa* deveria ser.
Mas, você já sabe como criar facilmente documentação automática para uma API com o **FastAPI**.
Então vamos usar esse mesmo conhecimento para documentar como a *API externa* deveria ser... criando as *operações de rota* que a *API externa* deveria implementar (as que sua API irá chamar).
/// tip | Dica
Ao escrever o código para documentar um callback, pode ser útil imaginar que você é aquele *desenvolvedor externo*. E que você está atualmente implementando a *API externa*, não *sua API*.
Adotar temporariamente esse ponto de vista (do *desenvolvedor externo*) pode ajudar a perceber mais facilmente onde colocar os parâmetros, o modelo Pydantic para o corpo, para a resposta, etc. para essa *API externa*.
///
### Crie um `APIRouter` de callback { #create-a-callback-apirouter }
Primeiro crie um novo `APIRouter` que conterá um ou mais callbacks.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Crie a *operação de rota* do callback { #create-the-callback-path-operation }
Para criar a *operação de rota* do callback, use o mesmo `APIRouter` que você criou acima.
Ela deve parecer exatamente como uma *operação de rota* normal do FastAPI:
* Ela provavelmente deveria ter uma declaração do corpo que deveria receber, por exemplo, `body: InvoiceEvent`.
* E também poderia ter uma declaração da resposta que deveria retornar, por exemplo, `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Há 2 diferenças principais de uma *operação de rota* normal:
* Ela não necessita ter nenhum código real, porque seu aplicativo nunca chamará esse código. Ele é usado apenas para documentar a *API externa*. Então, a função poderia ter apenas `pass`.
* O *path* pode conter uma [expressão OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (veja mais abaixo) em que pode usar variáveis com parâmetros e partes do request original enviado para *sua API*.
### A expressão do path do callback { #the-callback-path-expression }
O *path* do callback pode ter uma [expressão OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) que pode conter partes do request original enviado para *sua API*.
Nesse caso, é a `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Então, se o usuário da sua API (o desenvolvedor externo) enviar um request para *sua API* para:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
com um corpo JSON de:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
então *sua API* processará a fatura e, em algum momento posterior, enviará um request de callback para o `callback_url` (a *API externa*):
```
https://www.external.org/events/invoices/2expen51ve
```
com um corpo JSON contendo algo como:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
e esperaria uma resposta daquela *API externa* com um corpo JSON como:
```JSON
{
"ok": true
}
```
/// tip | Dica
Perceba como a URL de callback usada contém a URL recebida como um parâmetro de consulta em `callback_url` (`https://www.external.org/events`) e também o `id` da fatura de dentro do corpo JSON (`2expen51ve`).
///
### Adicione o roteador de callback { #add-the-callback-router }
Nesse ponto você tem a(s) *operação(ões) de rota de callback* necessária(s) (a(s) que o *desenvolvedor externo* deveria implementar na *API externa*) no roteador de callback que você criou acima.
Agora use o parâmetro `callbacks` no decorador da *operação de rota da sua API* para passar o atributo `.routes` (que é na verdade apenas uma `list` de rotas/*operações de path*) do roteador de callback:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Dica
Perceba que você não está passando o roteador em si (`invoices_callback_router`) para `callback=`, mas o atributo `.routes`, como em `invoices_callback_router.routes`.
///
### Verifique a documentação { #check-the-docs }
Agora você pode iniciar seu aplicativo e ir para [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá sua documentação incluindo uma seção "Callbacks" para sua *operação de rota* que mostra como a *API externa* deveria ser:
================================================
FILE: docs/pt/docs/advanced/openapi-webhooks.md
================================================
# Webhooks OpenAPI { #openapi-webhooks }
Existem situações onde você deseja informar os **usuários** da sua API que a sua aplicação pode chamar a aplicação *deles* (enviando uma requisição) com alguns dados, normalmente para **notificar** algum tipo de **evento**.
Isso significa que no lugar do processo normal de seus usuários enviarem requisições para a sua API, é a **sua API** (ou sua aplicação) que poderia **enviar requisições para o sistema deles** (para a API deles, a aplicação deles).
Isso normalmente é chamado de **webhook**.
## Etapas dos webhooks { #webhooks-steps }
Normalmente, o processo é que **você define** em seu código qual é a mensagem que você irá mandar, o **corpo da sua requisição**.
Você também define de alguma maneira em quais **momentos** a sua aplicação mandará essas requisições ou eventos.
E os **seus usuários** definem de alguma forma (em algum painel por exemplo) a **URL** que a sua aplicação deve enviar essas requisições.
Toda a **lógica** sobre como cadastrar as URLs para os webhooks e o código para enviar de fato as requisições cabe a você definir. Você escreve da maneira que você desejar no **seu próprio código**.
## Documentando webhooks com o **FastAPI** e OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
Com o **FastAPI**, utilizando o OpenAPI, você pode definir os nomes destes webhooks, os tipos das operações HTTP que a sua aplicação pode enviar (e.g. `POST`, `PUT`, etc.) e os **corpos** da requisição que a sua aplicação enviaria.
Isto pode facilitar bastante para os seus usuários **implementarem as APIs deles** para receber as requisições dos seus **webhooks**, eles podem inclusive ser capazes de gerar parte do código da API deles.
/// info | Informação
Webhooks estão disponíveis a partir do OpenAPI 3.1.0, e possui suporte do FastAPI a partir da versão `0.99.0`.
///
## Uma aplicação com webhooks { #an-app-with-webhooks }
Quando você cria uma aplicação com o **FastAPI**, existe um atributo chamado `webhooks`, que você utilizar para defini-los da mesma maneira que você definiria as suas **operações de rotas**, utilizando por exemplo `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Os webhooks que você define aparecerão no esquema do **OpenAPI** e na **página de documentação** gerada automaticamente.
/// info | Informação
O objeto `app.webhooks` é na verdade apenas um `APIRouter`, o mesmo tipo que você utilizaria ao estruturar a sua aplicação com diversos arquivos.
///
Note que utilizando webhooks você não está de fato declarando um *path* (como `/items/`), o texto que informa é apenas um **identificador** do webhook (o nome do evento), por exemplo em `@app.webhooks.post("new-subscription")`, o nome do webhook é `new-subscription`.
Isto porque espera-se que os **seus usuários** definam o verdadeiro **URL path** onde eles desejam receber a requisição do webhook de algum outra maneira. (e.g. um painel).
### Confira a documentação { #check-the-docs }
Agora você pode iniciar a sua aplicação e ir até [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá que a sua documentação possui as *operações de rota* normais e agora também possui alguns **webhooks**:
================================================
FILE: docs/pt/docs/advanced/path-operation-advanced-configuration.md
================================================
# Configuração Avançada da Operação de Rota { #path-operation-advanced-configuration }
## operationId do OpenAPI { #openapi-operationid }
/// warning | Atenção
Se você não é um "especialista" no OpenAPI, você provavelmente não precisa disso.
///
Você pode definir o `operationId` do OpenAPI que será utilizado na sua *operação de rota* com o parâmetro `operation_id`.
Você deveria ter certeza que ele é único para cada operação.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Utilizando o nome da *função de operação de rota* como o operationId { #using-the-path-operation-function-name-as-the-operationid }
Se você quiser utilizar o nome das funções da sua API como `operationId`s, você pode iterar sobre todos esses nomes e sobrescrever o `operation_id` em cada *operação de rota* utilizando o `APIRoute.name` dela.
Você deveria fazer isso depois de adicionar todas as suas *operações de rota*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Dica
Se você chamar `app.openapi()` manualmente, você deveria atualizar os `operationId`s antes dessa chamada.
///
/// warning | Atenção
Se você fizer isso, você tem que ter certeza de que cada uma das suas *funções de operação de rota* tem um nome único.
Mesmo que elas estejam em módulos (arquivos Python) diferentes.
///
## Excluir do OpenAPI { #exclude-from-openapi }
Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequência, dos sistemas de documentação automáticos), utilize o parâmetro `include_in_schema` e defina ele como `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Descrição avançada a partir de docstring { #advanced-description-from-docstring }
Você pode limitar as linhas utilizadas a partir da docstring de uma *função de operação de rota* para o OpenAPI.
Adicionar um `\f` (um caractere de escape para "form feed") faz com que o **FastAPI** trunque a saída usada para o OpenAPI até esse ponto.
Ele não será mostrado na documentação, mas outras ferramentas (como o Sphinx) serão capazes de utilizar o resto.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Respostas Adicionais { #additional-responses }
Você provavelmente já viu como declarar o `response_model` e `status_code` para uma *operação de rota*.
Isso define os metadados sobre a resposta principal da *operação de rota*.
Você também pode declarar respostas adicionais, com seus modelos, códigos de status, etc.
Existe um capítulo inteiro da nossa documentação sobre isso, você pode ler em [Respostas Adicionais no OpenAPI](additional-responses.md).
## Extras do OpenAPI { #openapi-extra }
Quando você declara uma *operação de rota* na sua aplicação, o **FastAPI** irá gerar os metadados relevantes da *operação de rota* automaticamente para serem incluídos no esquema do OpenAPI.
/// note | Detalhes Técnicos
Na especificação do OpenAPI, isso é chamado de um [Objeto de Operação](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
Ele possui toda a informação sobre a *operação de rota* e é usado para gerar a documentação automaticamente.
Ele inclui os atributos `tags`, `parameters`, `requestBody`, `responses`, etc.
Esse esquema específico para uma *operação de rota* normalmente é gerado automaticamente pelo **FastAPI**, mas você também pode estender ele.
/// tip | Dica
Esse é um ponto de extensão de baixo nível.
Caso você só precise declarar respostas adicionais, uma forma conveniente de fazer isso é com [Respostas Adicionais no OpenAPI](additional-responses.md).
///
Você pode estender o esquema do OpenAPI para uma *operação de rota* utilizando o parâmetro `openapi_extra`.
### Extensões do OpenAPI { #openapi-extensions }
Esse parâmetro `openapi_extra` pode ser útil, por exemplo, para declarar [Extensões do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Se você abrir os documentos criados automaticamente para a API, sua extensão aparecerá no final da *operação de rota* específica.
E se você olhar o esquema OpenAPI resultante (na rota `/openapi.json` da sua API), você verá que a sua extensão também faz parte da *operação de rota* específica:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Esquema de *operação de rota* do OpenAPI personalizado { #custom-openapi-path-operation-schema }
O dicionário em `openapi_extra` vai ser mesclado profundamente com o esquema OpenAPI gerado automaticamente para a *operação de rota*.
Então, você pode adicionar dados extras ao esquema gerado automaticamente.
Por exemplo, você poderia decidir ler e validar a requisição com seu próprio código, sem usar as funcionalidades automáticas do FastAPI com o Pydantic, mas ainda assim querer definir a requisição no esquema OpenAPI.
Você pode fazer isso com `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo analisado como JSON, ele é lido diretamente como `bytes`, e a função `magic_data_reader()` seria a responsável por analisá-lo de alguma forma.
De toda forma, nós podemos declarar o esquema esperado para o corpo da requisição.
### Tipo de conteúdo do OpenAPI personalizado { #custom-openapi-content-type }
Utilizando esse mesmo truque, você pode usar um modelo Pydantic para definir o JSON Schema que é então incluído na seção do esquema personalizado do OpenAPI na *operação de rota*.
E você pode fazer isso até mesmo quando o tipo de dados na requisição não é JSON.
Por exemplo, nesta aplicação nós não usamos a funcionalidade integrada ao FastAPI de extrair o JSON Schema dos modelos Pydantic nem a validação automática para JSON. Na verdade, estamos declarando o tipo de conteúdo da requisição como YAML, em vez de JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Entretanto, mesmo que não utilizemos a funcionalidade integrada por padrão, ainda estamos usando um modelo Pydantic para gerar um JSON Schema manualmente para os dados que queremos receber em YAML.
Então utilizamos a requisição diretamente e extraímos o corpo como `bytes`. Isso significa que o FastAPI não vai sequer tentar analisar o payload da requisição como JSON.
E então no nosso código, nós analisamos o conteúdo YAML diretamente e, em seguida, estamos usando novamente o mesmo modelo Pydantic para validar o conteúdo YAML:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Dica
Aqui reutilizamos o mesmo modelo do Pydantic.
Mas da mesma forma, nós poderíamos ter validado de alguma outra forma.
///
================================================
FILE: docs/pt/docs/advanced/response-change-status-code.md
================================================
# Retorno - Altere o Código de Status { #response-change-status-code }
Você provavelmente leu anteriormente que você pode definir um [Código de Status do Retorno](../tutorial/response-status-code.md) padrão.
Porém em alguns casos você precisa retornar um código de status diferente do padrão.
## Caso de uso { #use-case }
Por exemplo, imagine que você deseja retornar um código de status HTTP de "OK" `200` por padrão.
Mas se o dado não existir, você quer criá-lo e retornar um código de status HTTP de "CREATED" `201`.
Mas você ainda quer ser capaz de filtrar e converter o dado que você retornará com um `response_model`.
Para estes casos, você pode utilizar um parâmetro `Response`.
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` em sua *função de operação de rota* (assim como você pode fazer para cookies e headers).
E então você pode definir o `status_code` neste objeto de retorno *temporal*.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
E então você pode retornar qualquer objeto que você precise, como você faria normalmente (um `dict`, um modelo de banco de dados, etc.).
E se você declarar um `response_model`, ele ainda será utilizado para filtrar e converter o objeto que você retornou.
O **FastAPI** utilizará este retorno *temporal* para extrair o código de status (e também cookies e headers), e irá colocá-los no retorno final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` nas dependências, e definir o código de status nelas. Mas lembre-se que o último que for definido é o que prevalecerá.
================================================
FILE: docs/pt/docs/advanced/response-cookies.md
================================================
# Cookies de Resposta { #response-cookies }
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota*.
E então você pode definir cookies nesse objeto de resposta *temporário*.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Em seguida, você pode retornar qualquer objeto que precise, como normalmente faria (um `dict`, um modelo de banco de dados, etc).
E se você declarou um `response_model`, ele ainda será usado para filtrar e converter o objeto que você retornou.
**FastAPI** usará essa resposta *temporária* para extrair os cookies (também os cabeçalhos e código de status) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` em dependências e definir cookies (e cabeçalhos) nelas.
## Retorne uma `Response` diretamente { #return-a-response-directly }
Você também pode criar cookies ao retornar uma `Response` diretamente no seu código.
Para fazer isso, você pode criar uma resposta como descrito em [Retorne uma Resposta Diretamente](response-directly.md).
Então, defina os cookies nela e a retorne:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Dica
Lembre-se de que se você retornar uma resposta diretamente em vez de usar o parâmetro `Response`, FastAPI a retornará diretamente.
Portanto, você terá que garantir que seus dados sejam do tipo correto. E.g. será compatível com JSON se você estiver retornando um `JSONResponse`.
E também que você não esteja enviando nenhum dado que deveria ter sido filtrado por um `response_model`.
///
### Mais informações { #more-info }
/// note | Detalhes Técnicos
Você também poderia usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fornece as mesmas `starlette.responses` em `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
E como o `Response` pode ser usado frequentemente para definir cabeçalhos e cookies, o **FastAPI** também o fornece em `fastapi.Response`.
///
Para ver todos os parâmetros e opções disponíveis, verifique a [documentação no Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/pt/docs/advanced/response-directly.md
================================================
# Retornando uma Resposta Diretamente { #return-a-response-directly }
Quando você cria uma *operação de rota* no **FastAPI**, normalmente você pode retornar qualquer dado: um `dict`, uma `list`, um modelo do Pydantic, um modelo do banco de dados, etc.
Se você declarar um [Modelo de resposta](../tutorial/response-model.md), o FastAPI irá usá-lo para serializar os dados para JSON, usando o Pydantic.
Se você não declarar um modelo de resposta, o FastAPI usará o `jsonable_encoder` explicado em [Codificador Compatível com JSON](../tutorial/encoder.md) e o colocará em uma `JSONResponse`.
Você também pode criar uma `JSONResponse` diretamente e retorná-la.
/// tip | Dica
Normalmente você terá um desempenho muito melhor usando um [Modelo de resposta](../tutorial/response-model.md) do que retornando uma `JSONResponse` diretamente, pois assim ele serializa os dados usando o Pydantic, em Rust.
///
## Retornando uma `Response` { #return-a-response }
Você pode retornar uma `Response` ou qualquer subclasse dela.
/// info | Informação
A própria `JSONResponse` é uma subclasse de `Response`.
///
E quando você retorna uma `Response`, o **FastAPI** vai repassá-la diretamente.
Ele não fará conversões de dados com modelos do Pydantic, não converterá o conteúdo para nenhum tipo, etc.
Isso te dá bastante flexibilidade. Você pode retornar qualquer tipo de dado, sobrescrever qualquer declaração e validação nos dados, etc.
Isso também te dá muita responsabilidade. Você precisa garantir que os dados retornados estão corretos, no formato correto, que podem ser serializados, etc.
## Utilizando o `jsonable_encoder` em uma `Response` { #using-the-jsonable-encoder-in-a-response }
Como o **FastAPI** não realiza nenhuma mudança na `Response` que você retorna, você precisa garantir que o conteúdo dela está pronto para uso.
Por exemplo, você não pode colocar um modelo do Pydantic em uma `JSONResponse` sem antes convertê-lo em um `dict` com todos os tipos de dados (como `datetime`, `UUID`, etc) convertidos para tipos compatíveis com JSON.
Para esses casos, você pode usar o `jsonable_encoder` para converter seus dados antes de repassá-los para a resposta:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Detalhes Técnicos
Você também pode utilizar `from starlette.responses import JSONResponse`.
**FastAPI** utiliza a mesma `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas maior parte das respostas disponíveis vem diretamente do Starlette.
///
## Retornando uma `Response` personalizada { #returning-a-custom-response }
O exemplo acima mostra todas as partes que você precisa, mas ainda não é muito útil, já que você poderia ter retornado o `item` diretamente, e o **FastAPI** colocaria em uma `JSONResponse` para você, convertendo em um `dict`, etc. Tudo isso por padrão.
Agora, vamos ver como você pode usar isso para retornar uma resposta personalizada.
Vamos dizer que você quer retornar uma resposta [XML](https://en.wikipedia.org/wiki/XML).
Você pode colocar o seu conteúdo XML em uma string, colocar em uma `Response`, e retorná-lo:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Como funciona um Modelo de resposta { #how-a-response-model-works }
Quando você declara um [Modelo de resposta - Tipo de retorno](../tutorial/response-model.md) em uma operação de rota, o **FastAPI** irá usá-lo para serializar os dados para JSON, usando o Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Como isso acontece no lado do Rust, o desempenho será muito melhor do que se fosse feito com Python comum e a classe `JSONResponse`.
Ao usar um `response_model` ou tipo de retorno, o FastAPI não usará o `jsonable_encoder` para converter os dados (o que seria mais lento) nem a classe `JSONResponse`.
Em vez disso, ele pega os bytes JSON gerados com o Pydantic usando o modelo de resposta (ou tipo de retorno) e retorna uma `Response` com o media type correto para JSON diretamente (`application/json`).
## Notas { #notes }
Quando você retorna uma `Response` diretamente os dados não são validados, convertidos (serializados) ou documentados automaticamente.
Mas você ainda pode documentar como descrito em [Respostas adicionais no OpenAPI](additional-responses.md).
Você pode ver nas próximas seções como usar/declarar essas `Responses` customizadas enquanto mantém a conversão e documentação automática dos dados.
================================================
FILE: docs/pt/docs/advanced/response-headers.md
================================================
# Cabeçalhos de resposta { #response-headers }
## Use um parâmetro `Response` { #use-a-response-parameter }
Você pode declarar um parâmetro do tipo `Response` na sua *função de operação de rota* (assim como você pode fazer para cookies).
Então você pode definir os cabeçalhos nesse objeto de resposta *temporário*.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Em seguida você pode retornar qualquer objeto que precisar, da maneira que faria normalmente (um `dict`, um modelo de banco de dados, etc.).
Se você declarou um `response_model`, ele ainda será utilizado para filtrar e converter o objeto que você retornou.
**FastAPI** usará essa resposta *temporária* para extrair os cabeçalhos (cookies e código de status também) e os colocará na resposta final que contém o valor que você retornou, filtrado por qualquer `response_model`.
Você também pode declarar o parâmetro `Response` em dependências e definir cabeçalhos (e cookies) nelas.
## Retorne uma `Response` diretamente { #return-a-response-directly }
Você também pode adicionar cabeçalhos quando retornar uma `Response` diretamente.
Crie uma resposta conforme descrito em [Retornar uma resposta diretamente](response-directly.md) e passe os cabeçalhos como um parâmetro adicional:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Detalhes Técnicos
Você também pode usar `from starlette.responses import Response` ou `from starlette.responses import JSONResponse`.
**FastAPI** fornece as mesmas `starlette.responses` como `fastapi.responses` apenas como uma conveniência para você, desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
E como a `Response` pode ser usada frequentemente para definir cabeçalhos e cookies, **FastAPI** também a fornece em `fastapi.Response`.
///
## Cabeçalhos personalizados { #custom-headers }
Tenha em mente que cabeçalhos personalizados proprietários podem ser adicionados [usando o prefixo `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Porém, se você tiver cabeçalhos personalizados que deseja que um cliente no navegador possa ver, você precisa adicioná-los às suas configurações de CORS (saiba mais em [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), usando o parâmetro `expose_headers` descrito na [documentação de CORS do Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/pt/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
Para os casos mais simples, você pode utilizar o HTTP Basic Auth.
No HTTP Basic Auth, a aplicação espera um cabeçalho que contém um usuário e uma senha.
Caso ela não receba, ela retorna um erro HTTP 401 "Unauthorized".
E retorna um cabeçalho `WWW-Authenticate` com o valor `Basic`, e um parâmetro opcional `realm`.
Isso sinaliza ao navegador para mostrar o prompt integrado para um usuário e senha.
Então, quando você digitar o usuário e senha, o navegador os envia automaticamente no cabeçalho.
## HTTP Basic Auth Simples { #simple-http-basic-auth }
* Importe `HTTPBasic` e `HTTPBasicCredentials`.
* Crie um "esquema `security`" utilizando `HTTPBasic`.
* Utilize o `security` com uma dependência em sua *operação de rota*.
* Isso retorna um objeto do tipo `HTTPBasicCredentials`:
* Isto contém o `username` e o `password` enviado.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Quando você tentar abrir a URL pela primeira vez (ou clicar no botão "Executar" na documentação) o navegador vai pedir pelo seu usuário e senha:
## Verifique o usuário { #check-the-username }
Aqui está um exemplo mais completo.
Utilize uma dependência para verificar se o usuário e a senha estão corretos.
Para isso, utilize o módulo padrão do Python [`secrets`](https://docs.python.org/3/library/secrets.html) para verificar o usuário e senha.
O `secrets.compare_digest()` necessita receber `bytes` ou `str` que possuem apenas caracteres ASCII (os em inglês). Isso significa que não funcionaria com caracteres como o `á`, como em `Sebastián`.
Para lidar com isso, primeiramente nós convertemos o `username` e o `password` para `bytes`, codificando-os com UTF-8.
Então nós podemos utilizar o `secrets.compare_digest()` para garantir que o `credentials.username` é `"stanleyjobson"`, e que o `credentials.password` é `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Isso seria parecido com:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Return some error
...
```
Porém, ao utilizar o `secrets.compare_digest()`, isso estará seguro contra um tipo de ataque chamado "timing attacks".
### Ataques de Temporização { #timing-attacks }
Mas o que é um "timing attack"?
Vamos imaginar que alguns invasores estão tentando adivinhar o usuário e a senha.
E eles enviam uma requisição com um usuário `johndoe` e uma senha `love123`.
Então o código Python em sua aplicação seria equivalente a algo como:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Mas no exato momento que o Python compara o primeiro `j` em `johndoe` contra o primeiro `s` em `stanleyjobson`, ele retornará `False`, porque ele já sabe que aquelas duas strings não são a mesma, pensando que "não existe a necessidade de desperdiçar mais poder computacional comparando o resto das letras". E a sua aplicação dirá "Usuário ou senha incorretos".
Então os invasores vão tentar com o usuário `stanleyjobsox` e a senha `love123`.
E a sua aplicação faz algo como:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
O Python terá que comparar todo o `stanleyjobso` tanto em `stanleyjobsox` como em `stanleyjobson` antes de perceber que as strings não são a mesma. Então isso levará alguns microssegundos a mais para retornar "Usuário ou senha incorretos".
#### O tempo para responder ajuda os invasores { #the-time-to-answer-helps-the-attackers }
Neste ponto, ao perceber que o servidor demorou alguns microssegundos a mais para enviar o retorno "Usuário ou senha incorretos", os invasores irão saber que eles acertaram _alguma coisa_, algumas das letras iniciais estavam certas.
E eles podem tentar de novo sabendo que provavelmente é algo mais parecido com `stanleyjobsox` do que com `johndoe`.
#### Um ataque "profissional" { #a-professional-attack }
Claro, os invasores não tentariam tudo isso de forma manual, eles escreveriam um programa para fazer isso, possivelmente com milhares ou milhões de testes por segundo. E obteriam apenas uma letra a mais por vez.
Mas fazendo isso, em alguns minutos ou horas os invasores teriam adivinhado o usuário e senha corretos, com a "ajuda" da nossa aplicação, apenas usando o tempo levado para responder.
#### Corrija com o `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Mas em nosso código já estamos utilizando o `secrets.compare_digest()`.
Resumindo, levará o mesmo tempo para comparar `stanleyjobsox` com `stanleyjobson` do que comparar `johndoe` com `stanleyjobson`. E o mesmo para a senha.
Deste modo, ao utilizar `secrets.compare_digest()` no código de sua aplicação, ela estará a salvo contra toda essa gama de ataques de segurança.
### Retorne o erro { #return-the-error }
Após detectar que as credenciais estão incorretas, retorne um `HTTPException` com o status 401 (o mesmo retornado quando nenhuma credencial foi informada) e adicione o cabeçalho `WWW-Authenticate` para fazer com que o navegador mostre o prompt de login novamente:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/pt/docs/advanced/security/index.md
================================================
# Segurança Avançada { #advanced-security }
## Funcionalidades Adicionais { #additional-features }
Existem algumas funcionalidades adicionais para lidar com segurança além das cobertas em [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md).
/// tip | Dica
As próximas seções **não são necessariamente "avançadas"**.
E é possível que para o seu caso de uso, a solução está em uma delas.
///
## Leia o Tutorial primeiro { #read-the-tutorial-first }
As próximas seções pressupõem que você já leu o principal [Tutorial - Guia de Usuário: Segurança](../../tutorial/security/index.md).
Todas elas são baseadas nos mesmos conceitos, mas permitem algumas funcionalidades extras.
================================================
FILE: docs/pt/docs/advanced/security/oauth2-scopes.md
================================================
# Escopos OAuth2 { #oauth2-scopes }
Você pode utilizar escopos do OAuth2 diretamente com o **FastAPI**, eles são integrados para funcionar perfeitamente.
Isso permitiria que você tivesse um sistema de permissionamento mais refinado, seguindo o padrão do OAuth2 integrado na sua aplicação OpenAPI (e as documentações da API).
OAuth2 com escopos é o mecanismo utilizado por muitos provedores de autenticação, como o Facebook, Google, GitHub, Microsoft, X (Twitter), etc. Eles utilizam isso para prover permissões específicas para os usuários e aplicações.
Toda vez que você "se autentica com" Facebook, Google, GitHub, Microsoft, X (Twitter), aquela aplicação está utilizando o OAuth2 com escopos.
Nesta seção, você verá como gerenciar a autenticação e autorização com os mesmos escopos do OAuth2 em sua aplicação **FastAPI**.
/// warning | Atenção
Isso é uma seção mais ou menos avançada. Se você está apenas começando, você pode pular.
Você não necessariamente precisa de escopos do OAuth2, e você pode lidar com autenticação e autorização da maneira que você achar melhor.
Mas o OAuth2 com escopos pode ser integrado de maneira fácil em sua API (com OpenAPI) e a sua documentação de API.
No entanto, você ainda aplica estes escopos, ou qualquer outro requisito de segurança/autorização, conforme necessário, em seu código.
Em muitos casos, OAuth2 com escopos pode ser um exagero.
Mas se você sabe que precisa, ou está curioso, continue lendo.
///
## Escopos OAuth2 e OpenAPI { #oauth2-scopes-and-openapi }
A especificação OAuth2 define "escopos" como uma lista de strings separadas por espaços.
O conteúdo de cada uma dessas strings pode ter qualquer formato, mas não devem possuir espaços.
Estes escopos representam "permissões".
No OpenAPI (e.g. os documentos da API), você pode definir "esquemas de segurança".
Quando um desses esquemas de segurança utiliza OAuth2, você pode também declarar e utilizar escopos.
Cada "escopo" é apenas uma string (sem espaços).
Eles são normalmente utilizados para declarar permissões de segurança específicas, como por exemplo:
* `users:read` or `users:write` são exemplos comuns.
* `instagram_basic` é utilizado pelo Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` é utilizado pelo Google.
/// info | Informação
No OAuth2, um "escopo" é apenas uma string que declara uma permissão específica necessária.
Não importa se ela contém outros caracteres como `:` ou se ela é uma URL.
Estes detalhes são específicos da implementação.
Para o OAuth2, eles são apenas strings.
///
## Visão global { #global-view }
Primeiro, vamos olhar rapidamente as partes que mudam dos exemplos do **Tutorial - Guia de Usuário** para [OAuth2 com Senha (e hash), Bearer com tokens JWT](../../tutorial/security/oauth2-jwt.md). Agora utilizando escopos OAuth2:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Agora vamos revisar essas mudanças passo a passo.
## Esquema de segurança OAuth2 { #oauth2-security-scheme }
A primeira mudança é que agora nós estamos declarando o esquema de segurança OAuth2 com dois escopos disponíveis, `me` e `items`.
O parâmetro `scopes` recebe um `dict` contendo cada escopo como chave e a descrição como valor:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Pelo motivo de estarmos declarando estes escopos, eles aparecerão nos documentos da API quando você se autenticar/autorizar.
E você poderá selecionar quais escopos você deseja dar acesso: `me` e `items`.
Este é o mesmo mecanismo utilizado quando você adiciona permissões enquanto se autentica com o Facebook, Google, GitHub, etc:
## Token JWT com escopos { #jwt-token-with-scopes }
Agora, modifique a *operação de rota* do token para retornar os escopos solicitados.
Nós ainda estamos utilizando o mesmo `OAuth2PasswordRequestForm`. Ele inclui a propriedade `scopes` com uma `list` de `str`, com cada escopo que ele recebeu na requisição.
E nós retornamos os escopos como parte do token JWT.
/// danger | Cuidado
Para manter as coisas simples, aqui nós estamos apenas adicionando os escopos recebidos diretamente ao token.
Porém em sua aplicação, por segurança, você deveria garantir que você apenas adiciona os escopos que o usuário possui permissão de fato, ou aqueles que você predefiniu.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Declare escopos em *operações de rota* e dependências { #declare-scopes-in-path-operations-and-dependencies }
Agora nós declaramos que a *operação de rota* para `/users/me/items/` exige o escopo `items`.
Para isso, nós importamos e utilizamos `Security` de `fastapi`.
Você pode utilizar `Security` para declarar dependências (assim como `Depends`), porém o `Security` também recebe o parâmetro `scopes` com uma lista de escopos (strings).
Neste caso, nós passamos a função `get_current_active_user` como dependência para `Security` (da mesma forma que nós faríamos com `Depends`).
Mas nós também passamos uma `list` de escopos, neste caso com apenas um escopo: `items` (poderia ter mais).
E a função de dependência `get_current_active_user` também pode declarar subdependências, não apenas com `Depends`, mas também com `Security`. Ao declarar sua própria função de subdependência (`get_current_user`), e mais requisitos de escopo.
Neste caso, ele requer o escopo `me` (poderia requerer mais de um escopo).
/// note | Nota
Você não necessariamente precisa adicionar diferentes escopos em diferentes lugares.
Nós estamos fazendo isso aqui para demonstrar como o **FastAPI** lida com escopos declarados em diferentes níveis.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// note | Detalhes Técnicos
`Security` é na verdade uma subclasse de `Depends`, e ele possui apenas um parâmetro extra que veremos depois.
Porém ao utilizar `Security` no lugar de `Depends`, o **FastAPI** saberá que ele pode declarar escopos de segurança, utilizá-los internamente, e documentar a API com o OpenAPI.
Mas quando você importa `Query`, `Path`, `Depends`, `Security` entre outros de `fastapi`, eles são na verdade funções que retornam classes especiais.
///
## Utilize `SecurityScopes` { #use-securityscopes }
Agora atualize a dependência `get_current_user`.
Este é o usado pelas dependências acima.
Aqui é onde estamos utilizando o mesmo esquema OAuth2 que nós declaramos antes, declarando-o como uma dependência: `oauth2_scheme`.
Porque esta função de dependência não possui nenhum requerimento de escopo, nós podemos utilizar `Depends` com o `oauth2_scheme`. Nós não precisamos utilizar `Security` quando nós não precisamos especificar escopos de segurança.
Nós também declaramos um parâmetro especial do tipo `SecurityScopes`, importado de `fastapi.security`.
A classe `SecurityScopes` é semelhante à classe `Request` (`Request` foi utilizada para obter o objeto da requisição diretamente).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Utilize os `scopes` { #use-the-scopes }
O parâmetro `security_scopes` será do tipo `SecurityScopes`.
Ele terá a propriedade `scopes` com uma lista contendo todos os escopos requeridos por ele e todas as dependências que utilizam ele como uma subdependência. Isso significa, todos os "dependentes"... pode soar meio confuso, e isso será explicado novamente mais adiante.
O objeto `security_scopes` (da classe `SecurityScopes`) também oferece um atributo `scope_str` com uma única string, contendo os escopos separados por espaços (nós vamos utilizar isso).
Nós criamos uma `HTTPException` que nós podemos reutilizar (`raise`) mais tarde em diversos lugares.
Nesta exceção, nós incluímos os escopos necessários (se houver algum) como uma string separada por espaços (utilizando `scope_str`). Nós colocamos esta string contendo os escopos no cabeçalho `WWW-Authenticate` (isso é parte da especificação).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Verifique o `username` e o formato dos dados { #verify-the-username-and-data-shape }
Nós verificamos que nós obtemos um `username`, e extraímos os escopos.
E depois nós validamos esse dado com o modelo Pydantic (capturando a exceção `ValidationError`), e se nós obtemos um erro ao ler o token JWT ou validando os dados com o Pydantic, nós levantamos a exceção `HTTPException` que criamos anteriormente.
Para isso, nós atualizamos o modelo Pydantic `TokenData` com a nova propriedade `scopes`.
Ao validar os dados com o Pydantic nós podemos garantir que temos, por exemplo, exatamente uma `list` de `str` com os escopos e uma `str` com o `username`.
No lugar de, por exemplo, um `dict`, ou alguma outra coisa, que poderia quebrar a aplicação em algum lugar mais tarde, tornando isso um risco de segurança.
Nós também verificamos que nós temos um usuário com o "*username*", e caso contrário, nós levantamos a mesma exceção que criamos anteriormente.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Verifique os `scopes` { #verify-the-scopes }
Nós verificamos agora que todos os escopos necessários, por essa dependência e todos os dependentes (incluindo as *operações de rota*) estão incluídas nos escopos fornecidos pelo token recebido, caso contrário, levantamos uma `HTTPException`.
Para isso, nós utilizamos `security_scopes.scopes`, que contém uma `list` com todos esses escopos como uma `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Árvore de dependência e escopos { #dependency-tree-and-scopes }
Vamos rever novamente essa árvore de dependência e os escopos.
Como a dependência `get_current_active_user` possui uma subdependência em `get_current_user`, o escopo `"me"` declarado em `get_current_active_user` será incluído na lista de escopos necessários em `security_scopes.scopes` passado para `get_current_user`.
A própria *operação de rota* também declara o escopo, `"items"`, então ele também estará na lista de `security_scopes.scopes` passado para o `get_current_user`.
Aqui está como a hierarquia de dependências e escopos parecem:
* A *operação de rota* `read_own_items` possui:
* Escopos necessários `["items"]` com a dependência:
* `get_current_active_user`:
* A função de dependência `get_current_active_user` possui:
* Escopos necessários `["me"]` com a dependência:
* `get_current_user`:
* A função de dependência `get_current_user` possui:
* Nenhum escopo necessário.
* Uma dependência utilizando `oauth2_scheme`.
* Um parâmetro `security_scopes` do tipo `SecurityScopes`:
* Este parâmetro `security_scopes` possui uma propriedade `scopes` com uma `list` contendo todos estes escopos declarados acima, então:
* `security_scopes.scopes` terá `["me", "items"]` para a *operação de rota* `read_own_items`.
* `security_scopes.scopes` terá `["me"]` para a *operação de rota* `read_users_me`, porque ela declarou na dependência `get_current_active_user`.
* `security_scopes.scopes` terá `[]` (nada) para a *operação de rota* `read_system_status`, porque ele não declarou nenhum `Security` com `scopes`, e sua dependência, `get_current_user`, não declara nenhum `scopes` também.
/// tip | Dica
A coisa importante e "mágica" aqui é que `get_current_user` terá diferentes listas de `scopes` para validar para cada *operação de rota*.
Tudo depende dos `scopes` declarados em cada *operação de rota* e cada dependência da árvore de dependências para aquela *operação de rota* específica.
///
## Mais detalhes sobre `SecurityScopes` { #more-details-about-securityscopes }
Você pode utilizar `SecurityScopes` em qualquer lugar, e em diversos lugares. Ele não precisa estar na dependência "raiz".
Ele sempre terá os escopos de segurança declarados nas dependências atuais de `Security` e todos os dependentes para **aquela** *operação de rota* **específica** e **aquela** árvore de dependência **específica**.
Porque o `SecurityScopes` terá todos os escopos declarados por dependentes, você pode utilizá-lo para verificar se o token possui os escopos necessários em uma função de dependência central, e depois declarar diferentes requisitos de escopo em diferentes *operações de rota*.
Todos eles serão validados independentemente para cada *operação de rota*.
## Verifique { #check-it }
Se você abrir os documentos da API, você pode autenticar e especificar quais escopos você quer autorizar.
Se você não selecionar nenhum escopo, você terá "autenticado", mas quando você tentar acessar `/users/me/` ou `/users/me/items/`, você vai obter um erro dizendo que você não possui as permissões necessárias. Você ainda poderá acessar `/status/`.
E se você selecionar o escopo `me`, mas não o escopo `items`, você poderá acessar `/users/me/`, mas não `/users/me/items/`.
Isso é o que aconteceria se uma aplicação terceira que tentou acessar uma dessas *operações de rota* com um token fornecido por um usuário, dependendo de quantas permissões o usuário forneceu para a aplicação.
## Sobre integrações de terceiros { #about-third-party-integrations }
Neste exemplo nós estamos utilizando o fluxo de senha do OAuth2.
Isso é apropriado quando nós estamos autenticando em nossa própria aplicação, provavelmente com o nosso próprio "*frontend*".
Porque nós podemos confiar nele para receber o `username` e o `password`, pois nós controlamos isso.
Mas se nós estamos construindo uma aplicação OAuth2 que outros poderiam conectar (i.e., se você está construindo um provedor de autenticação equivalente ao Facebook, Google, GitHub, etc.) você deveria utilizar um dos outros fluxos.
O mais comum é o fluxo implícito.
O mais seguro é o fluxo de código, mas ele é mais complexo para implementar, pois ele necessita mais passos. Como ele é mais complexo, muitos provedores terminam sugerindo o fluxo implícito.
/// note | Nota
É comum que cada provedor de autenticação nomeie os seus fluxos de forma diferente, para torná-lo parte de sua marca.
Mas no final, eles estão implementando o mesmo padrão OAuth2.
///
O **FastAPI** inclui utilitários para todos esses fluxos de autenticação OAuth2 em `fastapi.security.oauth2`.
## `Security` em decoradores de `dependencies` { #security-in-decorator-dependencies }
Da mesma forma que você pode definir uma `list` de `Depends` no parâmetro `dependencies` do decorador (como explicado em [Dependências em decoradores de operações de rota](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), você também pode utilizar `Security` com escopos lá.
================================================
FILE: docs/pt/docs/advanced/settings.md
================================================
# Configurações e Variáveis de Ambiente { #settings-and-environment-variables }
Em muitos casos, sua aplicação pode precisar de configurações externas, por exemplo chaves secretas, credenciais de banco de dados, credenciais para serviços de e-mail, etc.
A maioria dessas configurações é variável (pode mudar), como URLs de banco de dados. E muitas podem ser sensíveis, como segredos.
Por esse motivo, é comum fornecê-las em variáveis de ambiente lidas pela aplicação.
/// tip | Dica
Para entender variáveis de ambiente, você pode ler [Variáveis de Ambiente](../environment-variables.md).
///
## Tipagem e validação { #types-and-validation }
Essas variáveis de ambiente só conseguem lidar com strings de texto, pois são externas ao Python e precisam ser compatíveis com outros programas e com o resto do sistema (e até com diferentes sistemas operacionais, como Linux, Windows, macOS).
Isso significa que qualquer valor lido em Python a partir de uma variável de ambiente será uma `str`, e qualquer conversão para um tipo diferente ou validação precisa ser feita em código.
## Pydantic `Settings` { #pydantic-settings }
Felizmente, o Pydantic fornece uma ótima utilidade para lidar com essas configurações vindas de variáveis de ambiente com [Pydantic: Settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Instalar `pydantic-settings` { #install-pydantic-settings }
Primeiro, certifique-se de criar seu [ambiente virtual](../virtual-environments.md), ativá-lo e então instalar o pacote `pydantic-settings`:
### Criar o objeto `Settings` { #create-the-settings-object }
Importe `BaseSettings` do Pydantic e crie uma subclasse, muito parecido com um modelo do Pydantic.
Da mesma forma que com modelos do Pydantic, você declara atributos de classe com anotações de tipo e, possivelmente, valores padrão.
Você pode usar as mesmas funcionalidades e ferramentas de validação que usa em modelos do Pydantic, como diferentes tipos de dados e validações adicionais com `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Dica
Se você quer algo rápido para copiar e colar, não use este exemplo, use o último abaixo.
///
Então, quando você cria uma instância dessa classe `Settings` (neste caso, no objeto `settings`), o Pydantic vai ler as variáveis de ambiente sem diferenciar maiúsculas de minúsculas; assim, uma variável em maiúsculas `APP_NAME` ainda será lida para o atributo `app_name`.
Em seguida, ele converterá e validará os dados. Assim, quando você usar esse objeto `settings`, terá dados dos tipos que declarou (por exemplo, `items_per_user` será um `int`).
### Usar o `settings` { #use-the-settings }
Depois você pode usar o novo objeto `settings` na sua aplicação:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Executar o servidor { #run-the-server }
Em seguida, você executaria o servidor passando as configurações como variáveis de ambiente, por exemplo, você poderia definir `ADMIN_EMAIL` e `APP_NAME` com:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Dica
Para definir várias variáveis de ambiente para um único comando, basta separá-las com espaço e colocá-las todas antes do comando.
///
Então a configuração `admin_email` seria definida como `"deadpool@example.com"`.
O `app_name` seria `"ChimichangApp"`.
E `items_per_user` manteria seu valor padrão de `50`.
## Configurações em outro módulo { #settings-in-another-module }
Você pode colocar essas configurações em outro arquivo de módulo como visto em [Aplicações Maiores - Múltiplos Arquivos](../tutorial/bigger-applications.md).
Por exemplo, você poderia ter um arquivo `config.py` com:
{* ../../docs_src/settings/app01_py310/config.py *}
E então usá-lo em um arquivo `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Dica
Você também precisaria de um arquivo `__init__.py` como visto em [Aplicações Maiores - Múltiplos Arquivos](../tutorial/bigger-applications.md).
///
## Configurações em uma dependência { #settings-in-a-dependency }
Em algumas ocasiões, pode ser útil fornecer as configurações a partir de uma dependência, em vez de ter um objeto global `settings` usado em todos os lugares.
Isso pode ser especialmente útil durante os testes, pois é muito fácil sobrescrever uma dependência com suas próprias configurações personalizadas.
### O arquivo de configuração { #the-config-file }
Vindo do exemplo anterior, seu arquivo `config.py` poderia ser assim:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Perceba que agora não criamos uma instância padrão `settings = Settings()`.
### O arquivo principal da aplicação { #the-main-app-file }
Agora criamos uma dependência que retorna um novo `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Dica
Vamos discutir o `@lru_cache` em breve.
Por enquanto, você pode assumir que `get_settings()` é uma função normal.
///
E então podemos exigi-la na *função de operação de rota* como dependência e usá-la onde for necessário.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Configurações e testes { #settings-and-testing }
Então seria muito fácil fornecer um objeto de configurações diferente durante os testes criando uma sobrescrita de dependência para `get_settings`:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
Na sobrescrita da dependência definimos um novo valor para `admin_email` ao criar o novo objeto `Settings`, e então retornamos esse novo objeto.
Depois podemos testar que ele é usado.
## Lendo um arquivo `.env` { #reading-a-env-file }
Se você tiver muitas configurações que possivelmente mudam bastante, talvez em diferentes ambientes, pode ser útil colocá-las em um arquivo e então lê-las como se fossem variáveis de ambiente.
Essa prática é tão comum que tem um nome: essas variáveis de ambiente são comumente colocadas em um arquivo `.env`, e o arquivo é chamado de "dotenv".
/// tip | Dica
Um arquivo começando com um ponto (`.`) é um arquivo oculto em sistemas tipo Unix, como Linux e macOS.
Mas um arquivo dotenv não precisa ter exatamente esse nome de arquivo.
///
O Pydantic tem suporte para leitura desses tipos de arquivos usando uma biblioteca externa. Você pode ler mais em [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Dica
Para isso funcionar, você precisa executar `pip install python-dotenv`.
///
### O arquivo `.env` { #the-env-file }
Você poderia ter um arquivo `.env` com:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Ler configurações do `.env` { #read-settings-from-env }
E então atualizar seu `config.py` com:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Dica
O atributo `model_config` é usado apenas para configuração do Pydantic. Você pode ler mais em [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Aqui definimos a configuração `env_file` dentro da sua classe `Settings` do Pydantic e definimos o valor como o nome do arquivo dotenv que queremos usar.
### Criando o `Settings` apenas uma vez com `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Ler um arquivo do disco normalmente é uma operação custosa (lenta), então você provavelmente vai querer fazer isso apenas uma vez e depois reutilizar o mesmo objeto de configurações, em vez de lê-lo a cada requisição.
Mas toda vez que fizermos:
```Python
Settings()
```
um novo objeto `Settings` seria criado e, na criação, ele leria o arquivo `.env` novamente.
Se a função de dependência fosse assim:
```Python
def get_settings():
return Settings()
```
criaríamos esse objeto para cada requisição e leríamos o arquivo `.env` para cada requisição. ⚠️
Mas como estamos usando o decorador `@lru_cache` por cima, o objeto `Settings` será criado apenas uma vez, na primeira vez em que for chamado. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Em qualquer chamada subsequente de `get_settings()` nas dependências das próximas requisições, em vez de executar o código interno de `get_settings()` e criar um novo objeto `Settings`, ele retornará o mesmo objeto que foi retornado na primeira chamada, repetidamente.
#### Detalhes Técnicos do `lru_cache` { #lru-cache-technical-details }
`@lru_cache` modifica a função que decora para retornar o mesmo valor que foi retornado na primeira vez, em vez de calculá-lo novamente executando o código da função todas as vezes.
Assim, a função abaixo dele será executada uma vez para cada combinação de argumentos. E então os valores retornados para cada uma dessas combinações de argumentos serão usados repetidamente sempre que a função for chamada com exatamente a mesma combinação de argumentos.
Por exemplo, se você tiver uma função:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
seu programa poderia executar assim:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
No caso da nossa dependência `get_settings()`, a função nem recebe argumentos, então ela sempre retorna o mesmo valor.
Dessa forma, ela se comporta quase como se fosse apenas uma variável global. Mas como usa uma função de dependência, podemos sobrescrevê-la facilmente para testes.
`@lru_cache` faz parte de `functools`, que faz parte da biblioteca padrão do Python; você pode ler mais sobre isso na [documentação do Python para `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Recapitulando { #recap }
Você pode usar Pydantic Settings para lidar com as configurações da sua aplicação, com todo o poder dos modelos Pydantic.
* Usando uma dependência você pode simplificar os testes.
* Você pode usar arquivos `.env` com ele.
* Usar `@lru_cache` permite evitar ler o arquivo dotenv repetidamente a cada requisição, enquanto permite sobrescrevê-lo durante os testes.
================================================
FILE: docs/pt/docs/advanced/stream-data.md
================================================
# Transmitir dados { #stream-data }
Se você quer transmitir dados que podem ser estruturados como JSON, você deveria [Transmitir JSON Lines](../tutorial/stream-json-lines.md).
Mas se você quer transmitir dados binários puros ou strings, veja como fazer.
/// info | Informação
Adicionado no FastAPI 0.134.0.
///
## Casos de uso { #use-cases }
Você pode usar isto para transmitir strings puras, por exemplo diretamente da saída de um serviço de AI LLM.
Você também pode usá-lo para transmitir arquivos binários grandes, enviando cada bloco de dados à medida que o lê, sem precisar carregar tudo na memória de uma vez.
Você também pode transmitir vídeo ou áudio desta forma; pode até ser gerado enquanto você processa e envia.
## Um `StreamingResponse` com `yield` { #a-streamingresponse-with-yield }
Se você declarar `response_class=StreamingResponse` na sua função de operação de rota, você pode usar `yield` para enviar cada bloco de dados em sequência.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
O FastAPI entregará cada bloco de dados para `StreamingResponse` como está, não tentará convertê-lo para JSON nem nada semelhante.
### Funções de operação de rota não assíncronas { #non-async-path-operation-functions }
Você também pode usar funções `def` normais (sem `async`) e usar `yield` da mesma forma.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Sem anotação { #no-annotation }
Você não precisa declarar a anotação de tipo de retorno para transmitir dados binários.
Como o FastAPI não tentará converter os dados para JSON com Pydantic nem serializá-los de nenhuma forma, neste caso a anotação de tipo serve apenas para seu editor e ferramentas; ela não será usada pelo FastAPI.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Isso também significa que, com `StreamingResponse`, você tem a liberdade e a responsabilidade de produzir e codificar os bytes exatamente como precisam ser enviados, independentemente das anotações de tipo. 🤓
### Transmitir bytes { #stream-bytes }
Um dos principais casos de uso é transmitir `bytes` em vez de strings; você pode fazer isso sem problemas.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Um `PNGStreamingResponse` personalizado { #a-custom-pngstreamingresponse }
Nos exemplos acima, os bytes eram transmitidos, mas a resposta não tinha um cabeçalho `Content-Type`, então o cliente não sabia que tipo de dado estava recebendo.
Você pode criar uma subclasse personalizada de `StreamingResponse` que define o cabeçalho `Content-Type` para o tipo de dado que você está transmitindo.
Por exemplo, você pode criar um `PNGStreamingResponse` que define o cabeçalho `Content-Type` como `image/png` usando o atributo `media_type`:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Em seguida, você pode usar essa nova classe em `response_class=PNGStreamingResponse` na sua função de operação de rota:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Simular um arquivo { #simulate-a-file }
Neste exemplo, estamos simulando um arquivo com `io.BytesIO`, que é um objeto semelhante a arquivo que vive somente na memória, mas nos permite usar a mesma interface.
Por exemplo, podemos iterar sobre ele para consumir seu conteúdo, como faríamos com um arquivo.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Detalhes Técnicos
As outras duas variáveis, `image_base64` e `binary_image`, são uma imagem codificada em Base64 e depois convertida para bytes, para então passá-la para `io.BytesIO`.
Apenas para que possa viver no mesmo arquivo deste exemplo e você possa copiar e executar como está. 🥚
///
Ao usar um bloco `with`, garantimos que o objeto semelhante a arquivo seja fechado após a função geradora (a função com `yield`) terminar. Ou seja, após terminar de enviar a resposta.
Isso não seria tão importante neste exemplo específico porque é um arquivo falso em memória (com `io.BytesIO`), mas com um arquivo real, seria importante garantir que o arquivo fosse fechado ao final do trabalho.
### Arquivos e async { #files-and-async }
Na maioria dos casos, objetos semelhantes a arquivo não são compatíveis com async e await por padrão.
Por exemplo, eles não têm `await file.read()`, nem `async for chunk in file`.
E, em muitos casos, lê-los seria uma operação bloqueante (que poderia bloquear o loop de eventos), pois são lidos do disco ou da rede.
/// info | Informação
O exemplo acima é, na verdade, uma exceção, porque o objeto `io.BytesIO` já está em memória, então lê-lo não bloqueará nada.
Mas, em muitos casos, ler um arquivo ou um objeto semelhante a arquivo bloquearia.
///
Para evitar bloquear o loop de eventos, você pode simplesmente declarar a função de operação de rota com `def` normal em vez de `async def`. Assim, o FastAPI a executará em um worker de threadpool, evitando bloquear o loop principal.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Dica
Se você precisar chamar código bloqueante de dentro de uma função assíncrona ou uma função assíncrona de dentro de uma função bloqueante, você poderia usar o [Asyncer](https://asyncer.tiangolo.com), uma biblioteca irmã do FastAPI.
///
### `yield from` { #yield-from }
Quando você está iterando sobre algo, como um objeto semelhante a arquivo, e faz `yield` para cada item, você também pode usar `yield from` para produzir cada item diretamente e pular o loop `for`.
Isso não é particular do FastAPI, é apenas Python, mas é um truque útil para conhecer. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/pt/docs/advanced/strict-content-type.md
================================================
# Verificação Estrita de Content-Type { #strict-content-type-checking }
Por padrão, o **FastAPI** usa verificação estrita do cabeçalho `Content-Type` para corpos de requisição JSON; isso significa que requisições JSON devem incluir um `Content-Type` válido (por exemplo, `application/json`) para que o corpo seja interpretado como JSON.
## Risco de CSRF { #csrf-risk }
Esse comportamento padrão oferece proteção contra uma classe de ataques de **Cross-Site Request Forgery (CSRF)** em um cenário muito específico.
Esses ataques exploram o fato de que navegadores permitem que scripts enviem requisições sem fazer qualquer verificação de preflight de CORS quando:
- não têm um cabeçalho `Content-Type` (por exemplo, usando `fetch()` com um corpo `Blob`)
- e não enviam nenhuma credencial de autenticação.
Esse tipo de ataque é relevante principalmente quando:
- a aplicação está em execução localmente (por exemplo, em `localhost`) ou em uma rede interna
- e a aplicação não tem autenticação, pressupondo que qualquer requisição da mesma rede é confiável.
## Exemplo de Ataque { #example-attack }
Imagine que você desenvolve uma forma de executar um agente de IA local.
Ele fornece uma API em
```
http://localhost:8000/v1/agents/multivac
```
Há também um frontend em
```
http://localhost:8000
```
/// tip | Dica
Observe que ambos têm o mesmo host.
///
Usando o frontend, você pode fazer o agente de IA executar ações em seu nome.
Como está em execução localmente e não na Internet aberta, você decide não configurar autenticação, confiando apenas no acesso à rede local.
Então um de seus usuários poderia instalá-lo e executá-lo localmente.
Em seguida, poderia abrir um site malicioso, por exemplo:
```
https://evilhackers.example.com
```
E esse site malicioso envia requisições usando `fetch()` com um corpo `Blob` para a API local em
```
http://localhost:8000/v1/agents/multivac
```
Mesmo que o host do site malicioso e o da aplicação local sejam diferentes, o navegador não acionará uma requisição preflight de CORS porque:
- Está em execução sem autenticação, não precisa enviar credenciais.
- O navegador acha que não está enviando JSON (devido à falta do cabeçalho `Content-Type`).
Então o site malicioso poderia fazer o agente de IA local enviar mensagens raivosas ao ex-chefe do usuário... ou pior. 😅
## Internet Aberta { #open-internet }
Se sua aplicação está na Internet aberta, você não “confiaria na rede” nem deixaria qualquer pessoa enviar requisições privilegiadas sem autenticação.
Atacantes poderiam simplesmente executar um script para enviar requisições à sua API, sem necessidade de interação do navegador, então você provavelmente já está protegendo quaisquer endpoints privilegiados.
Nesse caso, esse ataque/risco não se aplica a você.
Esse risco e ataque é relevante principalmente quando a aplicação roda na rede local e essa é a única proteção presumida.
## Permitindo Requisições sem Content-Type { #allowing-requests-without-content-type }
Se você precisa dar suporte a clientes que não enviam um cabeçalho `Content-Type`, você pode desativar a verificação estrita definindo `strict_content_type=False`:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
Com essa configuração, requisições sem um cabeçalho `Content-Type` terão o corpo interpretado como JSON, o mesmo comportamento das versões mais antigas do FastAPI.
/// info | Informação
Esse comportamento e configuração foram adicionados no FastAPI 0.132.0.
///
================================================
FILE: docs/pt/docs/advanced/sub-applications.md
================================================
# Sub Aplicações - Montagens { #sub-applications-mounts }
Se você precisar ter duas aplicações FastAPI independentes, cada uma com seu próprio OpenAPI e suas próprias interfaces de documentação, você pode ter um aplicativo principal e "montar" uma (ou mais) sub-aplicações.
## Montando uma aplicação **FastAPI** { #mounting-a-fastapi-application }
"Montar" significa adicionar uma aplicação completamente "independente" em um path específico, que então se encarrega de lidar com tudo sob esse path, com as _operações de rota_ declaradas nessa sub-aplicação.
### Aplicação de nível superior { #top-level-application }
Primeiro, crie a aplicação principal, de nível superior, **FastAPI**, e suas *operações de rota*:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Sub-aplicação { #sub-application }
Em seguida, crie sua sub-aplicação e suas *operações de rota*.
Essa sub-aplicação é apenas outra aplicação FastAPI padrão, mas esta é a que será "montada":
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Monte a sub-aplicação { #mount-the-sub-application }
Na sua aplicação de nível superior, `app`, monte a sub-aplicação, `subapi`.
Neste caso, ela será montada no path `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Verifique a documentação automática da API { #check-the-automatic-api-docs }
Agora, execute o comando `fastapi`:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
E abra a documentação em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá a documentação automática da API para a aplicação principal, incluindo apenas suas próprias _operações de rota_:
E então, abra a documentação para a sub-aplicação, em [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Você verá a documentação automática da API para a sub-aplicação, incluindo apenas suas próprias _operações de rota_, todas sob o prefixo de sub-path correto `/subapi`:
Se você tentar interagir com qualquer uma das duas interfaces de usuário, elas funcionarão corretamente, porque o navegador será capaz de se comunicar com cada aplicação ou sub-aplicação específica.
### Detalhes Técnicos: `root_path` { #technical-details-root-path }
Quando você monta uma sub-aplicação como descrito acima, o FastAPI se encarrega de comunicar o path de montagem para a sub-aplicação usando um mecanismo da especificação ASGI chamado `root_path`.
Dessa forma, a sub-aplicação saberá usar esse prefixo de path para a interface de documentação.
E a sub-aplicação também poderia ter suas próprias sub-aplicações montadas e tudo funcionaria corretamente, porque o FastAPI lida com todos esses `root_path`s automaticamente.
Você aprenderá mais sobre o `root_path` e como usá-lo explicitamente na seção sobre [Atrás de um Proxy](behind-a-proxy.md).
================================================
FILE: docs/pt/docs/advanced/templates.md
================================================
# Templates { #templates }
Você pode usar qualquer template engine com o **FastAPI**.
Uma escolha comum é o Jinja2, o mesmo usado pelo Flask e outras ferramentas.
Existem utilitários para configurá-lo facilmente que você pode usar diretamente em sua aplicação **FastAPI** (fornecidos pelo Starlette).
## Instalar dependências { #install-dependencies }
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e instalar `jinja2`:
/// info | Informação
Há outros formatos e ferramentas para definir e instalar dependências de pacotes.
///
### Crie o código do **FastAPI** { #create-the-fastapi-code }
* Crie um diretório `app` e entre nele.
* Crie um arquivo vazio `__init__.py`.
* Crie um 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Agora, no mesmo diretório do projeto, crie um arquivo `Dockerfile` com:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Inicie a partir da imagem base oficial do Python.
2. Defina o diretório de trabalho atual para `/code`.
Esse é o diretório onde colocaremos o arquivo `requirements.txt` e o diretório `app`.
3. Copie o arquivo com os requisitos para o diretório `/code`.
Copie **somente** o arquivo com os requisitos primeiro, não o resto do código.
Como esse arquivo **não muda com frequência**, o Docker irá detectá-lo e usar o **cache** para esse passo, habilitando o cache para o próximo passo também.
4. Instale as dependências de pacote vindas do arquivo de requisitos.
A opção `--no-cache-dir` diz ao `pip` para não salvar os pacotes baixados localmente, pois isso só aconteceria se `pip` fosse executado novamente para instalar os mesmos pacotes, mas esse não é o caso quando trabalhamos com contêineres.
/// note | Nota
`--no-cache-dir` é apenas relacionado ao `pip`, não tem nada a ver com Docker ou contêineres.
///
A opção `--upgrade` diz ao `pip` para atualizar os pacotes se eles já estiverem instalados.
Por causa do passo anterior de copiar o arquivo, ele pode ser detectado pelo **cache do Docker**, esse passo também **usará o cache do Docker** quando disponível.
Usando o cache nesse passo irá **salvar** muito **tempo** quando você for construir a imagem repetidas vezes durante o desenvolvimento, ao invés de **baixar e instalar** todas as dependências **toda vez**.
5. Copie o diretório `./app` dentro do diretório `/code`.
Como isso tem todo o código contendo o que **muda com mais frequência**, o **cache do Docker** não será usado para esse passo ou para **qualquer passo seguinte** facilmente.
Então, é importante colocar isso **perto do final** do `Dockerfile`, para otimizar o tempo de construção da imagem do contêiner.
6. Defina o **comando** para usar `fastapi run`, que utiliza o Uvicorn por baixo dos panos.
`CMD` recebe uma lista de strings, cada uma dessas strings é o que você digitaria na linha de comando separado por espaços.
Esse comando será executado a partir do **diretório de trabalho atual**, o mesmo diretório `/code` que você definiu acima com `WORKDIR /code`.
/// tip | Dica
Revise o que cada linha faz clicando em cada bolha com o número no código. 👆
///
/// warning | Atenção
Certifique-se de **sempre** usar a **forma exec** da instrução `CMD`, como explicado abaixo.
///
#### Use `CMD` - Forma Exec { #use-cmd-exec-form }
A instrução [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) no Docker pode ser escrita de duas formas:
✅ Forma **Exec**:
```Dockerfile
# ✅ Faça assim
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Forma **Shell**:
```Dockerfile
# ⛔️ Não faça assim
CMD fastapi run app/main.py --port 80
```
Garanta que você sempre use a forma **exec** para assegurar que o FastAPI consiga encerrar graciosamente e que os [eventos de lifespan](../advanced/events.md) sejam disparados.
Você pode ler mais na [documentação do Docker sobre as formas shell e exec](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Isso pode ser bem perceptível ao usar `docker compose`. Veja esta seção de FAQ do Docker Compose para mais detalhes técnicos: [Por que meus serviços demoram 10 segundos para recriar ou parar?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Estrutura de diretórios { #directory-structure }
Agora você deve haver uma estrutura de diretório como:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### Por trás de um Proxy de Terminação TLS { #behind-a-tls-termination-proxy }
Se você está executando seu contêiner atrás de um Proxy de Terminação TLS (load balancer) como Nginx ou Traefik, adicione a opção `--proxy-headers`, isso fará com que o Uvicorn (pela CLI do FastAPI) confie nos cabeçalhos enviados por esse proxy, informando que o aplicativo está sendo executado atrás do HTTPS, etc.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Cache Docker { #docker-cache }
Existe um truque importante nesse `Dockerfile`, primeiro copiamos o **arquivo com as dependências sozinho**, não o resto do código. Deixe-me te contar o porquê disso.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker e outras ferramentas **constróem** essas imagens de contêiner **incrementalmente**, adicionando **uma camada em cima da outra**, começando do topo do `Dockerfile` e adicionando qualquer arquivo criado por cada uma das instruções do `Dockerfile`.
Docker e ferramentas similares também usam um **cache interno** ao construir a imagem, se um arquivo não mudou desde a última vez que a imagem do contêiner foi construída, então ele irá **reutilizar a mesma camada** criada na última vez, ao invés de copiar o arquivo novamente e criar uma nova camada do zero.
Somente evitar a cópia de arquivos não melhora muito as coisas, mas porque ele usou o cache para esse passo, ele pode **usar o cache para o próximo passo**. Por exemplo, ele pode usar o cache para a instrução que instala as dependências com:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
O arquivo com os requisitos de pacote **não muda com frequência**. Então, ao copiar apenas esse arquivo, o Docker será capaz de **usar o cache** para esse passo.
E então, o Docker será capaz de **usar o cache para o próximo passo** que baixa e instala essas dependências. E é aqui que **salvamos muito tempo**. ✨ ...e evitamos tédio esperando. 😪😆
Baixar e instalar as dependências do pacote **pode levar minutos**, mas usando o **cache** leva **segundos** no máximo.
E como você estaria construindo a imagem do contêiner novamente e novamente durante o desenvolvimento para verificar se suas alterações de código estão funcionando, há muito tempo acumulado que isso economizaria.
A partir daí, perto do final do `Dockerfile`, copiamos todo o código. Como isso é o que **muda com mais frequência**, colocamos perto do final, porque quase sempre, qualquer coisa depois desse passo não será capaz de usar o cache.
```Dockerfile
COPY ./app /code/app
```
### Construa a Imagem Docker { #build-the-docker-image }
Agora que todos os arquivos estão no lugar, vamos construir a imagem do contêiner.
* Vá para o diretório do projeto (onde está o seu `Dockerfile`, contendo o diretório `app`).
* Construa sua imagem FastAPI:
/// tip | Dica
Note o `.` no final, é equivalente a `./`, ele diz ao Docker o diretório a ser usado para construir a imagem do contêiner.
Nesse caso, é o mesmo diretório atual (`.`).
///
### Inicie o Contêiner Docker { #start-the-docker-container }
* Execute um contêiner baseado na sua imagem:
## Verifique { #check-it }
Você deve ser capaz de verificar isso no URL do seu contêiner Docker, por exemplo: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) ou [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (ou equivalente, usando seu host Docker).
Você verá algo como:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Documentação interativa da API { #interactive-api-docs }
Agora você pode ir para [http://192.168.99.100/docs](http://192.168.99.100/docs) ou [http://127.0.0.1/docs](http://127.0.0.1/docs) (ou equivalente, usando seu host Docker).
Você verá a documentação interativa automática da API (fornecida pelo [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Documentação alternativa da API { #alternative-api-docs }
E você também pode ir para [http://192.168.99.100/redoc](http://192.168.99.100/redoc) ou [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (ou equivalente, usando seu host Docker).
Você verá a documentação alternativa automática (fornecida pelo [ReDoc](https://github.com/Rebilly/ReDoc)):

## Construa uma Imagem Docker com um FastAPI de Arquivo Único { #build-a-docker-image-with-a-single-file-fastapi }
Se seu FastAPI for um único arquivo, por exemplo, `main.py` sem um diretório `./app`, sua estrutura de arquivos poderia ser assim:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Então você só teria que alterar os caminhos correspondentes para copiar o arquivo dentro do `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Copie o arquivo `main.py` para o diretório `/code` diretamente (sem nenhum diretório `./app`).
2. Use `fastapi run` para servir sua aplicação no arquivo único `main.py`.
Quando você passa o arquivo para `fastapi run` ele detecta automaticamente que é um arquivo único e não parte de um pacote e sabe como importá-lo e servir sua aplicação FastAPI. 😎
## Conceitos de Implantação { #deployment-concepts }
Vamos falar novamente sobre alguns dos mesmos [Conceitos de Implantação](concepts.md) em termos de contêineres.
Contêineres são principalmente uma ferramenta para simplificar o processo de **construção e implantação** de um aplicativo, mas eles não impõem uma abordagem particular para lidar com esses **conceitos de implantação** e existem várias estratégias possíveis.
A **boa notícia** é que com cada estratégia diferente há uma maneira de cobrir todos os conceitos de implantação. 🎉
Vamos revisar esses **conceitos de implantação** em termos de contêineres:
* HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (número de processos rodando)
* Memória
* Passos anteriores antes de começar
## HTTPS { #https }
Se nos concentrarmos apenas na **imagem do contêiner** para um aplicativo FastAPI (e posteriormente no **contêiner** em execução), o HTTPS normalmente seria tratado **externamente** por outra ferramenta.
Isso poderia ser outro contêiner, por exemplo, com [Traefik](https://traefik.io/), lidando com **HTTPS** e aquisição **automática** de **certificados**.
/// tip | Dica
Traefik tem integrações com Docker, Kubernetes e outros, portanto, é muito fácil configurar o HTTPS para seus contêineres com ele.
///
Alternativamente, o HTTPS poderia ser tratado por um provedor de nuvem como um de seus serviços (enquanto ainda executasse o aplicativo em um contêiner).
## Executando na inicialização e reinicializações { #running-on-startup-and-restarts }
Normalmente, outra ferramenta é responsável por **iniciar e executar** seu contêiner.
Ela poderia ser o **Docker** diretamente, **Docker Compose**, **Kubernetes**, um **serviço de nuvem**, etc.
Na maioria (ou em todos) os casos, há uma opção simples para habilitar a execução do contêiner na inicialização e habilitar reinicializações em falhas. Por exemplo, no Docker, é a opção de linha de comando `--restart`.
Sem usar contêineres, fazer aplicativos executarem na inicialização e com reinicializações pode ser trabalhoso e difícil. Mas quando **trabalhando com contêineres** em muitos casos essa funcionalidade é incluída por padrão. ✨
## Replicação - Número de Processos { #replication-number-of-processes }
Se você tiver um cluster de máquinas com **Kubernetes**, Docker Swarm Mode, Nomad ou outro sistema complexo semelhante para gerenciar contêineres distribuídos em várias máquinas, então provavelmente desejará **lidar com a replicação** no **nível do cluster** em vez de usar um **gerenciador de processos** (como Uvicorn com workers) em cada contêiner.
Um desses sistemas de gerenciamento de contêineres distribuídos como o Kubernetes normalmente tem alguma maneira integrada de lidar com a **replicação de contêineres** enquanto ainda oferece **balanceamento de carga** para as solicitações recebidas. Tudo no **nível do cluster**.
Nesses casos, você provavelmente desejará criar uma **imagem Docker do zero** como [explicado acima](#dockerfile), instalando suas dependências e executando **um único processo Uvicorn** em vez de usar múltiplos workers do Uvicorn.
### Balanceador de Carga { #load-balancer }
Quando usando contêineres, normalmente você terá algum componente **escutando na porta principal**. Poderia ser outro contêiner que também é um **Proxy de Terminação TLS** para lidar com **HTTPS** ou alguma ferramenta semelhante.
Como esse componente assumiria a **carga** de solicitações e distribuiria isso entre os workers de uma maneira (esperançosamente) **balanceada**, ele também é comumente chamado de **Balanceador de Carga**.
/// tip | Dica
O mesmo componente **Proxy de Terminação TLS** usado para HTTPS provavelmente também seria um **Balanceador de Carga**.
///
E quando trabalhar com contêineres, o mesmo sistema que você usa para iniciar e gerenciá-los já terá ferramentas internas para transmitir a **comunicação de rede** (por exemplo, solicitações HTTP) do **balanceador de carga** (que também pode ser um **Proxy de Terminação TLS**) para o(s) contêiner(es) com seu aplicativo.
### Um Balanceador de Carga - Múltiplos Contêineres de Workers { #one-load-balancer-multiple-worker-containers }
Quando trabalhando com **Kubernetes** ou sistemas similares de gerenciamento de contêiner distribuído, usar seus mecanismos de rede internos permite que o único **balanceador de carga** que está escutando na **porta principal** transmita a comunicação (solicitações) para possivelmente **múltiplos contêineres** executando seu aplicativo.
Cada um desses contêineres executando seu aplicativo normalmente teria **apenas um processo** (ex.: um processo Uvicorn executando seu aplicativo FastAPI). Todos seriam **contêineres idênticos**, executando a mesma coisa, mas cada um com seu próprio processo, memória, etc. Dessa forma, você aproveitaria a **paralelização** em **núcleos diferentes** da CPU, ou até mesmo em **máquinas diferentes**.
E o sistema de contêiner com o **balanceador de carga** iria **distribuir as solicitações** para cada um dos contêineres com seu aplicativo **em turnos**. Portanto, cada solicitação poderia ser tratada por um dos múltiplos **contêineres replicados** executando seu aplicativo.
E normalmente esse **balanceador de carga** seria capaz de lidar com solicitações que vão para *outros* aplicativos em seu cluster (por exemplo, para um domínio diferente, ou sob um prefixo de URL diferente), e transmitiria essa comunicação para os contêineres certos para *esse outro* aplicativo em execução em seu cluster.
### Um Processo por Contêiner { #one-process-per-container }
Nesse tipo de cenário, provavelmente você desejará ter **um único processo (Uvicorn) por contêiner**, pois já estaria lidando com a replicação no nível do cluster.
Então, nesse caso, você **não** desejará ter múltiplos workers no contêiner, por exemplo com a opção de linha de comando `--workers`. Você desejará ter apenas um **único processo Uvicorn** por contêiner (mas provavelmente vários contêineres).
Ter outro gerenciador de processos dentro do contêiner (como seria com múltiplos workers) só adicionaria **complexidade desnecessária** que você provavelmente já está cuidando com seu sistema de cluster.
### Contêineres com Múltiplos Processos e Casos Especiais { #containers-with-multiple-processes-and-special-cases }
Claro, existem **casos especiais** em que você pode querer ter **um contêiner** com vários **processos workers do Uvicorn** dentro.
Nesses casos, você pode usar a opção de linha de comando `--workers` para definir o número de workers que deseja executar:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Aqui usamos a opção de linha de comando `--workers` para definir o número de workers como 4.
Aqui estão alguns exemplos de quando isso pode fazer sentido:
#### Um Aplicativo Simples { #a-simple-app }
Você pode querer um gerenciador de processos no contêiner se seu aplicativo for **simples o suficiente** para rodar em um **único servidor**, não em um cluster.
#### Docker Compose { #docker-compose }
Você pode estar implantando em um **único servidor** (não em um cluster) com o **Docker Compose**, então você não teria uma maneira fácil de gerenciar a replicação de contêineres (com o Docker Compose) enquanto preserva a rede compartilhada e o **balanceamento de carga**.
Então você pode querer ter **um único contêiner** com um **gerenciador de processos** iniciando **vários processos workers** dentro.
---
O ponto principal é que **nenhum** desses são **regras escritas em pedra** que você deve seguir cegamente. Você pode usar essas ideias para **avaliar seu próprio caso de uso** e decidir qual é a melhor abordagem para seu sistema, verificando como gerenciar os conceitos de:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Passos anteriores antes de iniciar
## Memória { #memory }
Se você executar **um único processo por contêiner**, terá uma quantidade mais ou menos bem definida, estável e limitada de memória consumida por cada um desses contêineres (mais de um se eles forem replicados).
E então você pode definir esses mesmos limites e requisitos de memória em suas configurações para seu sistema de gerenciamento de contêineres (por exemplo, no **Kubernetes**). Dessa forma, ele poderá **replicar os contêineres** nas **máquinas disponíveis** levando em consideração a quantidade de memória necessária por eles e a quantidade disponível nas máquinas no cluster.
Se sua aplicação for **simples**, isso provavelmente **não será um problema**, e você pode não precisar especificar limites de memória rígidos. Mas se você estiver **usando muita memória** (por exemplo, com **modelos de aprendizado de máquina**), deve verificar quanta memória está consumindo e ajustar o **número de contêineres** que executa em **cada máquina** (e talvez adicionar mais máquinas ao seu cluster).
Se você executar **múltiplos processos por contêiner**, deve garantir que o número de processos iniciados não **consuma mais memória** do que o disponível.
## Passos anteriores antes de iniciar e contêineres { #previous-steps-before-starting-and-containers }
Se você estiver usando contêineres (por exemplo, Docker, Kubernetes), existem duas abordagens principais que você pode usar.
### Contêineres Múltiplos { #multiple-containers }
Se você tiver **múltiplos contêineres**, provavelmente cada um executando um **único processo** (por exemplo, em um cluster do **Kubernetes**), então provavelmente você gostaria de ter um **contêiner separado** fazendo o trabalho dos **passos anteriores** em um único contêiner, executando um único processo, **antes** de executar os contêineres workers replicados.
/// info | Informação
Se você estiver usando o Kubernetes, provavelmente será um [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Se no seu caso de uso não houver problema em executar esses passos anteriores **em paralelo várias vezes** (por exemplo, se você não estiver executando migrações de banco de dados, mas apenas verificando se o banco de dados está pronto), então você também pode colocá-los em cada contêiner logo antes de iniciar o processo principal.
### Contêiner Único { #single-container }
Se você tiver uma configuração simples, com um **único contêiner** que então inicia vários **processos workers** (ou também apenas um processo), então poderia executar esses passos anteriores no mesmo contêiner, logo antes de iniciar o processo com o aplicativo.
### Imagem Docker base { #base-docker-image }
Antes havia uma imagem oficial do FastAPI para Docker: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Mas agora ela está descontinuada. ⛔️
Você provavelmente **não** deve usar essa imagem base do Docker (ou qualquer outra semelhante).
Se você está usando **Kubernetes** (ou outros) e já está definindo a **replicação** no nível do cluster, com vários **contêineres**. Nesses casos, é melhor **construir uma imagem do zero** como descrito acima: [Construir uma Imagem Docker para FastAPI](#build-a-docker-image-for-fastapi).
E se você precisar ter múltiplos workers, você pode simplesmente usar a opção de linha de comando `--workers`.
/// note | Detalhes Técnicos
A imagem Docker foi criada quando o Uvicorn não suportava gerenciar e reiniciar workers mortos, então era necessário usar o Gunicorn com o Uvicorn, o que adicionava bastante complexidade, apenas para que o Gunicorn gerenciasse e reiniciasse os processos workers do Uvicorn.
Mas agora que o Uvicorn (e o comando `fastapi`) suportam o uso de `--workers`, não há razão para usar uma imagem base do Docker em vez de construir a sua própria (é praticamente a mesma quantidade de código 😅).
///
## Deploy da Imagem do Contêiner { #deploy-the-container-image }
Depois de ter uma imagem de contêiner (Docker), existem várias maneiras de implantá-la.
Por exemplo:
* Com **Docker Compose** em um único servidor
* Com um cluster **Kubernetes**
* Com um cluster Docker Swarm Mode
* Com outra ferramenta como o Nomad
* Com um serviço de nuvem que pega sua imagem de contêiner e a implanta
## Imagem Docker com `uv` { #docker-image-with-uv }
Se você está usando o [uv](https://github.com/astral-sh/uv) para instalar e gerenciar seu projeto, você pode seguir o [guia de Docker do uv](https://docs.astral.sh/uv/guides/integration/docker/).
## Recapitulando { #recap }
Usando sistemas de contêiner (por exemplo, com **Docker** e **Kubernetes**), torna-se bastante simples lidar com todos os **conceitos de implantação**:
* HTTPS
* Executando na inicialização
* Reinícios
* Replicação (o número de processos rodando)
* Memória
* Passos anteriores antes de iniciar
Na maioria dos casos, você provavelmente não desejará usar nenhuma imagem base e, em vez disso, **construir uma imagem de contêiner do zero** baseada na imagem oficial do Docker Python.
Tendo cuidado com a **ordem** das instruções no `Dockerfile` e o **cache do Docker**, você pode **minimizar os tempos de construção**, para maximizar sua produtividade (e evitar o tédio). 😎
================================================
FILE: docs/pt/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Você pode implantar sua aplicação FastAPI no [FastAPI Cloud](https://fastapicloud.com) com um **único comando**; entre na lista de espera, caso ainda não tenha feito isso. 🚀
## Login { #login }
Certifique-se de que você já tem uma conta no **FastAPI Cloud** (nós convidamos você a partir da lista de espera 😉).
Depois, faça login:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Implantar { #deploy }
Agora, implante sua aplicação, com **um único comando**:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
É isso! Agora você pode acessar sua aplicação nesse URL. ✨
## Sobre o FastAPI Cloud { #about-fastapi-cloud }
O **[FastAPI Cloud](https://fastapicloud.com)** é desenvolvido pelo mesmo autor e equipe por trás do **FastAPI**.
Ele simplifica o processo de **criar**, **implantar** e **acessar** uma API com esforço mínimo.
Traz a mesma **experiência do desenvolvedor** de criar aplicações com FastAPI para **implantá-las** na nuvem. 🎉
Ele também cuidará da maioria das coisas de que você precisaria ao implantar uma aplicação, como:
* HTTPS
* Replicação, com dimensionamento automático baseado em requests
* etc.
O FastAPI Cloud é o patrocinador principal e provedor de financiamento dos projetos open source do ecossistema *FastAPI and friends*. ✨
## Implantar em outros provedores de nuvem { #deploy-to-other-cloud-providers }
FastAPI é open source e baseado em padrões. Você pode implantar aplicações FastAPI em qualquer provedor de nuvem que escolher.
Siga os tutoriais do seu provedor de nuvem para implantar aplicações FastAPI com esse provedor. 🤓
## Implantar no seu próprio servidor { #deploy-your-own-server }
Também vou lhe ensinar, mais adiante neste guia de **Implantação**, todos os detalhes, para que você possa entender o que está acontecendo, o que precisa acontecer, ou como implantar aplicações FastAPI por conta própria, inclusive nos seus próprios servidores. 🤓
================================================
FILE: docs/pt/docs/deployment/https.md
================================================
# Sobre HTTPS { #about-https }
É fácil assumir que HTTPS é algo que é apenas "habilitado" ou não.
Mas é bem mais complexo do que isso.
/// tip | Dica
Se você está com pressa ou não se importa, continue com as seções seguintes para instruções passo a passo para configurar tudo com diferentes técnicas.
///
Para aprender o básico de HTTPS do ponto de vista do consumidor, verifique [https://howhttps.works/](https://howhttps.works/).
Agora, a partir de uma perspectiva do desenvolvedor, aqui estão algumas coisas para ter em mente ao pensar em HTTPS:
* Para HTTPS, o servidor precisa ter "certificados" gerados por um terceiro.
* Esses certificados são na verdade adquiridos de um terceiro, eles não são simplesmente "gerados".
* Certificados têm um tempo de vida.
* Eles expiram.
* E então eles precisam ser renovados, adquirindo-os novamente de um terceiro.
* A criptografia da conexão acontece no nível TCP.
* Essa é uma camada abaixo do HTTP.
* Portanto, o manuseio do certificado e da criptografia é feito antes do HTTP.
* O TCP não sabe sobre "domínios". Apenas sobre endereços IP.
* As informações sobre o domínio específico solicitado vão nos dados HTTP.
* Os certificados HTTPS “certificam” um determinado domínio, mas o protocolo e a encriptação acontecem ao nível do TCP, antes de sabermos de que domínio se trata.
* Por padrão, isso significa que você só pode ter um certificado HTTPS por endereço IP.
* Não importa o tamanho do seu servidor ou quão pequeno cada aplicativo que você tem nele possa ser.
* No entanto, existe uma solução para isso.
* Há uma extensão para o protocolo TLS (aquele que lida com a criptografia no nível TCP, antes do HTTP) chamada [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication).
* Esta extensão SNI permite que um único servidor (com um único endereço IP) tenha vários certificados HTTPS e atenda a vários domínios / aplicativos HTTPS.
* Para que isso funcione, um único componente (programa) em execução no servidor, ouvindo no endereço IP público, deve ter todos os certificados HTTPS no servidor.
* Depois de obter uma conexão segura, o protocolo de comunicação ainda é HTTP.
* Os conteúdos são criptografados, embora sejam enviados com o protocolo HTTP.
É uma prática comum ter um programa/servidor HTTP em execução no servidor (máquina, host, etc.) e gerenciar todas as partes HTTPS: recebendo as requisições HTTPS encriptadas, enviando as solicitações HTTP descriptografadas para o aplicativo HTTP real em execução no mesmo servidor (a aplicação FastAPI, neste caso), pegar a resposta HTTP do aplicativo, criptografá-la usando o certificado HTTPS apropriado e enviá-la de volta ao cliente usando HTTPS. Este servidor é frequentemente chamado de [Proxy de Terminação TLS](https://en.wikipedia.org/wiki/TLS_termination_proxy).
Algumas das opções que você pode usar como Proxy de Terminação TLS são:
* Traefik (que também pode gerenciar a renovação de certificados)
* Caddy (que também pode gerenciar a renovação de certificados)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Antes de Let's Encrypt, esses certificados HTTPS eram vendidos por terceiros confiáveis.
O processo de aquisição de um desses certificados costumava ser complicado, exigia bastante papelada e os certificados eram bastante caros.
Mas então o [Let's Encrypt](https://letsencrypt.org/) foi criado.
Ele é um projeto da Linux Foundation que fornece certificados HTTPS gratuitamente. De forma automatizada. Esses certificados usam toda a segurança criptográfica padrão e têm vida curta (cerca de 3 meses), então a segurança é, na verdade, melhor por causa do seu lifespan reduzido.
Os domínios são verificados com segurança e os certificados são gerados automaticamente. Isso também permite automatizar a renovação desses certificados.
A ideia é automatizar a aquisição e renovação desses certificados, para que você tenha HTTPS seguro, de graça e para sempre.
## HTTPS para Desenvolvedores { #https-for-developers }
Aqui está um exemplo de como uma API HTTPS poderia ser estruturada, passo a passo, com foco principal nas ideias relevantes para desenvolvedores.
### Nome do domínio { #domain-name }
A etapa inicial provavelmente seria adquirir algum nome de domínio. Então, você iria configurá-lo em um servidor DNS (possivelmente no mesmo provedor em nuvem).
Você provavelmente usaria um servidor em nuvem (máquina virtual) ou algo parecido, e ele teria um fixo Endereço IP público.
No(s) servidor(es) DNS, você configuraria um registro (um `A record`) para apontar seu domínio para o endereço IP público do seu servidor.
Você provavelmente fará isso apenas uma vez, na primeira vez em que tudo estiver sendo configurado.
/// tip | Dica
Essa parte do Nome do Domínio se dá muito antes do HTTPS, mas como tudo depende do domínio e endereço IP público, vale a pena mencioná-la aqui.
///
### DNS { #dns }
Agora vamos focar em todas as partes que realmente fazem parte do HTTPS.
Primeiro, o navegador iria verificar com os servidores DNS qual o IP do domínio, nesse caso, `someapp.example.com`.
Os servidores DNS iriam informar o navegador para utilizar algum endereço IP específico. Esse seria o endereço IP público em uso no seu servidor, que você configurou nos servidores DNS.
### Início do Handshake TLS { #tls-handshake-start }
O navegador então irá comunicar-se com esse endereço IP na porta 443 (a porta HTTPS).
A primeira parte dessa comunicação é apenas para estabelecer a conexão entre o cliente e o servidor e para decidir as chaves criptográficas a serem utilizadas, etc.
Esse interação entre o cliente e o servidor para estabelecer uma conexão TLS é chamada de Handshake TLS.
### TLS com a Extensão SNI { #tls-with-sni-extension }
Apenas um processo no servidor pode se conectar a uma porta em um endereço IP. Poderiam existir outros processos conectados em outras portas desse mesmo endereço IP, mas apenas um para cada combinação de endereço IP e porta.
TLS (HTTPS) usa a porta `443` por padrão. Então essa é a porta que precisamos.
Como apenas um único processo pode se comunicar com essa porta, o processo que faria isso seria o Proxy de Terminação TLS.
O Proxy de Terminação TLS teria acesso a um ou mais certificados TLS (certificados HTTPS).
Utilizando a extensão SNI discutida acima, o Proxy de Terminação TLS iria checar qual dos certificados TLS (HTTPS) disponíveis deve ser usado para essa conexão, utilizando o que corresponda ao domínio esperado pelo cliente.
Nesse caso, ele usaria o certificado para `someapp.example.com`.
O cliente já confia na entidade que gerou o certificado TLS (nesse caso, o Let's Encrypt, mas veremos sobre isso mais tarde), então ele pode verificar que o certificado é válido.
Então, utilizando o certificado, o cliente e o Proxy de Terminação TLS decidem como encriptar o resto da comunicação TCP. Isso completa a parte do Handshake TLS.
Após isso, o cliente e o servidor possuem uma conexão TCP encriptada, que é provida pelo TLS. E então eles podem usar essa conexão para começar a comunicação HTTP propriamente dita.
E isso resume o que é HTTPS, apenas HTTP simples dentro de uma conexão TLS segura em vez de uma conexão TCP pura (não encriptada).
/// tip | Dica
Percebe que a encriptação da comunicação acontece no nível do TCP, não no nível do HTTP.
///
### Solicitação HTTPS { #https-request }
Agora que o cliente e servidor (especialmente o navegador e o Proxy de Terminação TLS) possuem uma conexão TCP encriptada, eles podem iniciar a comunicação HTTP.
Então, o cliente envia uma solicitação HTTPS. Que é apenas uma solicitação HTTP sobre uma conexão TLS encriptada.
### Desencripte a Solicitação { #decrypt-the-request }
O Proxy de Terminação TLS então usaria a encriptação combinada para desencriptar a solicitação, e transmitiria a solicitação básica (desencriptada) para o processo executando a aplicação (por exemplo, um processo com Uvicorn executando a aplicação FastAPI).
### Resposta HTTP { #http-response }
A aplicação processaria a solicitação e retornaria uma resposta HTTP básica (não encriptada) para o Proxy de Terminação TLS.
### Resposta HTTPS { #https-response }
O Proxy de Terminação TLS iria encriptar a resposta utilizando a criptografia combinada anteriormente (que foi definida com o certificado para `someapp.example.com`), e devolveria para o navegador.
No próximo passo, o navegador verifica que a resposta é válida e encriptada com a chave criptográfica correta, etc. E depois desencripta a resposta e a processa.
O cliente (navegador) saberá que a resposta vem do servidor correto por que ela usa a criptografia que foi combinada entre eles usando o certificado HTTPS anterior.
### Múltiplas Aplicações { #multiple-applications }
Podem existir múltiplas aplicações em execução no mesmo servidor (ou servidores), por exemplo: outras APIs ou um banco de dados.
Apenas um processo pode estar vinculado a um IP e porta (o Proxy de Terminação TLS, por exemplo), mas outras aplicações/processos também podem estar em execução no(s) servidor(es), desde que não tentem usar a mesma combinação de IP público e porta.
Dessa forma, o Proxy de Terminação TLS pode gerenciar o HTTPS e os certificados de múltiplos domínios, para múltiplas aplicações, e então transmitir as requisições para a aplicação correta em cada caso.
### Renovação de Certificados { #certificate-renewal }
Em algum momento futuro, cada certificado irá expirar (aproximadamente 3 meses após a aquisição).
E então, haverá outro programa (em alguns casos pode ser o próprio Proxy de Terminação TLS) que irá interagir com o Let's Encrypt e renovar o(s) certificado(s).
Os certificados TLS são associados com um nome de domínio, e não a um endereço IP.
Então para renovar os certificados, o programa de renovação precisa provar para a autoridade (Let's Encrypt) que ele realmente "possui" e controla esse domínio.
Para fazer isso, e acomodar as necessidades de diferentes aplicações, existem diferentes opções para esse programa. Algumas escolhas populares são:
* Modificar alguns registros DNS
* Para isso, o programa de renovação precisa ter suporte às APIs do provedor DNS, então, dependendo do provedor DNS que você utilize, isso pode ou não ser uma opção viável.
* Executar como um servidor (ao menos durante o processo de aquisição do certificado) no endereço IP público associado com o domínio.
* Como dito anteriormente, apenas um processo pode estar ligado a uma porta e IP específicos.
* Essa é uma dos motivos que fazem utilizar o mesmo Proxy de Terminação TLS para gerenciar a renovação de certificados ser tão útil.
* Caso contrário, você pode ter que parar a execução do Proxy de Terminação TLS momentaneamente, inicializar o programa de renovação para adquirir os certificados, depois configurá-los com o Proxy de Terminação TLS, e então reiniciar o Proxy de Terminação TLS. Isso não é o ideal, já que sua(s) aplicação(ões) não vão estar disponíveis enquanto o Proxy de Terminação TLS estiver desligado.
Todo esse processo de renovação, enquanto o aplicativo ainda funciona, é uma das principais razões para preferir um sistema separado para gerenciar HTTPS com um Proxy de Terminação TLS em vez de usar os certificados TLS no servidor da aplicação diretamente (e.g. com o Uvicorn).
## Cabeçalhos encaminhados por Proxy { #proxy-forwarded-headers }
Ao usar um proxy para lidar com HTTPS, seu servidor de aplicação (por exemplo, Uvicorn via FastAPI CLI) não sabe nada sobre o processo de HTTPS; ele se comunica com HTTP simples com o Proxy de Terminação TLS.
Esse proxy normalmente define alguns cabeçalhos HTTP dinamicamente antes de transmitir a requisição para o servidor de aplicação, para informar ao servidor de aplicação que a requisição está sendo encaminhada pelo proxy.
/// note | Detalhes Técnicos
Os cabeçalhos do proxy são:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
No entanto, como o servidor de aplicação não sabe que está atrás de um proxy confiável, por padrão ele não confiaria nesses cabeçalhos.
Mas você pode configurar o servidor de aplicação para confiar nos cabeçalhos encaminhados enviados pelo proxy. Se você estiver usando o FastAPI CLI, pode usar a opção de CLI `--forwarded-allow-ips` para dizer de quais IPs ele deve confiar nesses cabeçalhos encaminhados.
Por exemplo, se o servidor de aplicação só estiver recebendo comunicação do proxy confiável, você pode defini-lo como `--forwarded-allow-ips="*"` para fazê-lo confiar em todos os IPs de entrada, já que ele só receberá requisições de seja lá qual for o IP usado pelo proxy.
Dessa forma, a aplicação seria capaz de saber qual é sua própria URL pública, se está usando HTTPS, o domínio, etc.
Isso seria útil, por exemplo, para lidar corretamente com redirecionamentos.
/// tip | Dica
Você pode saber mais sobre isso na documentação em [Atrás de um Proxy - Habilitar cabeçalhos encaminhados pelo proxy](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Recapitulando { #recap }
Possuir HTTPS habilitado na sua aplicação é bastante importante, e até crítico na maioria dos casos. A maior parte do esforço que você tem que colocar sobre o HTTPS como desenvolvedor está em entender esses conceitos e como eles funcionam.
Mas uma vez que você saiba o básico de HTTPS para desenvolvedores, você pode combinar e configurar diferentes ferramentas facilmente para gerenciar tudo de uma forma simples.
Em alguns dos próximos capítulos, eu mostrarei para você vários exemplos concretos de como configurar o HTTPS para aplicações FastAPI. 🔒
================================================
FILE: docs/pt/docs/deployment/index.md
================================================
# Implantação { #deployment }
Implantar uma aplicação **FastAPI** é relativamente fácil.
## O que significa Implantação { #what-does-deployment-mean }
**Implantar** uma aplicação significa executar as etapas necessárias para torná-la **disponível para os usuários**.
Para uma **web API**, isso normalmente envolve colocá-la em uma **máquina remota**, com um **programa de servidor** que ofereça bom desempenho, estabilidade, etc., de modo que seus **usuários** possam **acessar** a aplicação com eficiência e sem interrupções ou problemas.
Isso contrasta com as fases de **desenvolvimento**, em que você está constantemente alterando o código, quebrando e consertando, parando e reiniciando o servidor de desenvolvimento, etc.
## Estratégias de Implantação { #deployment-strategies }
Há várias maneiras de fazer isso, dependendo do seu caso de uso específico e das ferramentas que você utiliza.
Você pode **implantar um servidor** por conta própria usando uma combinação de ferramentas, pode usar um **serviço em nuvem** que faça parte do trabalho por você, entre outras opções.
Por exemplo, nós, a equipe por trás do FastAPI, criamos [**FastAPI Cloud**](https://fastapicloud.com), para tornar a implantação de aplicações FastAPI na nuvem o mais simples possível, com a mesma experiência de desenvolvimento de trabalhar com o FastAPI.
Vou mostrar alguns dos principais conceitos que você provavelmente deve ter em mente ao implantar uma aplicação **FastAPI** (embora a maior parte se aplique a qualquer outro tipo de aplicação web).
Você verá mais detalhes para ter em mente e algumas das técnicas para fazer isso nas próximas seções. ✨
================================================
FILE: docs/pt/docs/deployment/manually.md
================================================
# Execute um Servidor Manualmente { #run-a-server-manually }
## Utilize o comando `fastapi run` { #use-the-fastapi-run-command }
Em resumo, utilize o comando `fastapi run` para inicializar sua aplicação FastAPI:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Isto deve funcionar para a maioria dos casos. 😎
Você pode utilizar esse comando, por exemplo, para iniciar sua aplicação **FastAPI** em um contêiner, em um servidor, etc.
## Servidores ASGI { #asgi-servers }
Vamos nos aprofundar um pouco mais em detalhes.
FastAPI utiliza um padrão para construir frameworks e servidores web em Python chamado ASGI. FastAPI é um framework web ASGI.
A principal coisa que você precisa para executar uma aplicação **FastAPI** (ou qualquer outra aplicação ASGI) em uma máquina de servidor remoto é um programa de servidor ASGI como o **Uvicorn**, que é o que vem por padrão no comando `fastapi`.
Existem diversas alternativas, incluindo:
* [Uvicorn](https://www.uvicorn.dev/): um servidor ASGI de alta performance.
* [Hypercorn](https://hypercorn.readthedocs.io/): um servidor ASGI compatível com HTTP/2, Trio e outros recursos.
* [Daphne](https://github.com/django/daphne): servidor ASGI construído para Django Channels.
* [Granian](https://github.com/emmett-framework/granian): um servidor HTTP Rust para aplicações Python.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit é um runtime de aplicação web leve e versátil.
## Máquina Servidora e Programa Servidor { #server-machine-and-server-program }
Existe um pequeno detalhe sobre estes nomes para se manter em mente. 💡
A palavra "**servidor**" é comumente usada para se referir tanto ao computador remoto/nuvem (a máquina física ou virtual) quanto ao programa que está sendo executado nessa máquina (por exemplo, Uvicorn).
Apenas tenha em mente que quando você ler "servidor" em geral, isso pode se referir a uma dessas duas coisas.
Quando se refere à máquina remota, é comum chamá-la de **servidor**, mas também de **máquina**, **VM** (máquina virtual), **nó**. Todos esses termos se referem a algum tipo de máquina remota, normalmente executando Linux, onde você executa programas.
## Instale o Programa Servidor { #install-the-server-program }
Quando você instala o FastAPI, ele vem com um servidor de produção, o Uvicorn, e você pode iniciá-lo com o comando `fastapi run`.
Mas você também pode instalar um servidor ASGI manualmente.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e, em seguida, você pode instalar a aplicação do servidor.
Por exemplo, para instalar o Uvicorn:
Um processo semelhante se aplicaria a qualquer outro programa de servidor ASGI.
/// tip | Dica
Adicionando o `standard`, o Uvicorn instalará e usará algumas dependências extras recomendadas.
Isso inclui o `uvloop`, a substituição de alto desempenho para `asyncio`, que fornece um grande aumento de desempenho de concorrência.
Quando você instala o FastAPI com algo como `pip install "fastapi[standard]"`, você já obtém `uvicorn[standard]` também.
///
## Execute o Programa Servidor { #run-the-server-program }
Se você instalou um servidor ASGI manualmente, normalmente precisará passar uma string de importação em um formato especial para que ele importe sua aplicação FastAPI:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Nota
O comando `uvicorn main:app` refere-se a:
* `main`: o arquivo `main.py` (o "módulo" Python).
* `app`: o objeto criado dentro de `main.py` com a linha `app = FastAPI()`.
É equivalente a:
```Python
from main import app
```
///
Cada programa de servidor ASGI alternativo teria um comando semelhante, você pode ler mais na documentação respectiva.
/// warning | Atenção
Uvicorn e outros servidores suportam a opção `--reload` que é útil durante o desenvolvimento.
A opção `--reload` consome muito mais recursos, é mais instável, etc.
Ela ajuda muito durante o **desenvolvimento**, mas você **não deve** usá-la em **produção**.
///
## Conceitos de Implantação { #deployment-concepts }
Esses exemplos executam o programa do servidor (por exemplo, Uvicorn), iniciando **um único processo**, ouvindo em todos os IPs (`0.0.0.0`) em uma porta predefinida (por exemplo, `80`).
Esta é a ideia básica. Mas você provavelmente vai querer cuidar de algumas coisas adicionais, como:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* Replicação (o número de processos em execução)
* Memória
* Passos anteriores antes de começar
Vou te contar mais sobre cada um desses conceitos, como pensar sobre eles e alguns exemplos concretos com estratégias para lidar com eles nos próximos capítulos. 🚀
================================================
FILE: docs/pt/docs/deployment/server-workers.md
================================================
# Trabalhadores do Servidor - Uvicorn com Trabalhadores { #server-workers-uvicorn-with-workers }
Vamos rever os conceitos de implantação anteriores:
* Segurança - HTTPS
* Executando na inicialização
* Reinicializações
* **Replicação (o número de processos em execução)**
* Memória
* Etapas anteriores antes de iniciar
Até este ponto, com todos os tutoriais nos documentos, você provavelmente estava executando um **programa de servidor**, por exemplo, usando o comando `fastapi`, que executa o Uvicorn, executando um **único processo**.
Ao implantar aplicativos, você provavelmente desejará ter alguma **replicação de processos** para aproveitar **vários núcleos** e poder lidar com mais solicitações.
Como você viu no capítulo anterior sobre [Conceitos de implantação](concepts.md), há várias estratégias que você pode usar.
Aqui mostrarei como usar o **Uvicorn** com **processos de trabalho** usando o comando `fastapi` ou o comando `uvicorn` diretamente.
/// info | Informação
Se você estiver usando contêineres, por exemplo com Docker ou Kubernetes, falarei mais sobre isso no próximo capítulo: [FastAPI em contêineres - Docker](docker.md).
Em particular, ao executar no **Kubernetes** você provavelmente **não** vai querer usar vários trabalhadores e, em vez disso, executar **um único processo Uvicorn por contêiner**, mas falarei sobre isso mais adiante neste capítulo.
///
## Vários trabalhadores { #multiple-workers }
Você pode iniciar vários trabalhadores com a opção de linha de comando `--workers`:
//// tab | `fastapi`
Se você usar o comando `fastapi`:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Se você preferir usar o comando `uvicorn` diretamente:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
A única opção nova aqui é `--workers` informando ao Uvicorn para iniciar 4 processos de trabalho.
Você também pode ver que ele mostra o **PID** de cada processo, `27365` para o processo pai (este é o **gerenciador de processos**) e um para cada processo de trabalho: `27368`, `27369`, `27370` e `27367`.
## Conceitos de Implantação { #deployment-concepts }
Aqui você viu como usar vários **trabalhadores** para **paralelizar** a execução do aplicativo, aproveitar **vários núcleos** na CPU e conseguir atender **mais solicitações**.
Da lista de conceitos de implantação acima, o uso de trabalhadores ajudaria principalmente com a parte da **replicação** e um pouco com as **reinicializações**, mas você ainda precisa cuidar dos outros:
* **Segurança - HTTPS**
* **Executando na inicialização**
* ***Reinicializações***
* Replicação (o número de processos em execução)
* **Memória**
* **Etapas anteriores antes de iniciar**
## Contêineres e Docker { #containers-and-docker }
No próximo capítulo sobre [FastAPI em contêineres - Docker](docker.md), explicarei algumas estratégias que você pode usar para lidar com os outros **conceitos de implantação**.
Vou mostrar como **construir sua própria imagem do zero** para executar um único processo Uvicorn. É um processo simples e provavelmente é o que você gostaria de fazer ao usar um sistema de gerenciamento de contêineres distribuídos como o **Kubernetes**.
## Recapitular { #recap }
Você pode usar vários processos de trabalho com a opção CLI `--workers` com os comandos `fastapi` ou `uvicorn` para aproveitar as vantagens de **CPUs multi-core** e executar **vários processos em paralelo**.
Você pode usar essas ferramentas e ideias se estiver configurando **seu próprio sistema de implantação** enquanto cuida dos outros conceitos de implantação.
Confira o próximo capítulo para aprender sobre **FastAPI** com contêineres (por exemplo, Docker e Kubernetes). Você verá que essas ferramentas têm maneiras simples de resolver os outros **conceitos de implantação** também. ✨
================================================
FILE: docs/pt/docs/deployment/versions.md
================================================
# Sobre as versões do FastAPI { #about-fastapi-versions }
**FastAPI** já está sendo usado em produção em muitas aplicações e sistemas. E a cobertura de testes é mantida em 100%. Mas seu desenvolvimento ainda está avançando rapidamente.
Novas funcionalidades são adicionadas com frequência, bugs são corrigidos regularmente e o código continua melhorando continuamente.
É por isso que as versões atuais ainda são `0.x.x`, isso reflete que cada versão pode potencialmente ter mudanças significativas. Isso segue as convenções de [Versionamento Semântico](https://semver.org/).
Você pode criar aplicações de produção com **FastAPI** agora mesmo (e provavelmente já vem fazendo isso há algum tempo), apenas certifique-se de usar uma versão que funcione corretamente com o resto do seu código.
## Fixe a sua versão de `fastapi` { #pin-your-fastapi-version }
A primeira coisa que você deve fazer é "fixar" a versão do **FastAPI** que você está utilizando na versão mais recente específica que você sabe que funciona corretamente para a sua aplicação.
Por exemplo, suponha que você esteja usando a versão `0.112.0` em sua aplicação.
Se você usa um arquivo `requirements.txt`, você poderia especificar a versão com:
```txt
fastapi[standard]==0.112.0
```
isso significaria que você usaria exatamente a versão `0.112.0`.
Ou você também poderia fixá-la com:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
isso significaria que você usaria as versões `0.112.0` ou superiores, mas menores que `0.113.0`, por exemplo, a versão `0.112.2` ainda seria aceita.
Se você usa qualquer outra ferramenta para gerenciar suas instalações, como `uv`, Poetry, Pipenv ou outras, todas elas têm uma forma de definir versões específicas para seus pacotes.
## Versões disponíveis { #available-versions }
Você pode ver as versões disponíveis (por exemplo, para verificar qual é a mais recente) nas [Release Notes](../release-notes.md).
## Sobre versões { #about-versions }
Seguindo as convenções de Versionamento Semântico, qualquer versão abaixo de `1.0.0` pode potencialmente adicionar mudanças significativas.
FastAPI também segue a convenção de que qualquer alteração de versão "PATCH" é para correções de bugs e mudanças que não quebram compatibilidade.
/// tip | Dica
O "PATCH" é o último número, por exemplo, em `0.2.3`, a versão PATCH é `3`.
///
Logo, você deveria conseguir fixar a versão, como:
```txt
fastapi>=0.45.0,<0.46.0
```
Mudanças significativas e novas funcionalidades são adicionadas em versões "MINOR".
/// tip | Dica
O "MINOR" é o número do meio, por exemplo, em `0.2.3`, a versão MINOR é `2`.
///
## Atualizando as versões do FastAPI { #upgrading-the-fastapi-versions }
Você deveria adicionar testes para a sua aplicação.
Com **FastAPI** isso é muito fácil (graças ao Starlette), veja a documentação: [Testes](../tutorial/testing.md)
Depois que você tiver testes, você pode atualizar a sua versão do **FastAPI** para uma mais recente e se certificar de que todo o seu código está funcionando corretamente executando seus testes.
Se tudo estiver funcionando, ou após você realizar as alterações necessárias e todos os testes estiverem passando, então você pode fixar sua versão de `fastapi` para essa versão mais recente.
## Sobre Starlette { #about-starlette }
Não é recomendado fixar a versão de `starlette`.
Versões diferentes de **FastAPI** utilizarão uma versão específica e mais recente de Starlette.
Então, você pode deixar **FastAPI** usar a versão correta do Starlette.
## Sobre Pydantic { #about-pydantic }
Pydantic inclui os testes para **FastAPI** em seus próprios testes, então novas versões do Pydantic (acima de `1.0.0`) são sempre compatíveis com FastAPI.
Você pode fixar o Pydantic em qualquer versão acima de `1.0.0` que funcione para você.
Por exemplo:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/pt/docs/editor-support.md
================================================
# Suporte a Editores { #editor-support }
A [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) oficial melhora seu fluxo de trabalho de desenvolvimento com descoberta e navegação de *operação de rota*, além de implantação no FastAPI Cloud e transmissão ao vivo de logs.
Para mais detalhes sobre a extensão, consulte o README no [repositório do GitHub](https://github.com/fastapi/fastapi-vscode).
## Configuração e Instalação { #setup-and-installation }
A **FastAPI Extension** está disponível para [VS Code](https://code.visualstudio.com/) e [Cursor](https://www.cursor.com/). Pode ser instalada diretamente pelo painel de Extensões de cada editor, pesquisando por "FastAPI" e selecionando a extensão publicada por **FastAPI Labs**. A extensão também funciona em editores no navegador, como [vscode.dev](https://vscode.dev) e [github.dev](https://github.dev).
### Descoberta da Aplicação { #application-discovery }
Por padrão, a extensão descobre automaticamente aplicações FastAPI no seu workspace procurando por arquivos que instanciam `FastAPI()`. Se a detecção automática não funcionar para a estrutura do seu projeto, você pode especificar um ponto de entrada via `[tool.fastapi]` em `pyproject.toml` ou pela configuração `fastapi.entryPoint` do VS Code usando notação de módulo (por exemplo, `myapp.main:app`).
## Funcionalidades { #features }
- **Explorador de Operações de Rota** - Uma visualização em árvore na barra lateral de todas as *operações de rota* da sua aplicação. Clique para ir diretamente a qualquer definição de rota ou de router.
- **Pesquisa de Rotas** - Pesquise por path, método ou nome com Ctrl + Shift + E (no macOS: Cmd + Shift + E).
- **Navegação com CodeLens** - Links clicáveis acima das chamadas do cliente de testes (por exemplo, `client.get('/items')`) que levam à *operação de rota* correspondente, facilitando a navegação entre testes e implementação.
- **Implantar no FastAPI Cloud** - Implantação com um clique da sua aplicação no [FastAPI Cloud](https://fastapicloud.com/).
- **Transmitir logs da aplicação** - Transmissão em tempo real dos logs da aplicação implantada no FastAPI Cloud, com filtragem por nível e busca de texto.
Se quiser se familiarizar com as funcionalidades da extensão, você pode abrir o walkthrough da extensão acessando a Paleta de Comandos (Ctrl + Shift + P ou no macOS: Cmd + Shift + P), selecionando "Welcome: Open walkthrough..." e, em seguida, escolhendo o walkthrough "Get started with FastAPI".
================================================
FILE: docs/pt/docs/environment-variables.md
================================================
# Variáveis de Ambiente { #environment-variables }
/// tip | Dica
Se você já sabe o que são "variáveis de ambiente" e como usá-las, pode pular esta seção.
///
Uma variável de ambiente (também conhecida como "**env var**") é uma variável que existe **fora** do código Python, no **sistema operacional**, e pode ser lida pelo seu código Python (ou por outros programas também).
Variáveis de ambiente podem ser úteis para lidar com **configurações** do aplicativo, como parte da **instalação** do Python, etc.
## Criar e Usar Variáveis de Ambiente { #create-and-use-env-vars }
Você pode **criar** e usar variáveis de ambiente no **shell (terminal)**, sem precisar do Python:
//// tab | Linux, macOS, Windows Bash
```console
// Você pode criar uma variável de ambiente MY_NAME com
$ export MY_NAME="Wade Wilson"
// Então você pode usá-la com outros programas, como
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Criar uma variável de ambiente MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Usá-la com outros programas, como
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Ler Variáveis de Ambiente no Python { #read-env-vars-in-python }
Você também pode criar variáveis de ambiente **fora** do Python, no terminal (ou com qualquer outro método) e depois **lê-las no Python**.
Por exemplo, você poderia ter um arquivo `main.py` com:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Dica
O segundo argumento para [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) é o valor padrão a ser retornado.
Se não for fornecido, é `None` por padrão, Aqui fornecemos `"World"` como o valor padrão a ser usado.
///
Então você poderia chamar esse programa Python:
//// tab | Linux, macOS, Windows Bash
```console
// Aqui ainda não definimos a variável de ambiente
$ python main.py
// Como não definimos a variável de ambiente, obtemos o valor padrão
Hello World from Python
// Mas se criarmos uma variável de ambiente primeiro
$ export MY_NAME="Wade Wilson"
// E então chamar o programa novamente
$ python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Aqui ainda não definimos a variável de ambiente
$ python main.py
// Como não definimos a variável de ambiente, obtemos o valor padrão
Hello World from Python
// Mas se criarmos uma variável de ambiente primeiro
$ $Env:MY_NAME = "Wade Wilson"
// E então chamar o programa novamente
$ python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
```
////
Como as variáveis de ambiente podem ser definidas fora do código, mas podem ser lidas pelo código e não precisam ser armazenadas (com versão no `git`) com o restante dos arquivos, é comum usá-las para configurações ou **definições**.
Você também pode criar uma variável de ambiente apenas para uma **invocação específica do programa**, que só está disponível para aquele programa e apenas pela duração dele.
Para fazer isso, crie-a na mesma linha, antes do próprio programa:
```console
// Criar uma variável de ambiente MY_NAME para esta chamada de programa
$ MY_NAME="Wade Wilson" python main.py
// Agora ele pode ler a variável de ambiente
Hello Wade Wilson from Python
// A variável de ambiente não existe mais depois
$ python main.py
Hello World from Python
```
/// tip | Dica
Você pode ler mais sobre isso em [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Tipos e Validação { #types-and-validation }
Essas variáveis de ambiente só podem lidar com **strings de texto**, pois são externas ao Python e precisam ser compatíveis com outros programas e com o resto do sistema (e até mesmo com diferentes sistemas operacionais, como Linux, Windows, macOS).
Isso significa que **qualquer valor** lido em Python de uma variável de ambiente **será uma `str`**, e qualquer conversão para um tipo diferente ou qualquer validação precisa ser feita no código.
Você aprenderá mais sobre como usar variáveis de ambiente para lidar com **configurações do aplicativo** no [Guia do Usuário Avançado - Configurações e Variáveis de Ambiente](./advanced/settings.md).
## Variável de Ambiente `PATH` { #path-environment-variable }
Existe uma variável de ambiente **especial** chamada **`PATH`** que é usada pelos sistemas operacionais (Linux, macOS, Windows) para encontrar programas para executar.
O valor da variável `PATH` é uma longa string composta por diretórios separados por dois pontos `:` no Linux e macOS, e por ponto e vírgula `;` no Windows.
Por exemplo, a variável de ambiente `PATH` poderia ter esta aparência:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Isso significa que o sistema deve procurar programas nos diretórios:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Isso significa que o sistema deve procurar programas nos diretórios:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Quando você digita um **comando** no terminal, o sistema operacional **procura** o programa em **cada um dos diretórios** listados na variável de ambiente `PATH`.
Por exemplo, quando você digita `python` no terminal, o sistema operacional procura um programa chamado `python` no **primeiro diretório** dessa lista.
Se ele o encontrar, então ele o **usará**. Caso contrário, ele continua procurando nos **outros diretórios**.
### Instalando o Python e Atualizando o `PATH` { #installing-python-and-updating-the-path }
Durante a instalação do Python, você pode ser questionado sobre a atualização da variável de ambiente `PATH`.
//// tab | Linux, macOS
Vamos supor que você instale o Python e ele fique em um diretório `/opt/custompython/bin`.
Se você concordar em atualizar a variável de ambiente `PATH`, o instalador adicionará `/opt/custompython/bin` para a variável de ambiente `PATH`.
Poderia parecer assim:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Dessa forma, ao digitar `python` no terminal, o sistema encontrará o programa Python em `/opt/custompython/bin` (último diretório) e o utilizará.
////
//// tab | Windows
Digamos que você instala o Python e ele acaba em um diretório `C:\opt\custompython\bin`.
Se você disser sim para atualizar a variável de ambiente `PATH`, o instalador adicionará `C:\opt\custompython\bin` à variável de ambiente `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Dessa forma, quando você digitar `python` no terminal, o sistema encontrará o programa Python em `C:\opt\custompython\bin` (o último diretório) e o utilizará.
////
Então, se você digitar:
```console
$ python
```
//// tab | Linux, macOS
O sistema **encontrará** o programa `python` em `/opt/custompython/bin` e o executará.
Seria aproximadamente equivalente a digitar:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
O sistema **encontrará** o programa `python` em `C:\opt\custompython\bin\python` e o executará.
Seria aproximadamente equivalente a digitar:
```console
$ C:\opt\custompython\bin\python
```
////
Essas informações serão úteis ao aprender sobre [Ambientes Virtuais](virtual-environments.md).
## Conclusão { #conclusion }
Com isso, você deve ter uma compreensão básica do que são **variáveis de ambiente** e como usá-las em Python.
Você também pode ler mais sobre elas na [Wikipedia para Variáveis de Ambiente](https://en.wikipedia.org/wiki/Environment_variable).
Em muitos casos, não é muito óbvio como as variáveis de ambiente seriam úteis e aplicáveis imediatamente. Mas elas continuam aparecendo em muitos cenários diferentes quando você está desenvolvendo, então é bom saber sobre elas.
Por exemplo, você precisará dessas informações na próxima seção, sobre [Ambientes Virtuais](virtual-environments.md).
================================================
FILE: docs/pt/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** é um programa de linha de comando que você pode usar para servir sua aplicação FastAPI, gerenciar seu projeto FastAPI e muito mais.
Quando você instala o FastAPI (por exemplo, com `pip install "fastapi[standard]"`), ele vem com um programa de linha de comando que você pode executar no terminal.
Para executar sua aplicação FastAPI durante o desenvolvimento, você pode usar o comando `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Dica
Em produção, você usaria `fastapi run` em vez de `fastapi dev`. 🚀
///
Internamente, o **FastAPI CLI** usa o [Uvicorn](https://www.uvicorn.dev), um servidor ASGI de alta performance e pronto para produção. 😎
O CLI `fastapi` tentará detectar automaticamente a aplicação FastAPI a ser executada, assumindo que seja um objeto chamado `app` em um arquivo `main.py` (ou algumas outras variantes).
Mas você pode configurar explicitamente a aplicação a ser usada.
## Configure o `entrypoint` da aplicação em `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Você pode configurar onde sua aplicação está localizada em um arquivo `pyproject.toml`, assim:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Esse `entrypoint` dirá ao comando `fastapi` que ele deve importar a aplicação assim:
```python
from main import app
```
Se o seu código estivesse estruturado assim:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Então você definiria o `entrypoint` como:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
o que seria equivalente a:
```python
from backend.main import app
```
### `fastapi dev` com caminho { #fastapi-dev-with-path }
Você também pode passar o caminho do arquivo para o comando `fastapi dev`, e ele deduzirá o objeto da aplicação FastAPI a usar:
```console
$ fastapi dev main.py
```
Mas você teria que lembrar de passar o caminho correto toda vez que chamar o comando `fastapi`.
Além disso, outras ferramentas podem não conseguir encontrá-la, por exemplo a [Extensão do VS Code](editor-support.md) ou a [FastAPI Cloud](https://fastapicloud.com), então é recomendado usar o `entrypoint` em `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
Executar `fastapi dev` inicia o modo de desenvolvimento.
Por padrão, o **recarregamento automático** está ativado, recarregando o servidor automaticamente quando você faz mudanças no seu código. Isso consome muitos recursos e pode ser menos estável do que quando está desativado. Você deveria usá-lo apenas no desenvolvimento. Ele também escuta no endereço IP `127.0.0.1`, que é o IP para a sua máquina se comunicar apenas consigo mesma (`localhost`).
## `fastapi run` { #fastapi-run }
Executar `fastapi run` inicia o FastAPI em modo de produção.
Por padrão, o **recarregamento automático** está desativado. Ele também escuta no endereço IP `0.0.0.0`, o que significa todos os endereços IP disponíveis; dessa forma, ficará acessível publicamente para qualquer pessoa que consiga se comunicar com a máquina. É assim que você normalmente o executaria em produção, por exemplo, em um contêiner.
Na maioria dos casos, você teria (e você deveria ter) um "proxy de terminação" tratando o HTTPS por cima; isso dependerá de como você faz o deploy da sua aplicação, seu provedor pode fazer isso por você ou talvez seja necessário que você configure isso por conta própria.
/// tip | Dica
Você pode aprender mais sobre isso na [documentação de deployment](deployment/index.md).
///
================================================
FILE: docs/pt/docs/features.md
================================================
# Recursos { #features }
## Recursos do FastAPI { #fastapi-features }
**FastAPI** te oferece o seguinte:
### Baseado em padrões abertos { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) para criação de APIs, incluindo declarações de caminho operações, parâmetros, requisições de corpo, segurança etc.
* Documentação automática de modelos de dados com [**JSON Schema**](https://json-schema.org/) (já que o OpenAPI em si é baseado no JSON Schema).
* Projetado em torno desses padrões, após um estudo meticuloso. Em vez de uma camada improvisada por cima.
* Isso também permite o uso de **geração de código do cliente** automaticamente em muitas linguagens.
### Documentação automática { #automatic-docs }
Documentação interativa da API e navegação web da interface de usuário. Como o framework é baseado no OpenAPI, há várias opções, 2 incluídas por padrão.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), com navegação interativa, chame e teste sua API diretamente do navegador.

* Documentação alternativa da API com [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Apenas Python moderno { #just-modern-python }
Tudo é baseado no padrão das declarações de **tipos do Python** (graças ao Pydantic). Nenhuma sintaxe nova para aprender. Apenas o padrão moderno do Python.
Se você precisa refrescar a memória rapidamente sobre como usar tipos do Python (mesmo que você não use o FastAPI), confira esse rápido tutorial: [Tipos do Python](python-types.md).
Você escreve Python padrão com tipos:
```Python
from datetime import date
from pydantic import BaseModel
# Declare uma variável como str
# e obtenha suporte do editor dentro da função
def main(user_id: str):
return user_id
# Um modelo do Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Que então pode ser usado como:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Informação
`**second_user_data` quer dizer:
Passe as chaves e valores do dicionário `second_user_data` diretamente como argumentos chave-valor, equivalente a: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Suporte de editores { #editor-support }
Todo o framework foi projetado para ser fácil e intuitivo de usar, todas as decisões foram testadas em vários editores antes do início do desenvolvimento, para garantir a melhor experiência de desenvolvimento.
Na pesquisa de desenvolvedores Python, ficou claro [que um dos recursos mais utilizados é o "preenchimento automático"](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Todo o framework **FastAPI** é feito para satisfazer isso. O preenchimento automático funciona em todos os lugares.
Você raramente precisará voltar à documentação.
Aqui está como o editor poderá te ajudar:
* no [Visual Studio Code](https://code.visualstudio.com/):

* no [PyCharm](https://www.jetbrains.com/pycharm/):

Você terá preenchimento automático no seu código que você poderia considerar impossível antes. Como por exemplo, a chave `price` dentro do corpo JSON (que poderia ter sido aninhado) que vem de uma requisição.
Sem a necessidade de digitar nomes de chaves erroneamente, ir e voltar entre documentações, ou rolar pela página para descobrir se você utilizou `username` ou `user_name`.
### Breve { #short }
Há **padrões** sensíveis para tudo, com configurações adicionais em todos os lugares. Todos os parâmetros podem ser regulados para fazer o que você precisa e para definir a API que você necessita.
Por padrão, tudo **"simplesmente funciona"**.
### Validação { #validation }
* Validação para a maioria dos (ou todos?) **tipos de dados** do Python, incluindo:
* objetos JSON (`dict`).
* arrays JSON (`list`), definindo tipos dos itens.
* campos String (`str`), definindo tamanho mínimo e máximo.
* Numbers (`int`, `float`) com valores mínimos e máximos, etc.
* Validação de tipos mais exóticos, como:
* URL.
* Email.
* UUID.
* ...e outros.
Toda a validação é controlada pelo robusto e bem estabelecido **Pydantic**.
### Segurança e autenticação { #security-and-authentication }
Segurança e autenticação integradas. Sem nenhum compromisso com bancos de dados ou modelos de dados.
Todos os esquemas de seguranças definidos no OpenAPI, incluindo:
* HTTP Basic.
* **OAuth2** (também com **tokens JWT**). Confira o tutorial em [OAuth2 com JWT](tutorial/security/oauth2-jwt.md).
* Chaves de API em:
* Headers.
* parâmetros da Query.
* Cookies etc.
Além disso, todos os recursos de segurança do Starlette (incluindo **cookies de sessão**).
Tudo construído como ferramentas e componentes reutilizáveis que são fáceis de integrar com seus sistemas, armazenamento de dados, banco de dados relacionais e não-relacionais etc.
### Injeção de dependência { #dependency-injection }
FastAPI inclui um sistema de injeção de dependência extremamente fácil de usar, mas extremamente poderoso.
* Mesmo dependências podem ter dependências, criando uma hierarquia ou **"grafo" de dependências**.
* Tudo **automaticamente controlado** pelo framework.
* Todas as dependências podem pedir dados das requisições e **ampliar** as restrições e documentação automática da **operação de rota**.
* **Validação automática** mesmo para parâmetros da *operação de rota* definidos em dependências.
* Suporte para sistemas de autenticação complexos, **conexões com banco de dados** etc.
* **Sem comprometer** os bancos de dados, frontends etc. Mas fácil integração com todos eles.
### "Plug-ins" ilimitados { #unlimited-plug-ins }
Ou, de outra forma, sem a necessidade deles, importe e use o código que precisar.
Qualquer integração é projetada para ser tão simples de usar (com dependências) que você pode criar um "plug-in" para suas aplicações com 2 linhas de código usando a mesma estrutura e sintaxe para as suas *operações de rota*.
### Testado { #tested }
* 100% de cobertura de testes.
* 100% do código com anotações de tipo.
* Usado para aplicações em produção.
## Recursos do Starlette { #starlette-features }
**FastAPI** é totalmente compatível com (e baseado no) [**Starlette**](https://www.starlette.dev/). Então, qualquer código adicional Starlette que você tiver, também funcionará.
`FastAPI` é na verdade uma sub-classe do `Starlette`. Então, se você já conhece ou usa Starlette, a maioria das funcionalidades se comportará da mesma forma.
Com **FastAPI**, você terá todos os recursos do **Starlette** (já que FastAPI é apenas um Starlette com esteróides):
* Desempenho realmente impressionante. É [um dos frameworks Python disponíveis mais rápidos, a par com o **NodeJS** e **Go**](https://github.com/encode/starlette#performance).
* Suporte a **WebSocket**.
* Tarefas em processo background.
* Eventos na inicialização e encerramento.
* Cliente de testes construído sobre HTTPX.
* Respostas em **CORS**, GZip, Static Files, Streaming.
* Suporte a **Session e Cookie**.
* 100% de cobertura de testes.
* 100% do código utilizando anotações de tipo.
## Recursos do Pydantic { #pydantic-features }
**FastAPI** é totalmente compatível com (e baseado no) [**Pydantic**](https://docs.pydantic.dev/). Então, qualquer código Pydantic adicional que você tiver, também funcionará.
Incluindo bibliotecas externas também baseadas no Pydantic, como ORMs e ODMs para bancos de dados.
Isso também significa que em muitos casos você poderá passar o mesmo objeto que você receber de uma requisição **diretamente para o banco de dados**, já que tudo é validado automaticamente.
O mesmo se aplica no sentido inverso, em muitos casos você poderá simplesmente passar o objeto que você recebeu do banco de dados **diretamente para o cliente**.
Com **FastAPI** você terá todos os recursos do **Pydantic** (já que FastAPI utiliza o Pydantic para todo o controle dos dados):
* **Sem pegadinhas**:
* Sem novas definições de esquema de micro-linguagem para aprender.
* Se você conhece os tipos do Python, você sabe como usar o Pydantic.
* Vai bem com o/a seu/sua **IDE/linter/cérebro**:
* Como as estruturas de dados do Pydantic são apenas instâncias de classes que você define, o preenchimento automático, linting, mypy e a sua intuição devem funcionar corretamente com seus dados validados.
* Valida **estruturas complexas**:
* Use modelos hierárquicos do Pydantic, `List` e `Dict` do `typing` do Python, etc.
* Validadores permitem que esquemas de dados complexos sejam limpos e facilmente definidos, conferidos e documentados como JSON Schema.
* Você pode ter **JSONs aninhados** profundamente e tê-los todos validados e anotados.
* **Extensível**:
* Pydantic permite que tipos de dados personalizados sejam definidos ou você pode estender a validação com métodos em um modelo decorado com seu decorador de validador.
* 100% de cobertura de testes.
================================================
FILE: docs/pt/docs/help-fastapi.md
================================================
# Ajude o FastAPI - Obtenha ajuda { #help-fastapi-get-help }
Você gosta do **FastAPI**?
Você gostaria de ajudar o FastAPI, outras pessoas usuárias e o autor?
Ou você gostaria de obter ajuda com o **FastAPI**?
Há maneiras muito simples de ajudar (várias envolvem apenas um ou dois cliques).
E também há várias formas de obter ajuda.
## Assine a newsletter { #subscribe-to-the-newsletter }
Você pode assinar a (infrequente) [newsletter do **FastAPI and friends**](newsletter.md) para ficar por dentro de:
* Notícias sobre FastAPI e amigos 🚀
* Tutoriais 📝
* Funcionalidades ✨
* Mudanças incompatíveis 🚨
* Dicas e truques ✅
## Siga o FastAPI no X (Twitter) { #follow-fastapi-on-x-twitter }
[Siga @fastapi no **X (Twitter)**](https://x.com/fastapi) para receber as últimas notícias sobre o **FastAPI**. 🐦
## Dê uma estrela ao **FastAPI** no GitHub { #star-fastapi-in-github }
Você pode “marcar com estrela” o FastAPI no GitHub (clicando no botão de estrela no canto superior direito): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Ao adicionar uma estrela, outras pessoas conseguirão encontrá-lo com mais facilidade e verão que já foi útil para muita gente.
## Acompanhe o repositório no GitHub para lançamentos { #watch-the-github-repository-for-releases }
Você pode “acompanhar” (watch) o FastAPI no GitHub (clicando no botão “watch” no canto superior direito): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Lá você pode selecionar “Apenas lançamentos”.
Fazendo isso, você receberá notificações (no seu email) sempre que houver um novo lançamento (uma nova versão) do **FastAPI** com correções de bugs e novas funcionalidades.
## Conecte-se com o autor { #connect-with-the-author }
Você pode se conectar [comigo (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), o autor.
Você pode:
* [Me seguir no **GitHub**](https://github.com/tiangolo).
* Ver outros projetos Open Source que criei e que podem ajudar você.
* Me seguir para saber quando eu criar um novo projeto Open Source.
* [Me seguir no **X (Twitter)**](https://x.com/tiangolo) ou no [Mastodon](https://fosstodon.org/@tiangolo).
* Me contar como você usa o FastAPI (adoro saber disso).
* Ficar sabendo quando eu fizer anúncios ou lançar novas ferramentas.
* Você também pode [seguir @fastapi no X (Twitter)](https://x.com/fastapi) (uma conta separada).
* [Me seguir no **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Ver quando eu fizer anúncios ou lançar novas ferramentas (embora eu use mais o X (Twitter) 🤷♂).
* Ler o que escrevo (ou me seguir) no [**Dev.to**](https://dev.to/tiangolo) ou no [**Medium**](https://medium.com/@tiangolo).
* Ler outras ideias, artigos e conhecer ferramentas que criei.
* Me seguir para ver quando eu publicar algo novo.
## Tweet sobre o **FastAPI** { #tweet-about-fastapi }
[Tweet sobre o **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) e conte para mim e para outras pessoas por que você gosta dele. 🎉
Eu adoro saber como o **FastAPI** está sendo usado, o que você tem curtido nele, em qual projeto/empresa você o utiliza, etc.
## Vote no FastAPI { #vote-for-fastapi }
* [Vote no **FastAPI** no Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Vote no **FastAPI** no AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Diga que você usa o **FastAPI** no StackShare](https://stackshare.io/pypi-fastapi).
## Ajude outras pessoas com perguntas no GitHub { #help-others-with-questions-in-github }
Você pode tentar ajudar outras pessoas com suas perguntas em:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
Em muitos casos você já pode saber a resposta para aquelas perguntas. 🤓
Se você estiver ajudando muitas pessoas com suas perguntas, você se tornará um(a) [Especialista em FastAPI](fastapi-people.md#fastapi-experts) oficial. 🎉
Apenas lembre-se, o ponto mais importante é: tente ser gentil. As pessoas chegam com frustrações e, em muitos casos, não perguntam da melhor forma, mas tente ao máximo ser gentil. 🤗
A ideia é que a comunidade do **FastAPI** seja gentil e acolhedora. Ao mesmo tempo, não aceite bullying ou comportamentos desrespeitosos com outras pessoas. Temos que cuidar uns dos outros.
---
Veja como ajudar outras pessoas com perguntas (em discussions ou issues):
### Entenda a pergunta { #understand-the-question }
* Verifique se você consegue entender qual é o **objetivo** e o caso de uso de quem está perguntando.
* Depois verifique se a pergunta (a grande maioria são perguntas) está **clara**.
* Em muitos casos a pergunta feita é sobre uma solução imaginada pela pessoa usuária, mas pode haver uma solução **melhor**. Se você entender melhor o problema e o caso de uso, pode sugerir uma **solução alternativa** melhor.
* Se você não entender a pergunta, peça mais **detalhes**.
### Reproduza o problema { #reproduce-the-problem }
Na maioria dos casos e na maioria das perguntas há algo relacionado ao **código original** da pessoa.
Em muitos casos ela só copia um fragmento do código, mas isso não é suficiente para **reproduzir o problema**.
* Você pode pedir que forneçam um [exemplo mínimo, reproduzível](https://stackoverflow.com/help/minimal-reproducible-example), que você possa **copiar e colar** e executar localmente para ver o mesmo erro ou comportamento que elas estão vendo, ou para entender melhor o caso de uso.
* Se você estiver muito generoso, pode tentar **criar um exemplo** assim você mesmo, apenas com base na descrição do problema. Só tenha em mente que isso pode levar bastante tempo e pode ser melhor pedir primeiro que esclareçam o problema.
### Sugira soluções { #suggest-solutions }
* Depois de conseguir entender a pergunta, você pode dar uma possível **resposta**.
* Em muitos casos, é melhor entender o **problema subjacente ou caso de uso**, pois pode haver uma forma melhor de resolver do que aquilo que estão tentando fazer.
### Peça para encerrar { #ask-to-close }
Se a pessoa responder, há uma grande chance de você ter resolvido o problema, parabéns, **você é um herói**! 🦸
* Agora, se isso resolveu o problema, você pode pedir para:
* No GitHub Discussions: marcar o comentário como a **resposta**.
* No GitHub Issues: **encerrar** a issue.
## Acompanhe o repositório do GitHub { #watch-the-github-repository }
Você pode “acompanhar” (watch) o FastAPI no GitHub (clicando no botão “watch” no canto superior direito): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Se você selecionar “Acompanhando” em vez de “Apenas lançamentos”, receberá notificações quando alguém criar uma nova issue ou pergunta. Você também pode especificar que quer ser notificado apenas sobre novas issues, ou discussions, ou PRs, etc.
Assim você pode tentar ajudar a resolver essas questões.
## Faça perguntas { #ask-questions }
Você pode [criar uma nova pergunta](https://github.com/fastapi/fastapi/discussions/new?category=questions) no repositório do GitHub, por exemplo para:
* Fazer uma **pergunta** ou perguntar sobre um **problema**.
* Sugerir uma nova **funcionalidade**.
**Nota**: se você fizer isso, então vou pedir que você também ajude outras pessoas. 😉
## Revise Pull Requests { #review-pull-requests }
Você pode me ajudar revisando pull requests de outras pessoas.
Novamente, por favor tente ao máximo ser gentil. 🤗
---
Veja o que ter em mente e como revisar um pull request:
### Entenda o problema { #understand-the-problem }
* Primeiro, garanta que você **entendeu o problema** que o pull request tenta resolver. Pode haver uma discussão mais longa em uma Discussion ou issue do GitHub.
* Também há uma boa chance de o pull request não ser realmente necessário porque o problema pode ser resolvido de uma **forma diferente**. Aí você pode sugerir ou perguntar sobre isso.
### Não se preocupe com estilo { #dont-worry-about-style }
* Não se preocupe muito com coisas como estilos de mensagens de commit, eu vou fazer squash e merge personalizando o commit manualmente.
* Também não se preocupe com regras de estilo, já há ferramentas automatizadas verificando isso.
E se houver qualquer outra necessidade de estilo ou consistência, vou pedir diretamente, ou vou adicionar commits por cima com as mudanças necessárias.
### Verifique o código { #check-the-code }
* Verifique e leia o código, veja se faz sentido, **execute localmente** e veja se realmente resolve o problema.
* Depois **comente** dizendo que você fez isso, é assim que saberei que você realmente verificou.
/// info | Informação
Infelizmente, eu não posso simplesmente confiar em PRs que têm várias aprovações.
Já aconteceu várias vezes de haver PRs com 3, 5 ou mais aprovações, provavelmente porque a descrição é atraente, mas quando eu verifico os PRs, eles estão quebrados, têm um bug, ou não resolvem o problema que afirmam resolver. 😅
Por isso, é realmente importante que você leia e execute o código, e me avise nos comentários que você fez isso. 🤓
///
* Se o PR puder ser simplificado de alguma forma, você pode pedir isso, mas não há necessidade de ser exigente demais, pode haver muitos pontos de vista subjetivos (e eu terei o meu também 🙈), então é melhor focar nas coisas fundamentais.
### Testes { #tests }
* Me ajude a verificar se o PR tem **testes**.
* Verifique se os testes **falham** antes do PR. 🚨
* Depois verifique se os testes **passam** após o PR. ✅
* Muitos PRs não têm testes, você pode **lembrar** a pessoa de adicionar testes, ou até **sugerir** alguns testes você mesmo. Essa é uma das coisas que consomem mais tempo e você pode ajudar muito com isso.
* Depois também comente o que você testou, assim vou saber que você verificou. 🤓
## Crie um Pull Request { #create-a-pull-request }
Você pode [contribuir](contributing.md) com o código-fonte fazendo Pull Requests, por exemplo:
* Para corrigir um erro de digitação que você encontrou na documentação.
* Para compartilhar um artigo, vídeo ou podcast que você criou ou encontrou sobre o FastAPI, [editando este arquivo](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Garanta que você adicione seu link no início da seção correspondente.
* Para ajudar a [traduzir a documentação](contributing.md#translations) para seu idioma.
* Você também pode ajudar a revisar as traduções criadas por outras pessoas.
* Para propor novas seções de documentação.
* Para corrigir uma issue/bug existente.
* Garanta que você adicione testes.
* Para adicionar uma nova funcionalidade.
* Garanta que você adicione testes.
* Garanta que você adicione documentação se for relevante.
## Ajude a manter o FastAPI { #help-maintain-fastapi }
Ajude-me a manter o **FastAPI**! 🤓
Há muito trabalho a fazer e, para a maior parte dele, **VOCÊ** pode ajudar.
As principais tarefas que você pode fazer agora são:
* [Ajudar outras pessoas com perguntas no GitHub](#help-others-with-questions-in-github) (veja a seção acima).
* [Revisar Pull Requests](#review-pull-requests) (veja a seção acima).
Essas duas tarefas são as que **mais consomem tempo**. Esse é o principal trabalho de manter o FastAPI.
Se você puder me ajudar com isso, **você está me ajudando a manter o FastAPI** e garantindo que ele continue **avançando mais rápido e melhor**. 🚀
## Entre no chat { #join-the-chat }
Entre no 👥 [servidor de chat do Discord](https://discord.gg/VQjSZaeJmf) 👥 e converse com outras pessoas da comunidade FastAPI.
/// tip | Dica
Para perguntas, faça-as no [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), há uma chance muito maior de você receber ajuda pelos [Especialistas em FastAPI](fastapi-people.md#fastapi-experts).
Use o chat apenas para outras conversas gerais.
///
### Não use o chat para perguntas { #dont-use-the-chat-for-questions }
Tenha em mente que, como os chats permitem uma “conversa mais livre”, é fácil fazer perguntas muito gerais e mais difíceis de responder, então você pode acabar não recebendo respostas.
No GitHub, o template vai orientar você a escrever a pergunta certa para que você consiga obter uma boa resposta com mais facilidade, ou até resolver o problema sozinho antes de perguntar. E no GitHub eu consigo garantir que sempre vou responder tudo, mesmo que leve algum tempo. Eu pessoalmente não consigo fazer isso com os sistemas de chat. 😅
As conversas nos sistemas de chat também não são tão fáceis de pesquisar quanto no GitHub, então perguntas e respostas podem se perder na conversa. E somente as que estão no GitHub contam para você se tornar um(a) [Especialista em FastAPI](fastapi-people.md#fastapi-experts), então é bem provável que você receba mais atenção no GitHub.
Por outro lado, há milhares de usuários nos sistemas de chat, então há uma grande chance de você encontrar alguém para conversar por lá quase o tempo todo. 😄
## Patrocine o autor { #sponsor-the-author }
Se o seu **produto/empresa** depende de ou está relacionado ao **FastAPI** e você quer alcançar suas pessoas usuárias, você pode patrocinar o autor (eu) através do [GitHub sponsors](https://github.com/sponsors/tiangolo). Dependendo do nível, você pode obter benefícios extras, como um selo na documentação. 🎁
---
Obrigado! 🚀
================================================
FILE: docs/pt/docs/history-design-future.md
================================================
# História, Design e Futuro { #history-design-and-future }
Há algum tempo, [um usuário **FastAPI** perguntou](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> Qual é a história desse projeto? Parece que surgiu do nada e se tornou incrível em poucas semanas [...]
Aqui está um pouco dessa história.
## Alternativas { #alternatives }
Eu tenho criado APIs com requisitos complexos por vários anos (Aprendizado de Máquina, sistemas distribuídos, tarefas assíncronas, banco de dados NoSQL etc.), liderando vários times de desenvolvedores.
Como parte disso, eu precisava investigar, testar e usar muitas alternativas.
A história do **FastAPI** é, em grande parte, a história de seus predecessores.
Como dito na seção [Alternativas](alternatives.md):
**FastAPI** não existiria se não pelo trabalho anterior de outros.
Há muitas ferramentas criadas antes que ajudaram a inspirar sua criação.
Eu estive evitando a criação de um novo _framework_ por vários anos. Primeiro tentei resolver todas as funcionalidades cobertas por **FastAPI** usando muitos _frameworks_, _plug-ins_ e ferramentas diferentes.
Mas em algum ponto, não havia outra opção senão criar algo que oferecia todas as funcionalidades, aproveitando as melhores ideias de ferramentas anteriores, e combinando-as da melhor maneira possível, usando funcionalidades da linguagem que nem estavam disponíveis antes (anotações de tipo do Python 3.6+).
## Investigação { #investigation }
Ao usar todas as alternativas anteriores, eu tive a chance de aprender com todas elas, aproveitar ideias e combiná-las da melhor maneira que encontrei para mim e para os times de desenvolvedores com os quais trabalhava.
Por exemplo, estava claro que idealmente ele deveria ser baseado nas anotações de tipo padrão do Python.
Também, a melhor abordagem era usar padrões já existentes.
Então, antes mesmo de começar a codificar o **FastAPI**, eu investi vários meses estudando as especificações do OpenAPI, JSON Schema, OAuth2 etc. Entendendo suas relações, sobreposições e diferenças.
## Design { #design }
Eu então dediquei algum tempo projetando a "API" de desenvolvimento que eu queria como usuário (como um desenvolvedor usando o FastAPI).
Eu testei várias ideias nos editores Python mais populares: PyCharm, VS Code, e editores baseados no Jedi.
Pela última [Pesquisa do Desenvolvedor Python](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), isso cobre cerca de 80% dos usuários.
Isso significa que o **FastAPI** foi testado especificamente com os editores usados por 80% dos desenvolvedores Python. Como a maioria dos outros editores tendem a funcionar de forma similar, todos os seus benefícios devem funcionar para virtualmente todos os editores.
Dessa forma eu pude encontrar a melhor maneira de reduzir duplicação de código o máximo possível, ter preenchimento automático em todos os lugares, conferência de tipos e erros etc.
Tudo de uma forma que oferecesse a melhor experiência de desenvolvimento para todos os desenvolvedores.
## Requisitos { #requirements }
Após testar várias alternativas, eu decidi que usaria o [**Pydantic**](https://docs.pydantic.dev/) por suas vantagens.
Então eu contribuí com ele, para deixá-lo completamente de acordo com o JSON Schema, para dar suporte a diferentes maneiras de definir declarações de restrições, e melhorar o suporte a editores (conferências de tipos, preenchimento automático) baseado nos testes em vários editores.
Durante o desenvolvimento, eu também contribuí com o [**Starlette**](https://www.starlette.dev/), outro requisito chave.
## Desenvolvimento { #development }
Quando comecei a criar o **FastAPI** de fato, a maior parte das peças já estavam encaixadas, o design estava definido, os requisitos e ferramentas já estavam prontos, e o conhecimento sobre os padrões e especificações estavam claros e frescos.
## Futuro { #future }
Nesse ponto, já está claro que o **FastAPI** com suas ideias está sendo útil para muitas pessoas.
Ele está sendo escolhido em relação a alternativas anteriores por se adequar melhor em muitos casos.
Muitos desenvolvedores e times já dependem do **FastAPI** para seus projetos (incluindo eu e meu time).
Mas ainda há muitas melhorias e funcionalidades a vir.
**FastAPI** tem um grande futuro à frente.
E [sua ajuda](help-fastapi.md) é muito bem-vinda.
================================================
FILE: docs/pt/docs/how-to/authentication-error-status-code.md
================================================
# Usar antigos códigos de status de erro de autenticação 403 { #use-old-403-authentication-error-status-codes }
Antes da versão `0.122.0` do FastAPI, quando os utilitários de segurança integrados retornavam um erro ao cliente após uma falha na autenticação, eles usavam o código de status HTTP `403 Forbidden`.
A partir da versão `0.122.0` do FastAPI, eles usam o código de status HTTP `401 Unauthorized`, mais apropriado, e retornam um cabeçalho `WWW-Authenticate` adequado na response, seguindo as especificações HTTP, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Mas, se por algum motivo seus clientes dependem do comportamento antigo, você pode voltar a ele sobrescrevendo o método `make_not_authenticated_error` nas suas classes de segurança.
Por exemplo, você pode criar uma subclasse de `HTTPBearer` que retorne um erro `403 Forbidden` em vez do erro padrão `401 Unauthorized`:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Dica
Perceba que a função retorna a instância da exceção, ela não a lança. O lançamento é feito no restante do código interno.
///
================================================
FILE: docs/pt/docs/how-to/conditional-openapi.md
================================================
# OpenAPI condicional { #conditional-openapi }
Se necessário, você pode usar configurações e variáveis de ambiente para configurar o OpenAPI condicionalmente dependendo do ambiente e até mesmo desativá-lo completamente.
## Sobre segurança, APIs e documentação { #about-security-apis-and-docs }
Ocultar suas interfaces de usuário de documentação na produção não *deveria* ser a maneira de proteger sua API.
Isso não adiciona nenhuma segurança extra à sua API; as *operações de rota* ainda estarão disponíveis onde estão.
Se houver uma falha de segurança no seu código, ela ainda existirá.
Ocultar a documentação apenas torna mais difícil entender como interagir com sua API e pode dificultar sua depuração na produção. Pode ser considerado simplesmente uma forma de [Segurança através da obscuridade](https://en.wikipedia.org/wiki/Security_through_obscurity).
Se você quiser proteger sua API, há várias coisas melhores que você pode fazer, por exemplo:
* Certifique-se de ter modelos Pydantic bem definidos para seus corpos de request e respostas.
* Configure quaisquer permissões e funções necessárias usando dependências.
* Nunca armazene senhas em texto simples, apenas hashes de senha.
* Implemente e use ferramentas criptográficas bem conhecidas, como pwdlib e tokens JWT, etc.
* Adicione controles de permissão mais granulares com escopos OAuth2 quando necessário.
* ...etc.
No entanto, você pode ter um caso de uso muito específico em que realmente precisa desabilitar a documentação da API para algum ambiente (por exemplo, para produção) ou dependendo de configurações de variáveis de ambiente.
## OpenAPI condicional com configurações e variáveis de ambiente { #conditional-openapi-from-settings-and-env-vars }
Você pode usar facilmente as mesmas configurações do Pydantic para configurar sua OpenAPI gerada e as interfaces de usuário da documentação.
Por exemplo:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Aqui declaramos a configuração `openapi_url` com o mesmo padrão de `"/openapi.json"`.
E então a usamos ao criar a aplicação `FastAPI`.
Então você pode desabilitar o OpenAPI (incluindo a documentação da interface do usuário) definindo a variável de ambiente `OPENAPI_URL` como a string vazia, como:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Então, se você acessar as URLs em `/openapi.json`, `/docs` ou `/redoc`, você receberá apenas um erro `404 Not Found` como:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/pt/docs/how-to/configure-swagger-ui.md
================================================
# Configure a UI do Swagger { #configure-swagger-ui }
Você pode configurar alguns [parâmetros extras da UI do Swagger](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
Para configurá-los, passe o argumento `swagger_ui_parameters` ao criar o objeto da aplicação `FastAPI()` ou para a função `get_swagger_ui_html()`.
`swagger_ui_parameters` recebe um dicionário com as configurações passadas diretamente para o Swagger UI.
O FastAPI converte as configurações para **JSON** para torná-las compatíveis com JavaScript, pois é disso que o Swagger UI precisa.
## Desabilitar destaque de sintaxe { #disable-syntax-highlighting }
Por exemplo, você pode desabilitar o destaque de sintaxe na UI do Swagger.
Sem alterar as configurações, o destaque de sintaxe é habilitado por padrão:
Mas você pode desabilitá-lo definindo `syntaxHighlight` como `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...e então o Swagger UI não mostrará mais o destaque de sintaxe:
## Alterar o tema { #change-the-theme }
Da mesma forma que você pode definir o tema de destaque de sintaxe com a chave `"syntaxHighlight.theme"` (observe que há um ponto no meio):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Essa configuração alteraria o tema de cores de destaque de sintaxe:
## Alterar parâmetros de UI padrão do Swagger { #change-default-swagger-ui-parameters }
O FastAPI inclui alguns parâmetros de configuração padrão apropriados para a maioria dos casos de uso.
Inclui estas configurações padrão:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Você pode substituir qualquer um deles definindo um valor diferente no argumento `swagger_ui_parameters`.
Por exemplo, para desabilitar `deepLinking` você pode passar essas configurações para `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Outros parâmetros da UI do Swagger { #other-swagger-ui-parameters }
Para ver todas as outras configurações possíveis que você pode usar, leia a [documentação oficial dos parâmetros da UI do Swagger](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Configurações somente JavaScript { #javascript-only-settings }
A UI do Swagger também permite que outras configurações sejam objetos **somente JavaScript** (por exemplo, funções JavaScript).
O FastAPI também inclui estas configurações `presets` somente para JavaScript:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Esses são objetos **JavaScript**, não strings, então você não pode passá-los diretamente do código Python.
Se você precisar usar configurações somente JavaScript como essas, você pode usar um dos métodos acima. Substitua toda a *operação de rota* do Swagger UI e escreva manualmente qualquer JavaScript que você precisar.
================================================
FILE: docs/pt/docs/how-to/custom-docs-ui-assets.md
================================================
# Recursos Estáticos Personalizados para a UI de Documentação (Hospedagem Própria) { #custom-docs-ui-static-assets-self-hosting }
A documentação da API usa **Swagger UI** e **ReDoc**, e cada um deles precisa de alguns arquivos JavaScript e CSS.
Por padrão, esses arquivos são fornecidos por um CDN.
Mas é possível personalizá-los, você pode definir um CDN específico ou providenciar os arquivos você mesmo.
## CDN Personalizado para JavaScript e CSS { #custom-cdn-for-javascript-and-css }
Vamos supor que você deseja usar um CDN diferente, por exemplo, você deseja usar `https://unpkg.com/`.
Isso pode ser útil se, por exemplo, você mora em um país que restringe algumas URLs.
### Desativar a documentação automática { #disable-the-automatic-docs }
O primeiro passo é desativar a documentação automática, pois por padrão, ela usa o CDN padrão.
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Incluir a documentação personalizada { #include-the-custom-docs }
Agora você pode criar as *operações de rota* para a documentação personalizada.
Você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
* `title`: o título da sua API.
* `oauth2_redirect_url`: você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. Este é o URL do CDN personalizado.
* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. Este é o URL do CDN personalizado.
E de forma semelhante para o ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Dica
A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E interagir com ela usando a autenticação OAuth2 real.
Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirecionamento".
///
### Criar uma *operação de rota* para testar { #create-a-path-operation-to-test-it }
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Teste { #test-it }
Agora, você deve ser capaz de ir para a documentação em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), e recarregar a página, ela carregará esses recursos do novo CDN.
## Hospedagem Própria de JavaScript e CSS para a documentação { #self-hosting-javascript-and-css-for-docs }
Hospedar o JavaScript e o CSS pode ser útil se, por exemplo, você precisa que seu aplicativo continue funcionando mesmo offline, sem acesso aberto à Internet, ou em uma rede local.
Aqui você verá como providenciar esses arquivos você mesmo, na mesma aplicação FastAPI, e configurar a documentação para usá-los.
### Estrutura de Arquivos do Projeto { #project-file-structure }
Vamos supor que a estrutura de arquivos do seu projeto se pareça com isso:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Agora crie um diretório para armazenar esses arquivos estáticos.
Sua nova estrutura de arquivos poderia se parecer com isso:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Baixe os arquivos { #download-the-files }
Baixe os arquivos estáticos necessários para a documentação e coloque-os no diretório `static/`.
Você provavelmente pode clicar com o botão direito em cada link e selecionar uma opção semelhante a "Salvar link como...".
**Swagger UI** usa os arquivos:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
E o **ReDoc** usa o arquivo:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Depois disso, sua estrutura de arquivos deve se parecer com:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Prover os arquivos estáticos { #serve-the-static-files }
* Importe `StaticFiles`.
* "Monte" a instância `StaticFiles()` em um caminho específico.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Teste os arquivos estáticos { #test-the-static-files }
Inicialize seu aplicativo e vá para [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Você deverá ser ver um arquivo JavaScript muito longo para o **ReDoc**.
Esse arquivo pode começar com algo como:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Isso confirma que você está conseguindo fornecer arquivos estáticos do seu aplicativo e que você colocou os arquivos estáticos para a documentação no local correto.
Agora, podemos configurar o aplicativo para usar esses arquivos estáticos para a documentação.
### Desativar a documentação automática para arquivos estáticos { #disable-the-automatic-docs-for-static-files }
Da mesma forma que ao usar um CDN personalizado, o primeiro passo é desativar a documentação automática, pois ela usa o CDN padrão.
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Incluir a documentação personalizada para arquivos estáticos { #include-the-custom-docs-for-static-files }
E da mesma forma que com um CDN personalizado, agora você pode criar as *operações de rota* para a documentação personalizada.
Novamente, você pode reutilizar as funções internas do FastAPI para criar as páginas HTML para a documentação e passar os argumentos necessários:
* `openapi_url`: a URL onde a página HTML para a documentação pode obter o esquema OpenAPI para a sua API. Você pode usar aqui o atributo `app.openapi_url`.
* `title`: o título da sua API.
* `oauth2_redirect_url`: Você pode usar `app.swagger_ui_oauth2_redirect_url` aqui para usar o padrão.
* `swagger_js_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **JavaScript**. **Este é o URL que seu aplicativo está fornecendo**.
* `swagger_css_url`: a URL onde a página HTML para a sua documentação do Swagger UI pode obter o arquivo **CSS**. **Esse é o que seu aplicativo está fornecendo**.
E de forma semelhante para o ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Dica
A *operação de rota* para `swagger_ui_redirect` é um auxiliar para quando você usa OAuth2.
Se você integrar sua API com um provedor OAuth2, você poderá autenticar e voltar para a documentação da API com as credenciais adquiridas. E, então, interagir com ela usando a autenticação OAuth2 real.
Swagger UI lidará com isso nos bastidores para você, mas ele precisa desse auxiliar de "redirect".
///
### Criar uma *operação de rota* para testar arquivos estáticos { #create-a-path-operation-to-test-static-files }
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Teste a UI de Arquivos Estáticos { #test-static-files-ui }
Agora, você deve ser capaz de desconectar o WiFi, ir para a documentação em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), e recarregar a página.
E mesmo sem Internet, você será capaz de ver a documentação da sua API e interagir com ela.
================================================
FILE: docs/pt/docs/how-to/custom-request-and-route.md
================================================
# Request e classe APIRoute personalizadas { #custom-request-and-apiroute-class }
Em alguns casos, você pode querer sobrescrever a lógica usada pelas classes `Request` e `APIRoute`.
Em particular, isso pode ser uma boa alternativa para uma lógica em um middleware.
Por exemplo, se você quiser ler ou manipular o corpo da requisição antes que ele seja processado pela sua aplicação.
/// danger | Cuidado
Isso é um recurso "avançado".
Se você for um iniciante em **FastAPI** você deve considerar pular essa seção.
///
## Casos de Uso { #use-cases }
Alguns casos de uso incluem:
* Converter requisições não-JSON para JSON (por exemplo, [`msgpack`](https://msgpack.org/index.html)).
* Descomprimir corpos de requisição comprimidos com gzip.
* Registrar automaticamente todos os corpos de requisição.
## Manipulando codificações de corpo de requisição personalizadas { #handling-custom-request-body-encodings }
Vamos ver como usar uma subclasse personalizada de `Request` para descomprimir requisições gzip.
E uma subclasse de `APIRoute` para usar essa classe de requisição personalizada.
### Criar uma classe `GzipRequest` personalizada { #create-a-custom-gziprequest-class }
/// tip | Dica
Isso é um exemplo de brincadeira para demonstrar como funciona, se você precisar de suporte para Gzip, você pode usar o [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) fornecido.
///
Primeiro, criamos uma classe `GzipRequest`, que irá sobrescrever o método `Request.body()` para descomprimir o corpo na presença de um cabeçalho apropriado.
Se não houver `gzip` no cabeçalho, ele não tentará descomprimir o corpo.
Dessa forma, a mesma classe de rota pode lidar com requisições comprimidas ou não comprimidas.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Criar uma classe `GzipRoute` personalizada { #create-a-custom-gziproute-class }
Em seguida, criamos uma subclasse personalizada de `fastapi.routing.APIRoute` que fará uso do `GzipRequest`.
Dessa vez, ele irá sobrescrever o método `APIRoute.get_route_handler()`.
Esse método retorna uma função. E essa função é o que irá receber uma requisição e retornar uma resposta.
Aqui nós usamos para criar um `GzipRequest` a partir da requisição original.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Detalhes Técnicos
Um `Request` tem um atributo `request.scope`, que é apenas um `dict` do Python contendo os metadados relacionados à requisição.
Um `Request` também tem um `request.receive`, que é uma função para "receber" o corpo da requisição.
O dicionário `scope` e a função `receive` são ambos parte da especificação ASGI.
E essas duas coisas, `scope` e `receive`, são o que é necessário para criar uma nova instância de `Request`.
Para aprender mais sobre o `Request` confira a [documentação do Starlette sobre Requests](https://www.starlette.dev/requests/).
///
A única coisa que a função retornada por `GzipRequest.get_route_handler` faz de diferente é converter o `Request` para um `GzipRequest`.
Fazendo isso, nosso `GzipRequest` irá cuidar de descomprimir os dados (se necessário) antes de passá-los para nossas *operações de rota*.
Depois disso, toda a lógica de processamento é a mesma.
Mas por causa das nossas mudanças em `GzipRequest.body`, o corpo da requisição será automaticamente descomprimido quando for carregado pelo **FastAPI** quando necessário.
## Acessando o corpo da requisição em um manipulador de exceção { #accessing-the-request-body-in-an-exception-handler }
/// tip | Dica
Para resolver esse mesmo problema, é provavelmente muito mais fácil usar o `body` em um manipulador personalizado para `RequestValidationError` ([Tratando Erros](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Mas esse exemplo ainda é valido e mostra como interagir com os componentes internos.
///
Também podemos usar essa mesma abordagem para acessar o corpo da requisição em um manipulador de exceção.
Tudo que precisamos fazer é manipular a requisição dentro de um bloco `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Se uma exceção ocorrer, a instância `Request` ainda estará em escopo, então podemos ler e fazer uso do corpo da requisição ao lidar com o erro:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Classe `APIRoute` personalizada em um router { #custom-apiroute-class-in-a-router }
Você também pode definir o parâmetro `route_class` de uma `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
Nesse exemplo, as *operações de rota* sob o `router` irão usar a classe `TimedRoute` personalizada, e terão um cabeçalho extra `X-Response-Time` na resposta com o tempo que levou para gerar a resposta:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/pt/docs/how-to/extending-openapi.md
================================================
# Extendendo o OpenAPI { #extending-openapi }
Existem alguns casos em que pode ser necessário modificar o esquema OpenAPI gerado.
Nesta seção, você verá como fazer isso.
## O processo normal { #the-normal-process }
O processo normal (padrão) é o seguinte:
Uma aplicação (instância) do `FastAPI` possui um método `.openapi()` que deve retornar o esquema OpenAPI.
Como parte da criação do objeto de aplicação, uma *operação de rota* para `/openapi.json` (ou para o que você definir como `openapi_url`) é registrada.
Ela apenas retorna uma resposta JSON com o resultado do método `.openapi()` da aplicação.
Por padrão, o que o método `.openapi()` faz é verificar se a propriedade `.openapi_schema` tem conteúdo e retorná-lo.
Se não tiver, ele gera o conteúdo usando a função utilitária em `fastapi.openapi.utils.get_openapi`.
E essa função `get_openapi()` recebe como parâmetros:
* `title`: O título do OpenAPI, exibido na documentação.
* `version`: A versão da sua API, por exemplo, `2.5.0`.
* `openapi_version`: A versão da especificação OpenAPI utilizada. Por padrão, a mais recente: `3.1.0`.
* `summary`: Um resumo curto da API.
* `description`: A descrição da sua API, que pode incluir markdown e será exibida na documentação.
* `routes`: Uma lista de rotas, que são cada uma das *operações de rota* registradas. Elas são obtidas de `app.routes`.
/// info | Informação
O parâmetro `summary` está disponível no OpenAPI 3.1.0 e superior, suportado pelo FastAPI 0.99.0 e superior.
///
## Sobrescrevendo os padrões { #overriding-the-defaults }
Com as informações acima, você pode usar a mesma função utilitária para gerar o esquema OpenAPI e sobrescrever cada parte que precisar.
Por exemplo, vamos adicionar [Extensão OpenAPI do ReDoc para incluir um logo personalizado](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### **FastAPI** Normal { #normal-fastapi }
Primeiro, escreva toda a sua aplicação **FastAPI** normalmente:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Gerar o esquema OpenAPI { #generate-the-openapi-schema }
Em seguida, use a mesma função utilitária para gerar o esquema OpenAPI, dentro de uma função `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Modificar o esquema OpenAPI { #modify-the-openapi-schema }
Agora, você pode adicionar a extensão do ReDoc, incluindo um `x-logo` personalizado ao "objeto" `info` no esquema OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Armazenar em cache o esquema OpenAPI { #cache-the-openapi-schema }
Você pode usar a propriedade `.openapi_schema` como um "cache" para armazenar o esquema gerado.
Dessa forma, sua aplicação não precisará gerar o esquema toda vez que um usuário abrir a documentação da sua API.
Ele será gerado apenas uma vez, e o mesmo esquema armazenado em cache será utilizado nas próximas requisições.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Sobrescrever o método { #override-the-method }
Agora, você pode substituir o método `.openapi()` pela sua nova função.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Verificar { #check-it }
Uma vez que você acessar [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc), verá que está usando seu logo personalizado (neste exemplo, o logo do **FastAPI**):
================================================
FILE: docs/pt/docs/how-to/general.md
================================================
# Geral - Como Fazer - Receitas { #general-how-to-recipes }
Aqui estão vários links para outros locais na documentação, para perguntas gerais ou frequentes.
## Filtro de dados- Segurança { #filter-data-security }
Para assegurar que você não vai retornar mais dados do que deveria, leia a documentação de [Tutorial - Modelo de Resposta - Tipo de Retorno](../tutorial/response-model.md).
## Otimizar Desempenho da Resposta - Modelo de Resposta - Tipo de Retorno { #optimize-response-performance-response-model-return-type }
Para otimizar o desempenho ao retornar dados JSON, use um tipo de retorno ou modelo de resposta; assim, o Pydantic fará a serialização para JSON no lado do Rust, sem passar pelo Python. Leia mais na documentação de [Tutorial - Modelo de Resposta - Tipo de Retorno](../tutorial/response-model.md).
## Tags de Documentação - OpenAPI { #documentation-tags-openapi }
Para adicionar tags às suas *operações de rota* e agrupá-las na UI da documentação, leia a documentação de [Tutorial - Configurações da Operação de Rota - Tags](../tutorial/path-operation-configuration.md#tags).
## Resumo e Descrição da documentação - OpenAPI { #documentation-summary-and-description-openapi }
Para adicionar um resumo e uma descrição às suas *operações de rota* e exibi-los na UI da documentação, leia a documentação de [Tutorial - Configurações da Operação de Rota - Resumo e Descrição](../tutorial/path-operation-configuration.md#summary-and-description).
## Documentação - Descrição da Resposta - OpenAPI { #documentation-response-description-openapi }
Para definir a descrição de uma resposta exibida na interface da documentação, leia a documentação de [Tutorial - Configurações da Operação de Rota - Descrição da Resposta](../tutorial/path-operation-configuration.md#response-description).
## Documentação - Descontinuar uma *Operação de Rota* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Para descontinuar uma *operação de rota* e exibi-la na UI da documentação, leia a documentação de [Tutorial - Configurações da Operação de Rota - Descontinuação](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Converter qualquer dado para compatível com JSON { #convert-any-data-to-json-compatible }
Para converter qualquer dado para um formato compatível com JSON, leia a documentação de [Tutorial - Codificador Compatível com JSON](../tutorial/encoder.md).
## OpenAPI Metadata - Docs { #openapi-metadata-docs }
Para adicionar metadados ao seu esquema OpenAPI, incluindo licença, versão, contato, etc, leia a documentação de [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md).
## OpenAPI com URL customizada { #openapi-custom-url }
Para customizar a URL do OpenAPI (ou removê-la), leia a documentação de [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md#openapi-url).
## URLs de documentação do OpenAPI { #openapi-docs-urls }
Para alterar as URLs usadas para as interfaces de usuário da documentação gerada automaticamente, leia a documentação de [Tutorial - Metadados e URLs da Documentação](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/pt/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Como o **FastAPI** é baseado no padrão **ASGI**, é muito fácil integrar qualquer biblioteca **GraphQL** também compatível com ASGI.
Você pode combinar *operações de rota* normais do FastAPI com GraphQL na mesma aplicação.
/// tip | Dica
**GraphQL** resolve alguns casos de uso muito específicos.
Ele tem **vantagens** e **desvantagens** quando comparado a **web APIs** comuns.
Certifique-se de avaliar se os **benefícios** para o seu caso de uso compensam as **desvantagens**. 🤓
///
## Bibliotecas GraphQL { #graphql-libraries }
Aqui estão algumas das bibliotecas **GraphQL** que têm suporte **ASGI**. Você pode usá-las com **FastAPI**:
* [Strawberry](https://strawberry.rocks/) 🍓
* Com [documentação para FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* Com [documentação para FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* Com [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) para fornecer integração ASGI
* [Graphene](https://graphene-python.org/)
* Com [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL com Strawberry { #graphql-with-strawberry }
Se você precisar ou quiser trabalhar com **GraphQL**, [**Strawberry**](https://strawberry.rocks/) é a biblioteca **recomendada** pois tem o design mais próximo ao design do **FastAPI**, ela é toda baseada em **anotações de tipo**.
Dependendo do seu caso de uso, você pode preferir usar uma biblioteca diferente, mas se você me perguntasse, eu provavelmente sugeriria que você experimentasse o **Strawberry**.
Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Você pode aprender mais sobre Strawberry na [documentação do Strawberry](https://strawberry.rocks/).
E também na documentação sobre [Strawberry com FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Antigo `GraphQLApp` do Starlette { #older-graphqlapp-from-starlette }
Versões anteriores do Starlette incluiam uma classe `GraphQLApp` para integrar com [Graphene](https://graphene-python.org/).
Ela foi descontinuada do Starlette, mas se você tem código que a utilizava, você pode facilmente **migrar** para [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), que cobre o mesmo caso de uso e tem uma **interface quase idêntica**.
/// tip | Dica
Se você precisa de GraphQL, eu ainda recomendaria que você desse uma olhada no [Strawberry](https://strawberry.rocks/), pois ele é baseado em anotações de tipo em vez de classes e tipos personalizados.
///
## Saiba Mais { #learn-more }
Você pode aprender mais sobre **GraphQL** na [documentação oficial do GraphQL](https://graphql.org/).
Você também pode ler mais sobre cada uma das bibliotecas descritas acima em seus links.
================================================
FILE: docs/pt/docs/how-to/index.md
================================================
# Como Fazer - Receitas { #how-to-recipes }
Aqui você encontrará diferentes exemplos práticos ou tutoriais de "como fazer" para **vários tópicos**.
A maioria dessas ideias será mais ou menos **independente**, e na maioria dos casos você deveria estudá-las apenas se elas se aplicarem diretamente ao **seu projeto**.
Se algo parecer interessante e útil para o seu projeto, vá em frente e dê uma olhada. Caso contrário, você pode simplesmente ignorá-las.
/// tip | Dica
Se você deseja **aprender FastAPI** de forma estruturada (recomendado), leia capítulo por capítulo [Tutorial - Guia de Usuário](../tutorial/index.md) em vez disso.
///
================================================
FILE: docs/pt/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Migrar do Pydantic v1 para o Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Se você tem uma aplicação FastAPI antiga, pode estar usando o Pydantic versão 1.
O FastAPI versão 0.100.0 tinha suporte ao Pydantic v1 ou v2. Ele usaria aquele que você tivesse instalado.
O FastAPI versão 0.119.0 introduziu suporte parcial ao Pydantic v1 a partir de dentro do Pydantic v2 (como `pydantic.v1`), para facilitar a migração para o v2.
O FastAPI 0.126.0 removeu o suporte ao Pydantic v1, enquanto ainda oferece suporte a `pydantic.v1` por mais algum tempo.
/// warning | Atenção
A equipe do Pydantic interrompeu o suporte ao Pydantic v1 para as versões mais recentes do Python, a partir do **Python 3.14**.
Isso inclui `pydantic.v1`, que não é mais suportado no Python 3.14 e superiores.
Se quiser usar as funcionalidades mais recentes do Python, você precisará garantir que usa o Pydantic v2.
///
Se você tem uma aplicação FastAPI antiga com Pydantic v1, aqui vou mostrar como migrá-la para o Pydantic v2, e as **funcionalidades no FastAPI 0.119.0** para ajudar em uma migração gradual.
## Guia oficial { #official-guide }
O Pydantic tem um [Guia de Migração](https://docs.pydantic.dev/latest/migration/) oficial do v1 para o v2.
Ele também inclui o que mudou, como as validações agora são mais corretas e rigorosas, possíveis ressalvas, etc.
Você pode lê-lo para entender melhor o que mudou.
## Testes { #tests }
Garanta que você tenha [testes](../tutorial/testing.md) para sua aplicação e que os execute na integração contínua (CI).
Assim, você pode fazer a atualização e garantir que tudo continua funcionando como esperado.
## `bump-pydantic` { #bump-pydantic }
Em muitos casos, quando você usa modelos Pydantic regulares sem personalizações, será possível automatizar a maior parte do processo de migração do Pydantic v1 para o Pydantic v2.
Você pode usar [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) da própria equipe do Pydantic.
Essa ferramenta ajuda a alterar automaticamente a maior parte do código que precisa ser modificado.
Depois disso, você pode rodar os testes e verificar se tudo funciona. Se funcionar, está concluído. 😎
## Pydantic v1 no v2 { #pydantic-v1-in-v2 }
O Pydantic v2 inclui tudo do Pydantic v1 como um submódulo `pydantic.v1`. Mas isso não é mais suportado em versões acima do Python 3.13.
Isso significa que você pode instalar a versão mais recente do Pydantic v2 e importar e usar os componentes antigos do Pydantic v1 a partir desse submódulo, como se tivesse o Pydantic v1 antigo instalado.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Suporte do FastAPI ao Pydantic v1 no v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Desde o FastAPI 0.119.0, há também suporte parcial ao Pydantic v1 a partir de dentro do Pydantic v2, para facilitar a migração para o v2.
Assim, você pode atualizar o Pydantic para a versão 2 mais recente e alterar os imports para usar o submódulo `pydantic.v1`, e em muitos casos tudo simplesmente funcionará.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Atenção
Tenha em mente que, como a equipe do Pydantic não oferece mais suporte ao Pydantic v1 nas versões recentes do Python, a partir do Python 3.14, o uso de `pydantic.v1` também não é suportado no Python 3.14 e superiores.
///
### Pydantic v1 e v2 na mesma aplicação { #pydantic-v1-and-v2-on-the-same-app }
Não é **suportado** pelo Pydantic ter um modelo do Pydantic v2 com campos próprios definidos como modelos do Pydantic v1, ou vice-versa.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...mas, você pode ter modelos separados usando Pydantic v1 e v2 na mesma aplicação.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
Em alguns casos, é até possível ter modelos Pydantic v1 e v2 na mesma **operação de rota** na sua aplicação FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
No exemplo acima, o modelo de entrada é um modelo Pydantic v1, e o modelo de saída (definido em `response_model=ItemV2`) é um modelo Pydantic v2.
### Parâmetros do Pydantic v1 { #pydantic-v1-parameters }
Se você precisar usar algumas das ferramentas específicas do FastAPI para parâmetros como `Body`, `Query`, `Form` etc. com modelos do Pydantic v1, pode importá-las de `fastapi.temp_pydantic_v1_params` enquanto conclui a migração para o Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Migre em etapas { #migrate-in-steps }
/// tip | Dica
Primeiro tente com o `bump-pydantic`, se seus testes passarem e isso funcionar, então você concluiu tudo com um único comando. ✨
///
Se o `bump-pydantic` não funcionar para o seu caso, você pode usar o suporte a modelos Pydantic v1 e v2 na mesma aplicação para fazer a migração para o Pydantic v2 gradualmente.
Você poderia primeiro atualizar o Pydantic para usar a versão 2 mais recente e alterar os imports para usar `pydantic.v1` para todos os seus modelos.
Depois, você pode começar a migrar seus modelos do Pydantic v1 para o v2 em grupos, em etapas graduais. 🚶
================================================
FILE: docs/pt/docs/how-to/separate-openapi-schemas.md
================================================
# Esquemas OpenAPI Separados para Entrada e Saída ou Não { #separate-openapi-schemas-for-input-and-output-or-not }
Desde que o **Pydantic v2** foi lançado, o OpenAPI gerado é um pouco mais exato e **correto** do que antes. 😎
De fato, em alguns casos, ele terá até **dois JSON Schemas** no OpenAPI para o mesmo modelo Pydantic, para entrada e saída, dependendo se eles possuem **valores padrão**.
Vamos ver como isso funciona e como alterar se for necessário.
## Modelos Pydantic para Entrada e Saída { #pydantic-models-for-input-and-output }
Digamos que você tenha um modelo Pydantic com valores padrão, como este:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Modelo para Entrada { #model-for-input }
Se você usar esse modelo como entrada, como aqui:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
... então o campo `description` **não será obrigatório**. Porque ele tem um valor padrão de `None`.
### Modelo de Entrada na Documentação { #input-model-in-docs }
Você pode confirmar que na documentação, o campo `description` não tem um **asterisco vermelho**, não é marcado como obrigatório:
### Modelo para Saída { #model-for-output }
Mas se você usar o mesmo modelo como saída, como aqui:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
... então, como `description` tem um valor padrão, se você **não retornar nada** para esse campo, ele ainda terá o **valor padrão**.
### Modelo para Dados de Resposta de Saída { #model-for-output-response-data }
Se você interagir com a documentação e verificar a resposta, mesmo que o código não tenha adicionado nada em um dos campos `description`, a resposta JSON contém o valor padrão (`null`):
Isso significa que ele **sempre terá um valor**, só que às vezes o valor pode ser `None` (ou `null` em termos de JSON).
Isso quer dizer que, os clientes que usam sua API não precisam verificar se o valor existe ou não, eles podem **assumir que o campo sempre estará lá**, mas que em alguns casos terá o valor padrão de `None`.
A maneira de descrever isso no OpenAPI é marcar esse campo como **obrigatório**, porque ele sempre estará lá.
Por causa disso, o JSON Schema para um modelo pode ser diferente dependendo se ele é usado para **entrada ou saída**:
* para **entrada**, o `description` **não será obrigatório**
* para **saída**, ele será **obrigatório** (e possivelmente `None`, ou em termos de JSON, `null`)
### Modelo para Saída na Documentação { #model-for-output-in-docs }
Você pode verificar o modelo de saída na documentação também, **ambos** `name` e `description` são marcados como **obrigatórios** com um **asterisco vermelho**:
### Modelo para Entrada e Saída na Documentação { #model-for-input-and-output-in-docs }
E se você verificar todos os Schemas disponíveis (JSON Schemas) no OpenAPI, verá que há dois, um `Item-Input` e um `Item-Output`.
Para `Item-Input`, `description` **não é obrigatório**, não tem um asterisco vermelho.
Mas para `Item-Output`, `description` **é obrigatório**, tem um asterisco vermelho.
Com esse recurso do **Pydantic v2**, sua documentação da API fica mais **precisa**, e se você tiver clientes e SDKs gerados automaticamente, eles serão mais precisos também, proporcionando uma melhor **experiência para desenvolvedores** e consistência. 🎉
## Não Separe Schemas { #do-not-separate-schemas }
Agora, há alguns casos em que você pode querer ter o **mesmo esquema para entrada e saída**.
Provavelmente, o principal caso de uso para isso é se você já tem algum código de cliente/SDK gerado automaticamente e não quer atualizar todo o código de cliente/SDK gerado ainda, você provavelmente vai querer fazer isso em algum momento, mas talvez não agora.
Nesse caso, você pode desativar esse recurso no **FastAPI**, com o parâmetro `separate_input_output_schemas=False`.
/// info | Informação
O suporte para `separate_input_output_schemas` foi adicionado no FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Mesmo Esquema para Modelos de Entrada e Saída na Documentação { #same-schema-for-input-and-output-models-in-docs }
E agora haverá um único esquema para entrada e saída para o modelo, apenas `Item`, e ele terá `description` como **não obrigatório**:
================================================
FILE: docs/pt/docs/how-to/testing-database.md
================================================
# Testando a Base de Dados { #testing-a-database }
Você pode estudar sobre bases de dados, SQL e SQLModel na [documentação de SQLModel](https://sqlmodel.tiangolo.com/). 🤓
Aqui tem um mini [tutorial de como usar SQLModel com FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
Esse tutorial inclui uma seção sobre [testar bases de dados SQL](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/pt/docs/index.md
================================================
# FastAPI { #fastapi }
Framework FastAPI, alta performance, fácil de aprender, rápido para codar, pronto para produção
---
**Documentação**: [https://fastapi.tiangolo.com/pt](https://fastapi.tiangolo.com/pt)
**Código fonte**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI é um framework web moderno e rápido (alta performance) para construção de APIs com Python, baseado nos type hints padrões do Python.
Os recursos chave são:
* **Rápido**: alta performance, equivalente a **NodeJS** e **Go** (graças ao Starlette e Pydantic). [Um dos frameworks Python mais rápidos disponíveis](#performance).
* **Rápido para codar**: Aumenta a velocidade para desenvolver recursos entre 200% a 300%. *
* **Poucos bugs**: Reduz cerca de 40% de erros induzidos por humanos (desenvolvedores). *
* **Intuitivo**: Grande suporte a editores. Completação em todos os lugares. Menos tempo debugando.
* **Fácil**: Projetado para ser fácil de aprender e usar. Menos tempo lendo docs.
* **Enxuto**: Minimize duplicação de código. Múltiplas funcionalidades para cada declaração de parâmetro. Menos bugs.
* **Robusto**: Tenha código pronto para produção. E com documentação interativa automática.
* **Baseado em padrões**: Baseado em (e totalmente compatível com) os padrões abertos para APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (anteriormente conhecido como Swagger) e [JSON Schema](https://json-schema.org/).
* estimativas baseadas em testes realizados com equipe interna de desenvolvimento, construindo aplicações em produção.
## Patrocinadores { #sponsors }
### Patrocinador Keystone { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Patrocinadores Ouro e Prata { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Outros patrocinadores](https://fastapi.tiangolo.com/pt/fastapi-people/#sponsors)
## Opiniões { #opinions }
"_[...] Estou usando **FastAPI** muito esses dias. [...] Estou na verdade planejando utilizar ele em todos os times de **serviços ML na Microsoft**. Alguns deles estão sendo integrados no _core_ do produto **Windows** e alguns produtos **Office**._"
---
"_Nós adotamos a biblioteca **FastAPI** para iniciar um servidor **REST** que pode ser consultado para obter **previsões**. [para o Ludwig]_"
Piero Molino, Yaroslav Dudin, e Sai Sumanth Miryala - Uber(ref)
---
"_A **Netflix** tem o prazer de anunciar o lançamento open-source do nosso framework de orquestração de **gerenciamento de crises**: **Dispatch**! [criado com **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_Estou muito entusiasmado com o **FastAPI**. É tão divertido!_"
Brian Okken - [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855) apresentador do podcast(ref)
---
"_Honestamente, o que você construiu parece super sólido e refinado. De muitas formas, é o que eu queria que o **Hug** fosse - é realmente inspirador ver alguém construir isso._"
Timothy Crosley - criador do [Hug](https://github.com/hugapi/hug)(ref)
---
"_Se você está procurando aprender um **framework moderno** para construir APIs REST, dê uma olhada no **FastAPI** [...] É rápido, fácil de usar e fácil de aprender [...]_"
"_Nós trocamos nossas **APIs** por **FastAPI** [...] Acredito que você gostará dele [...]_"
Ines Montani - Matthew Honnibal - fundadores da [Explosion AI](https://explosion.ai) - criadores da [spaCy](https://spacy.io)(ref) - (ref)
---
"_Se alguém estiver procurando construir uma API Python para produção, eu recomendaria fortemente o **FastAPI**. Ele é **lindamente projetado**, **simples de usar** e **altamente escalável**, e se tornou um **componente chave** para a nossa estratégia de desenvolvimento API first, impulsionando diversas automações e serviços, como o nosso Virtual TAC Engineer._"
---
## Mini documentário do FastAPI { #fastapi-mini-documentary }
Há um [mini documentário do FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE) lançado no fim de 2025, você pode assisti-lo online:
## **Typer**, o FastAPI das interfaces de linhas de comando { #typer-the-fastapi-of-clis }
Se você estiver construindo uma aplicação CLI para ser utilizada no terminal ao invés de uma API web, dê uma olhada no [**Typer**](https://typer.tiangolo.com/).
**Typer** é o irmão menor do FastAPI. E seu propósito é ser o **FastAPI das CLIs**. ⌨️ 🚀
## Requisitos { #requirements }
FastAPI está nos ombros de gigantes:
* [Starlette](https://www.starlette.dev/) para as partes web.
* [Pydantic](https://docs.pydantic.dev/) para a parte de dados.
## Instalação { #installation }
Crie e ative um [ambiente virtual](https://fastapi.tiangolo.com/pt/virtual-environments/) e então instale o FastAPI:
**Nota**: Certifique-se de que você colocou `"fastapi[standard]"` com aspas, para garantir que funcione em todos os terminais.
## Exemplo { #example }
### Crie { #create-it }
Crie um 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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Ou use async def...
Se seu código utiliza `async` / `await`, use `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Nota**:
Se você não sabe, verifique a seção _"Com pressa?"_ sobre [`async` e `await` nas docs](https://fastapi.tiangolo.com/pt/async/#in-a-hurry).
### Rode { #run-it }
Rode o servidor com:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
Sobre o comando fastapi dev...
O comando `fastapi dev` lê automaticamente o seu arquivo `main.py`, detecta a aplicação **FastAPI** nele e inicia um servidor usando o [Uvicorn](https://www.uvicorn.dev).
Por padrão, o `fastapi dev` iniciará com auto-reload habilitado para desenvolvimento local.
Você pode ler mais sobre isso na [documentação do FastAPI CLI](https://fastapi.tiangolo.com/pt/fastapi-cli/).
### Verifique { #check-it }
Abra seu navegador em [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Você verá a resposta JSON como:
```JSON
{"item_id": 5, "q": "somequery"}
```
Você acabou de criar uma API que:
* Recebe requisições HTTP nos _paths_ `/` e `/items/{item_id}`.
* Ambos _paths_ fazem operações `GET` (também conhecido como _métodos_ HTTP).
* O _path_ `/items/{item_id}` tem um _parâmetro de path_ `item_id` que deve ser um `int`.
* O _path_ `/items/{item_id}` tem um _parâmetro query_ `q` `str` opcional.
### Documentação Interativa da API { #interactive-api-docs }
Agora vá para [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá a documentação automática interativa da API (fornecida por [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Documentação Alternativa da API { #alternative-api-docs }
E agora, vá para [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Você verá a documentação automática alternativa (fornecida por [ReDoc](https://github.com/Rebilly/ReDoc)):

## Evoluindo o Exemplo { #example-upgrade }
Agora modifique o arquivo `main.py` para receber um corpo de uma requisição `PUT`.
Declare o corpo utilizando tipos padrão Python, graças ao Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
O servidor `fastapi dev` deverá recarregar automaticamente.
### Evoluindo a Documentação Interativa da API { #interactive-api-docs-upgrade }
Agora vá para [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* A documentação interativa da API será automaticamente atualizada, incluindo o novo corpo:

* Clique no botão "Try it out", ele permitirá que você preencha os parâmetros e interaja diretamente com a API:

* Então clique no botão "Execute", a interface do usuário irá se comunicar com a API, enviar os parâmetros, pegar os resultados e mostrá-los na tela:

### Evoluindo a Documentação Alternativa da API { #alternative-api-docs-upgrade }
E agora, vá para [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* A documentação alternativa também irá refletir o novo parâmetro query e o corpo:

### Recapitulando { #recap }
Resumindo, você declara **uma vez** os tipos dos parâmetros, corpo etc. como parâmetros de função.
Você faz isso com os tipos padrão do Python moderno.
Você não terá que aprender uma nova sintaxe, os métodos ou classes de uma biblioteca específica etc.
Apenas **Python** padrão.
Por exemplo, para um `int`:
```Python
item_id: int
```
ou para um modelo mais complexo, `Item`:
```Python
item: Item
```
...e com essa única declaração você tem:
* Suporte ao Editor, incluindo:
* Completação.
* Verificação de tipos.
* Validação de dados:
* Erros automáticos e claros quando o dado é inválido.
* Validação até para objetos JSON profundamente aninhados.
* Conversão de dados de entrada: vindo da rede para dados e tipos Python. Consegue ler:
* JSON.
* Parâmetros de path.
* Parâmetros query.
* Cookies.
* Cabeçalhos.
* Formulários.
* Arquivos.
* Conversão de dados de saída: convertendo de tipos e dados Python para dados de rede (como JSON):
* Converte tipos Python (`str`, `int`, `float`, `bool`, `list` etc).
* Objetos `datetime`.
* Objetos `UUID`.
* Modelos de Banco de Dados.
* ...e muito mais.
* Documentação interativa automática da API, incluindo 2 alternativas de interface de usuário:
* Swagger UI.
* ReDoc.
---
Voltando ao código do exemplo anterior, **FastAPI** irá:
* Validar que existe um `item_id` no path para requisições `GET` e `PUT`.
* Validar que `item_id` é do tipo `int` para requisições `GET` e `PUT`.
* Se não for, o cliente verá um erro útil e claro.
* Verificar se existe um parâmetro query opcional nomeado como `q` (como em `http://127.0.0.1:8000/items/foo?q=somequery`) para requisições `GET`.
* Como o parâmetro `q` é declarado com `= None`, ele é opcional.
* Sem o `None` ele seria obrigatório (como o corpo no caso de `PUT`).
* Para requisições `PUT` para `/items/{item_id}`, lerá o corpo como JSON:
* Verifica que tem um atributo obrigatório `name` que deve ser `str`.
* Verifica que tem um atributo obrigatório `price` que tem que ser um `float`.
* Verifica que tem um atributo opcional `is_offer`, que deve ser um `bool`, se presente.
* Tudo isso também funcionaria para objetos JSON profundamente aninhados.
* Converter de e para JSON automaticamente.
* Documentar tudo com OpenAPI, que poderá ser usado por:
* Sistemas de documentação interativos.
* Sistemas de clientes de geração de código automáticos, para muitas linguagens.
* Fornecer diretamente 2 interfaces web de documentação interativa.
---
Nós apenas arranhamos a superfície, mas você já tem ideia de como tudo funciona.
Experimente mudar a seguinte linha:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...de:
```Python
... "item_name": item.name ...
```
...para:
```Python
... "item_price": item.price ...
```
...e veja como seu editor irá auto-completar os atributos e saberá os tipos:

Para um exemplo mais completo incluindo mais recursos, veja o Tutorial - Guia do Usuário.
**Alerta de Spoiler**: o tutorial - guia do usuário inclui:
* Declaração de **parâmetros** de diferentes lugares como: **cabeçalhos**, **cookies**, **campos de formulários** e **arquivos**.
* Como configurar **limitações de validação** como `maximum_length` ou `regex`.
* Um poderoso e fácil de usar sistema de **Injeção de Dependência**.
* Segurança e autenticação, incluindo suporte para **OAuth2** com autenticação com **JWT tokens** e **HTTP Basic**.
* Técnicas mais avançadas (mas igualmente fáceis) para declaração de **modelos JSON profundamente aninhados** (graças ao Pydantic).
* Integrações **GraphQL** com o [Strawberry](https://strawberry.rocks) e outras bibliotecas.
* Muitos recursos extras (graças ao Starlette) como:
* **WebSockets**
* testes extremamente fáceis baseados em HTTPX e `pytest`
* **CORS**
* **Cookie Sessions**
* ...e mais.
### Implemente sua aplicação (opcional) { #deploy-your-app-optional }
Você pode opcionalmente implantar sua aplicação FastAPI na [FastAPI Cloud](https://fastapicloud.com), vá e entre na lista de espera se ainda não o fez. 🚀
Se você já tem uma conta na **FastAPI Cloud** (nós convidamos você da lista de espera 😉), pode implantar sua aplicação com um único comando.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
É isso! Agora você pode acessar sua aplicação nesse URL. ✨
#### Sobre a FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** é construída pelo mesmo autor e equipe por trás do **FastAPI**.
Ela simplifica o processo de **construir**, **implantar** e **acessar** uma API com esforço mínimo.
Traz a mesma **experiência do desenvolvedor** de construir aplicações com FastAPI para **implantá-las** na nuvem. 🎉
A FastAPI Cloud é a principal patrocinadora e financiadora dos projetos open source do ecossistema *FastAPI and friends*. ✨
#### Implante em outros provedores de nuvem { #deploy-to-other-cloud-providers }
FastAPI é open source e baseado em padrões. Você pode implantar aplicações FastAPI em qualquer provedor de nuvem que escolher.
Siga os tutoriais do seu provedor de nuvem para implantar aplicações FastAPI com eles. 🤓
## Performance { #performance }
Testes de performance independentes do TechEmpower mostram aplicações **FastAPI** rodando sob Uvicorn como [um dos frameworks Python mais rápidos disponíveis](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), somente atrás de Starlette e Uvicorn (utilizados internamente pelo FastAPI). (*)
Para entender mais sobre isso, veja a seção [Comparações](https://fastapi.tiangolo.com/pt/benchmarks/).
## Dependências { #dependencies }
O FastAPI depende do Pydantic e do Starlette.
### Dependências `standard` { #standard-dependencies }
Quando você instala o FastAPI com `pip install "fastapi[standard]"`, ele vem com o grupo `standard` de dependências opcionais:
Utilizado pelo Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - para validação de email.
Utilizado pelo Starlette:
* [`httpx`](https://www.python-httpx.org) - Obrigatório caso você queira utilizar o `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - Obrigatório se você quer utilizar a configuração padrão de templates.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - Obrigatório se você deseja suporte a "parsing" de formulário, com `request.form()`.
Utilizado pelo FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) - para o servidor que carrega e serve a sua aplicação. Isto inclui `uvicorn[standard]`, que inclui algumas dependências (e.g. `uvloop`) necessárias para servir em alta performance.
* `fastapi-cli[standard]` - que disponibiliza o comando `fastapi`.
* Isso inclui `fastapi-cloud-cli`, que permite implantar sua aplicação FastAPI na [FastAPI Cloud](https://fastapicloud.com).
### Sem as dependências `standard` { #without-standard-dependencies }
Se você não deseja incluir as dependências opcionais `standard`, você pode instalar utilizando `pip install fastapi` ao invés de `pip install "fastapi[standard]"`.
### Sem o `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Se você quiser instalar o FastAPI com as dependências padrão, mas sem o `fastapi-cloud-cli`, você pode instalar com `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Dependências opcionais adicionais { #additional-optional-dependencies }
Existem algumas dependências adicionais que você pode querer instalar.
Dependências opcionais adicionais do Pydantic:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - para gerenciamento de configurações.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - para tipos extras a serem utilizados com o Pydantic.
Dependências opcionais adicionais do FastAPI:
* [`orjson`](https://github.com/ijl/orjson) - Obrigatório se você deseja utilizar o `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - Obrigatório se você deseja utilizar o `UJSONResponse`.
## Licença { #license }
Esse projeto é licenciado sob os termos da licença MIT.
================================================
FILE: docs/pt/docs/learn/index.md
================================================
# Aprender { #learn }
Aqui estão as seções introdutórias e os tutoriais para aprender o **FastAPI**.
Pode considerar isto um **livro**, um **curso**, a forma **oficial** e recomendada de aprender o FastAPI. 😎
================================================
FILE: docs/pt/docs/project-generation.md
================================================
# Full Stack FastAPI Template { #full-stack-fastapi-template }
_Templates_, embora tipicamente venham com alguma configuração específica, são desenhados para serem flexíveis e customizáveis. Isso permite que você os modifique e adapte para as especificações do seu projeto, fazendo-os um excelente ponto de partida. 🏁
Você pode usar esse _template_ para começar, já que ele inclui várias configurações iniciais, segurança, banco de dados, e alguns _endpoints_ de API já feitos para você.
Repositório GitHub: [Full Stack FastAPI Template](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI Template - Pilha de Tecnologias e Recursos { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/pt) para a API do backend em Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) para as interações do Python com bancos de dados SQL (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), usado pelo FastAPI, para validação de dados e gerenciamento de configurações.
- 💾 [PostgreSQL](https://www.postgresql.org) como banco de dados SQL.
- 🚀 [React](https://react.dev) para o frontend.
- 💃 Usando TypeScript, hooks, Vite, e outras partes de uma _stack_ frontend moderna.
- 🎨 [Tailwind CSS](https://tailwindcss.com) e [shadcn/ui](https://ui.shadcn.com) para os componentes de frontend.
- 🤖 Um cliente frontend automaticamente gerado.
- 🧪 [Playwright](https://playwright.dev) para testes Ponta-a-Ponta.
- 🦇 Suporte para modo escuro.
- 🐋 [Docker Compose](https://www.docker.com) para desenvolvimento e produção.
- 🔒 Hash seguro de senhas por padrão.
- 🔑 Autenticação JWT (JSON Web Token).
- 📫 Recuperação de senhas baseada em email.
- ✅ Testes com [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) como proxy reverso / balanceador de carga.
- 🚢 Instruções de deployment usando Docker Compose, incluindo como configurar um proxy frontend com Traefik para gerenciar automaticamente certificados HTTPS.
- 🏭 CI (Integração Contínua) e CD (_Deploy_ Contínuo) baseado em GitHub Actions.
================================================
FILE: docs/pt/docs/python-types.md
================================================
# Introdução aos tipos Python { #python-types-intro }
O Python possui suporte para "type hints" opcionais (também chamados de "type annotations").
Esses **"type hints"** ou anotações são uma sintaxe especial que permite declarar o tipo de uma variável.
Ao declarar tipos para suas variáveis, editores e ferramentas podem oferecer um melhor suporte.
Este é apenas um **tutorial rápido / atualização** sobre type hints do Python. Ele cobre apenas o mínimo necessário para usá-los com o **FastAPI**... que é realmente muito pouco.
O **FastAPI** é todo baseado nesses type hints, eles oferecem muitas vantagens e benefícios.
Mas mesmo que você nunca use o **FastAPI**, você se beneficiaria de aprender um pouco sobre eles.
/// note | Nota
Se você é um especialista em Python e já sabe tudo sobre type hints, pule para o próximo capítulo.
///
## Motivação { #motivation }
Vamos começar com um exemplo simples:
{* ../../docs_src/python_types/tutorial001_py310.py *}
A chamada deste programa gera:
```
John Doe
```
A função faz o seguinte:
* Pega um `first_name` e `last_name`.
* Converte a primeira letra de cada uma em maiúsculas com `title()`.
* Concatena com um espaço no meio.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Edite-o { #edit-it }
É um programa muito simples.
Mas agora imagine que você estava escrevendo do zero.
Em algum momento você teria iniciado a definição da função, já tinha os parâmetros prontos...
Mas então você deve chamar "esse método que converte a primeira letra em maiúscula".
Era `upper`? Era `uppercase`? `first_uppercase`? `capitalize`?
Em seguida, tente com o velho amigo do programador, o preenchimento automático do editor.
Você digita o primeiro parâmetro da função, `first_name`, depois um ponto (`.`) e, em seguida, pressiona `Ctrl+Space` para acionar o preenchimento automático.
Mas, infelizmente, você não obtém nada útil:
### Adicionar tipos { #add-types }
Vamos modificar uma única linha da versão anterior.
Vamos mudar exatamente esse fragmento, os parâmetros da função, de:
```Python
first_name, last_name
```
para:
```Python
first_name: str, last_name: str
```
É isso aí.
Esses são os "type hints":
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Isso não é o mesmo que declarar valores padrão como seria com:
```Python
first_name="john", last_name="doe"
```
É uma coisa diferente.
Estamos usando dois pontos (`:`), não sinal de igual (`=`).
E adicionar type hints normalmente não muda o que acontece do que aconteceria sem eles.
Mas agora, imagine que você está novamente no meio da criação dessa função, mas com type hints.
No mesmo ponto, você tenta acionar o preenchimento automático com o `Ctrl+Space` e vê:
Com isso, você pode rolar, vendo as opções, até encontrar o que "soa familiar":
## Mais motivação { #more-motivation }
Verifique esta função, ela já possui type hints:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Como o editor conhece os tipos das variáveis, você não obtém apenas o preenchimento automático, mas também as verificações de erro:
Agora você sabe que precisa corrigi-la, convertendo `age` em uma string com `str(age)`:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Declarando tipos { #declaring-types }
Você acabou de ver o local principal para declarar type hints. Como parâmetros de função.
Este também é o principal local em que você os usaria com o **FastAPI**.
### Tipos simples { #simple-types }
Você pode declarar todos os tipos padrão de Python, não apenas `str`.
Você pode usar, por exemplo:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Módulo `typing` { #typing-module }
Para alguns casos adicionais, você pode precisar importar alguns itens do módulo padrão `typing`, por exemplo, quando quiser declarar que algo pode ter "qualquer tipo", você pode usar `Any` de `typing`:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Tipos genéricos { #generic-types }
Alguns tipos podem receber "parâmetros de tipo" entre colchetes, para definir seus tipos internos, por exemplo, uma "lista de strings" seria declarada como `list[str]`.
Esses tipos que podem receber parâmetros de tipo são chamados **tipos genéricos** ou **genéricos**.
Você pode usar os mesmos tipos internos como genéricos (com colchetes e tipos dentro):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
Por exemplo, vamos definir uma variável para ser uma `list` de `str`.
Declare a variável, com a mesma sintaxe com dois pontos (`:`).
Como o tipo, coloque `list`.
Como a lista é um tipo que contém tipos internos, você os coloca entre colchetes:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Informação
Esses tipos internos dentro dos colchetes são chamados de "parâmetros de tipo".
Neste caso, `str` é o parâmetro de tipo passado para `list`.
///
Isso significa: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`".
Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da lista:
Sem tipos, isso é quase impossível de alcançar.
Observe que a variável `item` é um dos elementos da lista `items`.
E, ainda assim, o editor sabe que é um `str` e fornece suporte para isso.
#### Tuple e Set { #tuple-and-set }
Você faria o mesmo para declarar `tuple`s e `set`s:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Isso significa:
* A variável `items_t` é uma `tuple` com 3 itens, um `int`, outro `int` e uma `str`.
* A variável `items_s` é um `set`, e cada um de seus itens é do tipo `bytes`.
#### Dict { #dict }
Para definir um `dict`, você passa 2 parâmetros de tipo, separados por vírgulas.
O primeiro parâmetro de tipo é para as chaves do `dict`.
O segundo parâmetro de tipo é para os valores do `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Isso significa que:
* A variável `prices` é um `dict`:
* As chaves deste `dict` são do tipo `str` (digamos, o nome de cada item).
* Os valores deste `dict` são do tipo `float` (digamos, o preço de cada item).
#### Union { #union }
Você pode declarar que uma variável pode ser de qualquer um dentre **vários tipos**, por exemplo, um `int` ou um `str`.
Para defini-la, você usa a barra vertical (`|`) para separar ambos os tipos.
Isso é chamado de "união", porque a variável pode ser qualquer coisa na união desses dois conjuntos de tipos.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Isso significa que `item` pode ser um `int` ou um `str`.
#### Possivelmente `None` { #possibly-none }
Você pode declarar que um valor pode ter um tipo, como `str`, mas que ele também pode ser `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Usar `str | None` em vez de apenas `str` permitirá que o editor o ajude a detectar erros em que você poderia estar assumindo que um valor é sempre um `str`, quando na verdade ele também pode ser `None`.
### Classes como tipos { #classes-as-types }
Você também pode declarar uma classe como o tipo de uma variável.
Digamos que você tenha uma classe `Person`, com um nome:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Então você pode declarar que uma variável é do tipo `Person`:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
E então, novamente, você recebe todo o suporte do editor:
Perceba que isso significa que "`one_person` é uma **instância** da classe `Person`".
Isso não significa que "`one_person` é a **classe** chamada `Person`".
## Modelos Pydantic { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) é uma biblioteca Python para executar a validação de dados.
Você declara a "forma" dos dados como classes com atributos.
E cada atributo tem um tipo.
Em seguida, você cria uma instância dessa classe com alguns valores e ela os validará, os converterá para o tipo apropriado (se for esse o caso) e fornecerá um objeto com todos os dados.
E você recebe todo o suporte do editor com esse objeto resultante.
Um exemplo da documentação oficial do Pydantic:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Informação
Para saber mais sobre o [Pydantic, verifique a documentação](https://docs.pydantic.dev/).
///
O **FastAPI** é todo baseado em Pydantic.
Você verá muito mais disso na prática no [Tutorial - Guia do usuário](tutorial/index.md).
## Type Hints com Metadados de Anotações { #type-hints-with-metadata-annotations }
O Python também possui uma funcionalidade que permite incluir **metadados adicionais** nesses type hints utilizando `Annotated`.
Você pode importar `Annotated` de `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
O Python em si não faz nada com este `Annotated`. E para editores e outras ferramentas, o tipo ainda é `str`.
Mas você pode utilizar este espaço dentro do `Annotated` para fornecer ao **FastAPI** metadados adicionais sobre como você deseja que a sua aplicação se comporte.
O importante aqui de se lembrar é que **o primeiro *type parameter*** que você informar ao `Annotated` é o **tipo de fato**. O resto é apenas metadado para outras ferramentas.
Por hora, você precisa apenas saber que o `Annotated` existe, e que ele é Python padrão. 😎
Mais tarde você verá o quão **poderoso** ele pode ser.
/// tip | Dica
O fato de que isso é **Python padrão** significa que você ainda obtém a **melhor experiência de desenvolvedor possível** no seu editor, com as ferramentas que você utiliza para analisar e refatorar o seu código, etc. ✨
E também que o seu código será muito compatível com diversas outras ferramentas e bibliotecas Python. 🚀
///
## Type hints no **FastAPI** { #type-hints-in-fastapi }
O **FastAPI** aproveita esses type hints para fazer várias coisas.
Com o **FastAPI**, você declara parâmetros com type hints e obtém:
* **Suporte ao editor**.
* **Verificações de tipo**.
... e o **FastAPI** usa as mesmas declarações para:
* **Definir requisitos**: dos parâmetros de path da request, parâmetros da query, cabeçalhos, corpos, dependências, etc.
* **Converter dados**: da request para o tipo necessário.
* **Validar dados**: provenientes de cada request:
* Gerando **erros automáticos** retornados ao cliente quando os dados são inválidos.
* **Documentar** a API usando OpenAPI:
* que é usada pelas interfaces de usuário da documentação interativa automática.
Tudo isso pode parecer abstrato. Não se preocupe. Você verá tudo isso em ação no [Tutorial - Guia do usuário](tutorial/index.md).
O importante é que, usando tipos padrão de Python, em um único local (em vez de adicionar mais classes, decoradores, etc.), o **FastAPI** fará muito trabalho para você.
/// info | Informação
Se você já passou por todo o tutorial e voltou para ver mais sobre os tipos, um bom recurso é [a "cheat sheet" do `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/pt/docs/resources/index.md
================================================
# Recursos { #resources }
Material complementar, links externos e mais. ✈️
================================================
FILE: docs/pt/docs/translation-banner.md
================================================
/// details | 🌐 Tradução por IA e humanos
Esta tradução foi feita por IA orientada por humanos. 🤝
Ela pode conter erros de interpretação do significado original ou soar pouco natural, etc. 🤖
Você pode melhorar esta tradução [ajudando-nos a orientar melhor o LLM de IA](https://fastapi.tiangolo.com/pt/contributing/#translations).
[Versão em inglês](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/pt/docs/tutorial/background-tasks.md
================================================
# Tarefas em segundo plano { #background-tasks }
Você pode definir tarefas em segundo plano para serem executadas *após* retornar uma resposta.
Isso é útil para operações que precisam acontecer após uma request, mas que o cliente não precisa realmente esperar a operação terminar antes de receber a resposta.
Isso inclui, por exemplo:
* Notificações por e-mail enviadas após realizar uma ação:
* Como conectar-se a um servidor de e-mail e enviar um e-mail tende a ser “lento” (vários segundos), você pode retornar a resposta imediatamente e enviar a notificação por e-mail em segundo plano.
* Processamento de dados:
* Por exemplo, digamos que você receba um arquivo que precisa passar por um processo lento; você pode retornar uma resposta “Accepted” (HTTP 202) e processar o arquivo em segundo plano.
## Usando `BackgroundTasks` { #using-backgroundtasks }
Primeiro, importe `BackgroundTasks` e defina um parâmetro na sua *função de operação de rota* com uma declaração de tipo `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
O **FastAPI** criará o objeto do tipo `BackgroundTasks` para você e o passará como esse parâmetro.
## Crie uma função de tarefa { #create-a-task-function }
Crie uma função para ser executada como a tarefa em segundo plano.
É apenas uma função padrão que pode receber parâmetros.
Pode ser uma função `async def` ou um `def` normal, o **FastAPI** saberá como lidar com isso corretamente.
Neste caso, a função da tarefa escreverá em um arquivo (simulando o envio de um e-mail).
E como a operação de escrita não usa `async` e `await`, definimos a função com um `def` normal:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Adicione a tarefa em segundo plano { #add-the-background-task }
Dentro da sua *função de operação de rota*, passe sua função de tarefa para o objeto de *tarefas em segundo plano* com o método `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
O `.add_task()` recebe como argumentos:
* Uma função de tarefa a ser executada em segundo plano (`write_notification`).
* Qualquer sequência de argumentos que deve ser passada para a função de tarefa na ordem (`email`).
* Quaisquer argumentos nomeados que devem ser passados para a função de tarefa (`message="some notification"`).
## Injeção de dependências { #dependency-injection }
Usar `BackgroundTasks` também funciona com o sistema de injeção de dependências; você pode declarar um parâmetro do tipo `BackgroundTasks` em vários níveis: em uma *função de operação de rota*, em uma dependência (dependable), em uma subdependência, etc.
O **FastAPI** sabe o que fazer em cada caso e como reutilizar o mesmo objeto, de forma que todas as tarefas em segundo plano sejam combinadas e executadas em segundo plano depois:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
Neste exemplo, as mensagens serão escritas no arquivo `log.txt` *após* o envio da resposta.
Se houver uma query na request, ela será registrada em uma tarefa em segundo plano.
E então outra tarefa em segundo plano gerada na *função de operação de rota* escreverá uma mensagem usando o parâmetro de path `email`.
## Detalhes técnicos { #technical-details }
A classe `BackgroundTasks` vem diretamente de [`starlette.background`](https://www.starlette.dev/background/).
Ela é importada/incluída diretamente no FastAPI para que você possa importá-la de `fastapi` e evitar importar acidentalmente a alternativa `BackgroundTask` (sem o `s` no final) de `starlette.background`.
Usando apenas `BackgroundTasks` (e não `BackgroundTask`), é possível usá-la como um parâmetro de *função de operação de rota* e deixar o **FastAPI** cuidar do resto para você, assim como ao usar o objeto `Request` diretamente.
Ainda é possível usar `BackgroundTask` sozinho no FastAPI, mas você precisa criar o objeto no seu código e retornar uma `Response` da Starlette incluindo-o.
Você pode ver mais detalhes na [documentação oficial da Starlette para tarefas em segundo plano](https://www.starlette.dev/background/).
## Ressalva { #caveat }
Se você precisar realizar computação pesada em segundo plano e não necessariamente precisar que seja executada pelo mesmo processo (por exemplo, você não precisa compartilhar memória, variáveis, etc.), pode se beneficiar do uso de outras ferramentas maiores, como o [Celery](https://docs.celeryq.dev).
Elas tendem a exigir configurações mais complexas, um gerenciador de fila de mensagens/tarefas, como RabbitMQ ou Redis, mas permitem executar tarefas em segundo plano em vários processos e, especialmente, em vários servidores.
Mas se você precisa acessar variáveis e objetos da mesma aplicação **FastAPI**, ou precisa realizar pequenas tarefas em segundo plano (como enviar uma notificação por e-mail), você pode simplesmente usar `BackgroundTasks`.
## Recapitulando { #recap }
Importe e use `BackgroundTasks` com parâmetros em *funções de operação de rota* e dependências para adicionar tarefas em segundo plano.
================================================
FILE: docs/pt/docs/tutorial/bigger-applications.md
================================================
# Aplicações Maiores - Múltiplos Arquivos { #bigger-applications-multiple-files }
Se você está construindo uma aplicação ou uma API web, é raro que você possa colocar tudo em um único arquivo.
**FastAPI** oferece uma ferramenta conveniente para estruturar sua aplicação, mantendo toda a flexibilidade.
/// info | Informação
Se você vem do Flask, isso seria o equivalente aos Blueprints do Flask.
///
## Um exemplo de estrutura de arquivos { #an-example-file-structure }
Digamos que você tenha uma estrutura de arquivos como esta:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Dica
Existem vários arquivos `__init__.py`: um em cada diretório ou subdiretório.
Isso permite a importação de código de um arquivo para outro.
Por exemplo, no arquivo `app/main.py`, você poderia ter uma linha como:
```
from app.routers import items
```
///
* O diretório `app` contém tudo. E possui um arquivo vazio `app/__init__.py`, então ele é um "pacote Python" (uma coleção de "módulos Python"): `app`.
* Ele contém um arquivo `app/main.py`. Como está dentro de um pacote Python (um diretório com um arquivo `__init__.py`), ele é um "módulo" desse pacote: `app.main`.
* Existe também um arquivo `app/dependencies.py`, assim como `app/main.py`, ele é um "módulo": `app.dependencies`.
* Há um subdiretório `app/routers/` com outro arquivo `__init__.py`, então ele é um "subpacote Python": `app.routers`.
* O arquivo `app/routers/items.py` está dentro de um pacote, `app/routers/`, portanto é um submódulo: `app.routers.items`.
* O mesmo com `app/routers/users.py`, ele é outro submódulo: `app.routers.users`.
* Há também um subdiretório `app/internal/` com outro arquivo `__init__.py`, então ele é outro "subpacote Python": `app.internal`.
* E o arquivo `app/internal/admin.py` é outro submódulo: `app.internal.admin`.
A mesma estrutura de arquivos com comentários:
```bash
.
├── app # "app" é um pacote Python
│ ├── __init__.py # este arquivo torna "app" um "pacote Python"
│ ├── main.py # módulo "main", p.ex., import app.main
│ ├── dependencies.py # módulo "dependencies", p.ex., import app.dependencies
│ └── routers # "routers" é um "subpacote Python"
│ │ ├── __init__.py # torna "routers" um "subpacote Python"
│ │ ├── items.py # submódulo "items", p.ex., import app.routers.items
│ │ └── users.py # submódulo "users", p.ex., import app.routers.users
│ └── internal # "internal" é um "subpacote Python"
│ ├── __init__.py # torna "internal" um "subpacote Python"
│ └── admin.py # submódulo "admin", p.ex., import app.internal.admin
```
## `APIRouter` { #apirouter }
Vamos supor que o arquivo dedicado a lidar apenas com usuários seja o submódulo em `/app/routers/users.py`.
Você quer manter as *operações de rota* relacionadas aos seus usuários separadas do restante do código, para mantê-lo organizado.
Mas ele ainda faz parte da mesma aplicação/web API **FastAPI** (faz parte do mesmo "pacote Python").
Você pode criar as *operações de rota* para esse módulo usando o `APIRouter`.
### Importe `APIRouter` { #import-apirouter }
Você o importa e cria uma "instância" da mesma maneira que faria com a classe `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Operações de Rota* com `APIRouter` { #path-operations-with-apirouter }
E então você o utiliza para declarar suas *operações de rota*.
Utilize-o da mesma maneira que utilizaria a classe `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Você pode pensar em `APIRouter` como uma classe "mini `FastAPI`".
Todas as mesmas opções são suportadas.
Todos os mesmos `parameters`, `responses`, `dependencies`, `tags`, etc.
/// tip | Dica
Neste exemplo, a variável é chamada de `router`, mas você pode nomeá-la como quiser.
///
Vamos incluir este `APIRouter` na aplicação principal `FastAPI`, mas primeiro, vamos verificar as dependências e outro `APIRouter`.
## Dependências { #dependencies }
Vemos que precisaremos de algumas dependências usadas em vários lugares da aplicação.
Então, as colocamos em seu próprio módulo de `dependencies` (`app/dependencies.py`).
Agora usaremos uma dependência simples para ler um cabeçalho `X-Token` personalizado:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Dica
Estamos usando um cabeçalho inventado para simplificar este exemplo.
Mas em casos reais, você obterá melhores resultados usando os [Utilitários de Segurança](security/index.md) integrados.
///
## Outro módulo com `APIRouter` { #another-module-with-apirouter }
Digamos que você também tenha os endpoints dedicados a manipular "itens" do seu aplicativo no módulo em `app/routers/items.py`.
Você tem *operações de rota* para:
* `/items/`
* `/items/{item_id}`
É tudo a mesma estrutura de `app/routers/users.py`.
Mas queremos ser mais inteligentes e simplificar um pouco o código.
Sabemos que todas as *operações de rota* neste módulo têm o mesmo:
* Path `prefix`: `/items`.
* `tags`: (apenas uma tag: `items`).
* Extra `responses`.
* `dependencies`: todas elas precisam da dependência `X-Token` que criamos.
Então, em vez de adicionar tudo isso a cada *operação de rota*, podemos adicioná-lo ao `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Como o path de cada *operação de rota* tem que começar com `/`, como em:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...o prefixo não deve incluir um `/` final.
Então, o prefixo neste caso é `/items`.
Também podemos adicionar uma list de `tags` e `responses` extras que serão aplicadas a todas as *operações de rota* incluídas neste router.
E podemos adicionar uma list de `dependencies` que serão adicionadas a todas as *operações de rota* no router e serão executadas/resolvidas para cada request feita a elas.
/// tip | Dica
Observe que, assim como [dependências em *decoradores de operação de rota*](dependencies/dependencies-in-path-operation-decorators.md), nenhum valor será passado para sua *função de operação de rota*.
///
O resultado final é que os paths dos itens agora são:
* `/items/`
* `/items/{item_id}`
...como pretendíamos.
* Elas serão marcadas com uma lista de tags que contêm uma única string `"items"`.
* Essas "tags" são especialmente úteis para os sistemas de documentação interativa automática (usando OpenAPI).
* Todas elas incluirão as `responses` predefinidas.
* Todas essas *operações de rota* terão a list de `dependencies` avaliada/executada antes delas.
* Se você também declarar dependências em uma *operação de rota* específica, **elas também serão executadas**.
* As dependências do router são executadas primeiro, depois as [`dependencies` no decorador](dependencies/dependencies-in-path-operation-decorators.md) e, em seguida, as dependências de parâmetros normais.
* Você também pode adicionar [dependências de `Segurança` com `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Dica
Ter `dependencies` no `APIRouter` pode ser usado, por exemplo, para exigir autenticação para um grupo inteiro de *operações de rota*. Mesmo que as dependências não sejam adicionadas individualmente a cada uma delas.
///
/// check | Verifique
Os parâmetros `prefix`, `tags`, `responses` e `dependencies` são (como em muitos outros casos) apenas um recurso do **FastAPI** para ajudar a evitar duplicação de código.
///
### Importe as dependências { #import-the-dependencies }
Este código reside no módulo `app.routers.items`, o arquivo `app/routers/items.py`.
E precisamos obter a função de dependência do módulo `app.dependencies`, o arquivo `app/dependencies.py`.
Então usamos uma importação relativa com `..` para as dependências:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Como funcionam as importações relativas { #how-relative-imports-work }
/// tip | Dica
Se você sabe perfeitamente como funcionam as importações, continue para a próxima seção abaixo.
///
Um único ponto `.`, como em:
```Python
from .dependencies import get_token_header
```
significaria:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)...
* encontre o módulo `dependencies` (um arquivo imaginário em `app/routers/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Mas esse arquivo não existe, nossas dependências estão em um arquivo em `app/dependencies.py`.
Lembre-se de como nossa estrutura app/file se parece:
---
Os dois pontos `..`, como em:
```Python
from ..dependencies import get_token_header
```
significa:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)...
* vá para o pacote pai (o diretório `app/`)...
* e lá, encontre o módulo `dependencies` (o arquivo em `app/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Isso funciona corretamente! 🎉
---
Da mesma forma, se tivéssemos usado três pontos `...`, como em:
```Python
from ...dependencies import get_token_header
```
isso significaria:
* Começando no mesmo pacote em que este módulo (o arquivo `app/routers/items.py`) vive (o diretório `app/routers/`)...
* vá para o pacote pai (o diretório `app/`)...
* então vá para o pai daquele pacote (não há pacote pai, `app` é o nível superior 😱)...
* e lá, encontre o módulo `dependencies` (o arquivo em `app/dependencies.py`)...
* e dele, importe a função `get_token_header`.
Isso se referiria a algum pacote acima de `app/`, com seu próprio arquivo `__init__.py`, etc. Mas não temos isso. Então, isso geraria um erro em nosso exemplo. 🚨
Mas agora você sabe como funciona, então você pode usar importações relativas em seus próprios aplicativos, não importa o quão complexos eles sejam. 🤓
### Adicione algumas `tags`, `responses` e `dependencies` personalizadas { #add-some-custom-tags-responses-and-dependencies }
Não estamos adicionando o prefixo `/items` nem `tags=["items"]` a cada *operação de rota* porque os adicionamos ao `APIRouter`.
Mas ainda podemos adicionar _mais_ `tags` que serão aplicadas a uma *operação de rota* específica, e também algumas `responses` extras específicas para essa *operação de rota*:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Dica
Esta última operação de rota terá a combinação de tags: `["items", "custom"]`.
E também terá ambas as responses na documentação, uma para `404` e uma para `403`.
///
## O principal `FastAPI` { #the-main-fastapi }
Agora, vamos ver o módulo em `app/main.py`.
Aqui é onde você importa e usa a classe `FastAPI`.
Este será o arquivo principal em seu aplicativo que une tudo.
E como a maior parte de sua lógica agora viverá em seu próprio módulo específico, o arquivo principal será bem simples.
### Importe o `FastAPI` { #import-fastapi }
Você importa e cria uma classe `FastAPI` normalmente.
E podemos até declarar [dependências globais](dependencies/global-dependencies.md) que serão combinadas com as dependências para cada `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Importe o `APIRouter` { #import-the-apirouter }
Agora importamos os outros submódulos que possuem `APIRouter`s:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Como os arquivos `app/routers/users.py` e `app/routers/items.py` são submódulos que fazem parte do mesmo pacote Python `app`, podemos usar um único ponto `.` para importá-los usando "importações relativas".
### Como funciona a importação { #how-the-importing-works }
A seção:
```Python
from .routers import items, users
```
significa:
* Começando no mesmo pacote em que este módulo (o arquivo `app/main.py`) vive (o diretório `app/`)...
* procure o subpacote `routers` (o diretório em `app/routers/`)...
* e dele, importe o submódulo `items` (o arquivo em `app/routers/items.py`) e `users` (o arquivo em `app/routers/users.py`)...
O módulo `items` terá uma variável `router` (`items.router`). Esta é a mesma que criamos no arquivo `app/routers/items.py`, é um objeto `APIRouter`.
E então fazemos o mesmo para o módulo `users`.
Também poderíamos importá-los como:
```Python
from app.routers import items, users
```
/// info | Informação
A primeira versão é uma "importação relativa":
```Python
from .routers import items, users
```
A segunda versão é uma "importação absoluta":
```Python
from app.routers import items, users
```
Para saber mais sobre pacotes e módulos Python, leia [a documentação oficial do Python sobre módulos](https://docs.python.org/3/tutorial/modules.html).
///
### Evite colisões de nomes { #avoid-name-collisions }
Estamos importando o submódulo `items` diretamente, em vez de importar apenas sua variável `router`.
Isso ocorre porque também temos outra variável chamada `router` no submódulo `users`.
Se tivéssemos importado um após o outro, como:
```Python
from .routers.items import router
from .routers.users import router
```
o `router` de `users` sobrescreveria o de `items` e não poderíamos usá-los ao mesmo tempo.
Então, para poder usar ambos no mesmo arquivo, importamos os submódulos diretamente:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Inclua os `APIRouter`s para `users` e `items` { #include-the-apirouters-for-users-and-items }
Agora, vamos incluir os `router`s dos submódulos `users` e `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Informação
`users.router` contém o `APIRouter` dentro do arquivo `app/routers/users.py`.
E `items.router` contém o `APIRouter` dentro do arquivo `app/routers/items.py`.
///
Com `app.include_router()` podemos adicionar cada `APIRouter` ao aplicativo principal `FastAPI`.
Ele incluirá todas as rotas daquele router como parte dele.
/// note | Detalhes Técnicos
Na verdade, ele criará internamente uma *operação de rota* para cada *operação de rota* que foi declarada no `APIRouter`.
Então, nos bastidores, ele realmente funcionará como se tudo fosse o mesmo aplicativo único.
///
/// check | Verifique
Você não precisa se preocupar com desempenho ao incluir routers.
Isso levará microssegundos e só acontecerá na inicialização.
Então não afetará o desempenho. ⚡
///
### Inclua um `APIRouter` com um `prefix`, `tags`, `responses` e `dependencies` personalizados { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Agora, vamos imaginar que sua organização lhe deu o arquivo `app/internal/admin.py`.
Ele contém um `APIRouter` com algumas *operações de rota* de administração que sua organização compartilha entre vários projetos.
Para este exemplo, será super simples. Mas digamos que, como ele é compartilhado com outros projetos na organização, não podemos modificá-lo e adicionar um `prefix`, `dependencies`, `tags`, etc. diretamente ao `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Mas ainda queremos definir um `prefix` personalizado ao incluir o `APIRouter` para que todas as suas *operações de rota* comecem com `/admin`, queremos protegê-lo com as `dependencies` que já temos para este projeto e queremos incluir `tags` e `responses`.
Podemos declarar tudo isso sem precisar modificar o `APIRouter` original passando esses parâmetros para `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Dessa forma, o `APIRouter` original permanecerá inalterado, para que possamos compartilhar o mesmo arquivo `app/internal/admin.py` com outros projetos na organização.
O resultado é que em nosso aplicativo, cada uma das *operações de rota* do módulo `admin` terá:
* O prefixo `/admin`.
* A tag `admin`.
* A dependência `get_token_header`.
* A resposta `418`. 🍵
Mas isso afetará apenas o `APIRouter` em nosso aplicativo, e não em nenhum outro código que o utilize.
Assim, por exemplo, outros projetos poderiam usar o mesmo `APIRouter` com um método de autenticação diferente.
### Inclua uma *operação de rota* { #include-a-path-operation }
Também podemos adicionar *operações de rota* diretamente ao aplicativo `FastAPI`.
Aqui fazemos isso... só para mostrar que podemos 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
e funcionará corretamente, junto com todas as outras *operações de rota* adicionadas com `app.include_router()`.
/// note | Detalhes Técnicos Avançados
**Nota**: este é um detalhe muito técnico que você provavelmente pode **simplesmente pular**.
---
Os `APIRouter`s não são "montados", eles não são isolados do resto do aplicativo.
Isso ocorre porque queremos incluir suas *operações de rota* no esquema OpenAPI e nas interfaces de usuário.
Como não podemos simplesmente isolá-los e "montá-los" independentemente do resto, as *operações de rota* são "clonadas" (recriadas), não incluídas diretamente.
///
## Configure o `entrypoint` em `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
Como seu objeto `app` do FastAPI fica em `app/main.py`, você pode configurar o `entrypoint` no seu arquivo `pyproject.toml` assim:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
isso é equivalente a importar como:
```python
from app.main import app
```
Dessa forma o comando `fastapi` saberá onde encontrar sua aplicação.
/// Note | Nota
Você também poderia passar o path para o comando, como:
```console
$ fastapi dev app/main.py
```
Mas você teria que lembrar de passar o path correto toda vez que chamar o comando `fastapi`.
Além disso, outras ferramentas podem não conseguir encontrá-la, por exemplo a [Extensão do VS Code](../editor-support.md) ou a [FastAPI Cloud](https://fastapicloud.com), portanto é recomendável usar o `entrypoint` em `pyproject.toml`.
///
## Verifique a documentação automática da API { #check-the-automatic-api-docs }
Agora, execute sua aplicação:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
E abra a documentação em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá a documentação automática da API, incluindo os paths de todos os submódulos, usando os paths (e prefixos) corretos e as tags corretas:
## Inclua o mesmo router várias vezes com `prefix` diferentes { #include-the-same-router-multiple-times-with-different-prefix }
Você também pode usar `.include_router()` várias vezes com o *mesmo* router usando prefixos diferentes.
Isso pode ser útil, por exemplo, para expor a mesma API sob prefixos diferentes, por exemplo, `/api/v1` e `/api/latest`.
Esse é um uso avançado que você pode não precisar, mas está lá caso precise.
## Inclua um `APIRouter` em outro { #include-an-apirouter-in-another }
Da mesma forma que você pode incluir um `APIRouter` em uma aplicação `FastAPI`, você pode incluir um `APIRouter` em outro `APIRouter` usando:
```Python
router.include_router(other_router)
```
Certifique-se de fazer isso antes de incluir `router` na aplicação `FastAPI`, para que as *operações de rota* de `other_router` também sejam incluídas.
================================================
FILE: docs/pt/docs/tutorial/body-fields.md
================================================
# Corpo - Campos { #body-fields }
Da mesma forma que você pode declarar validações adicionais e metadados nos parâmetros de uma *função de operação de rota* com `Query`, `Path` e `Body`, você pode declarar validações e metadados dentro de modelos do Pydantic usando `Field` do Pydantic.
## Importe `Field` { #import-field }
Primeiro, você tem que importá-lo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Atenção
Note que `Field` é importado diretamente do `pydantic`, não do `fastapi` como todo o resto (`Query`, `Path`, `Body`, etc).
///
## Declare atributos do modelo { #declare-model-attributes }
Você pode então utilizar `Field` com atributos do modelo:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` funciona da mesma forma que `Query`, `Path` e `Body`, ele possui todos os mesmos parâmetros, etc.
/// note | Detalhes Técnicos
Na realidade, `Query`, `Path` e outros que você verá em seguida, criam objetos de subclasses de uma classe `Param` comum, que é ela mesma uma subclasse da classe `FieldInfo` do Pydantic.
E `Field` do Pydantic retorna uma instância de `FieldInfo` também.
`Body` também retorna objetos de uma subclasse de `FieldInfo` diretamente. E tem outras que você verá mais tarde que são subclasses da classe `Body`.
Lembre-se que quando você importa `Query`, `Path`, e outros de `fastapi`, esse são na realidade funções que retornam classes especiais.
///
/// tip | Dica
Note como cada atributo do modelo com um tipo, valor padrão e `Field` possuem a mesma estrutura que parâmetros de *funções de operações de rota*, com `Field` ao invés de `Path`, `Query` e `Body`.
///
## Adicione informações extras { #add-extra-information }
Você pode declarar informação extra em `Field`, `Query`, `Body`, etc. E isso será incluído no JSON Schema gerado.
Você irá aprender mais sobre adicionar informações extras posteriormente nessa documentação, quando estiver aprendendo a declarar exemplos.
/// warning | Atenção
Chaves extras passadas para `Field` também estarão presentes no schema OpenAPI resultante da sua aplicação.
Como essas chaves podem não fazer necessariamente parte da especificação OpenAPI, algumas ferramentas de OpenAPI, por exemplo [o validador do OpenAPI](https://validator.swagger.io/), podem não funcionar com o schema gerado.
///
## Recapitulando { #recap }
Você pode usar `Field` do Pydantic para declarar validações extras e metadados para atributos do modelo.
Você também pode usar os argumentos de palavras-chave extras para passar metadados do JSON Schema adicionais.
================================================
FILE: docs/pt/docs/tutorial/body-multiple-params.md
================================================
# Corpo - Múltiplos parâmetros { #body-multiple-parameters }
Agora que nós vimos como usar `Path` e `Query`, veremos usos mais avançados de declarações no corpo da requisição.
## Misture `Path`, `Query` e parâmetros de corpo { #mix-path-query-and-body-parameters }
Primeiro, é claro, você pode misturar `Path`, `Query` e declarações de parâmetro no corpo da requisição livremente e o **FastAPI** saberá o que fazer.
E você também pode declarar parâmetros de corpo como opcionais, definindo o valor padrão com `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Nota
Repare que, neste caso, o `item` que seria capturado a partir do corpo é opcional. Visto que ele possui `None` como valor padrão.
///
## Múltiplos parâmetros de corpo { #multiple-body-parameters }
No exemplo anterior, as *operações de rota* esperariam um JSON no corpo contendo os atributos de um `Item`, exemplo:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Mas você pode também declarar múltiplos parâmetros de corpo, por exemplo, `item` e `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
Neste caso, o **FastAPI** perceberá que existe mais de um parâmetro de corpo na função (dois parâmetros que são modelos Pydantic).
Então, ele usará o nome dos parâmetros como chaves (nome dos campos) no corpo, e espera um corpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Nota
Repare que mesmo que o `item` esteja declarado da mesma maneira que antes, agora é esperado que ele esteja dentro do corpo com uma chave `item`.
///
O **FastAPI** fará a conversão automática a partir da requisição, assim esse parâmetro `item` receberá seu respectivo conteúdo e o mesmo ocorrerá com `user`.
Ele executará a validação dos dados compostos e irá documentá-los de maneira compatível com o esquema OpenAPI e documentação automática.
## Valores singulares no corpo { #singular-values-in-body }
Assim como existem uma `Query` e uma `Path` para definir dados adicionais para parâmetros de consulta e de rota, o **FastAPI** provê o equivalente para `Body`.
Por exemplo, extendendo o modelo anterior, você poder decidir por ter uma outra chave `importance` no mesmo corpo, além de `item` e `user`.
Se você declará-lo como é, porque é um valor singular, o **FastAPI** assumirá que se trata de um parâmetro de consulta.
Mas você pode instruir o **FastAPI** para tratá-lo como outra chave do corpo usando `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
Neste caso, o **FastAPI** esperará um corpo como:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Mais uma vez, ele converterá os tipos de dados, validar, documentar, etc.
## Múltiplos parâmetros de corpo e consulta { #multiple-body-params-and-query }
Obviamente, você também pode declarar parâmetros de consulta assim que você precisar, de modo adicional a quaisquer parâmetros de corpo.
Dado que, por padrão, valores singulares são interpretados como parâmetros de consulta, você não precisa explicitamente adicionar uma `Query`, você pode somente:
```Python
q: str | None = None
```
Por exemplo:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Informação
`Body` também possui todas as validações adicionais e metadados de parâmetros como em `Query`,`Path` e outras que você verá depois.
///
## Declare um único parâmetro de corpo indicando sua chave { #embed-a-single-body-parameter }
Suponha que você tem um único parâmetro de corpo `item`, a partir de um modelo Pydantic `Item`.
Por padrão, o **FastAPI** esperará que seu conteúdo venha no corpo diretamente.
Mas se você quiser que ele espere por um JSON com uma chave `item` e dentro dele os conteúdos do modelo, como ocorre ao declarar vários parâmetros de corpo, você pode usar o parâmetro especial de `Body` chamado `embed`:
```Python
item: Item = Body(embed=True)
```
como em:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
Neste caso o **FastAPI** esperará um corpo como:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
ao invés de:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Recapitulando { #recap }
Você pode adicionar múltiplos parâmetros de corpo para sua *função de operação de rota*, mesmo que a requisição possa ter somente um único corpo.
E o **FastAPI** vai manipulá-los, mandar para você os dados corretos na sua função, e validar e documentar o schema correto na *operação de rota*.
Você também pode declarar valores singulares para serem recebidos como parte do corpo.
E você pode instruir o **FastAPI** para requisitar no corpo a indicação de chave mesmo quando existe somente um único parâmetro declarado.
================================================
FILE: docs/pt/docs/tutorial/body-nested-models.md
================================================
# Corpo - Modelos aninhados { #body-nested-models }
Com o **FastAPI**, você pode definir, validar, documentar e usar modelos arbitrariamente e profundamente aninhados (graças ao Pydantic).
## Campos do tipo Lista { #list-fields }
Você pode definir um atributo como um subtipo. Por exemplo, uma `list` do Python:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Isso fará com que tags seja uma lista de itens mesmo sem declarar o tipo dos elementos desta lista.
## Campos do tipo Lista com um parâmetro de tipo { #list-fields-with-type-parameter }
Mas o Python tem uma maneira específica de declarar listas com tipos internos ou "parâmetros de tipo":
### Declare uma `list` com um parâmetro de tipo { #declare-a-list-with-a-type-parameter }
Para declarar tipos que têm parâmetros de tipo (tipos internos), como `list`, `dict`, `tuple`,
passe o(s) tipo(s) interno(s) como "parâmetros de tipo" usando colchetes: `[` e `]`
```Python
my_list: list[str]
```
Essa é a sintaxe padrão do Python para declarações de tipo.
Use a mesma sintaxe padrão para atributos de modelo com tipos internos.
Portanto, em nosso exemplo, podemos fazer com que `tags` sejam especificamente uma "lista de strings":
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Tipos "set" { #set-types }
Mas então, quando nós pensamos mais, percebemos que as tags não devem se repetir, elas provavelmente devem ser strings únicas.
E que o Python tem um tipo de dados especial para conjuntos de itens únicos, o `set`.
Então podemos declarar `tags` como um conjunto de strings:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Com isso, mesmo que você receba uma requisição contendo dados duplicados, ela será convertida em um conjunto de itens exclusivos.
E sempre que você enviar esses dados como resposta, mesmo se a fonte tiver duplicatas, eles serão gerados como um conjunto de itens exclusivos.
E também teremos anotações/documentação em conformidade.
## Modelos aninhados { #nested-models }
Cada atributo de um modelo Pydantic tem um tipo.
Mas esse tipo pode ser outro modelo Pydantic.
Portanto, você pode declarar "objects" JSON profundamente aninhados com nomes, tipos e validações de atributos específicos.
Tudo isso, aninhado arbitrariamente.
### Defina um sub-modelo { #define-a-submodel }
Por exemplo, nós podemos definir um modelo `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Use o sub-modelo como um tipo { #use-the-submodel-as-a-type }
E então podemos usá-lo como o tipo de um atributo:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Isso significa que o **FastAPI** vai esperar um corpo similar à:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Novamente, apenas fazendo essa declaração, com o **FastAPI**, você ganha:
* Suporte do editor (preenchimento automático, etc.), inclusive para modelos aninhados
* Conversão de dados
* Validação de dados
* Documentação automática
## Tipos especiais e validação { #special-types-and-validation }
Além dos tipos singulares normais como `str`, `int`, `float`, etc. Você também pode usar tipos singulares mais complexos que herdam de `str`.
Para ver todas as opções possíveis, consulte a [Visão geral dos tipos do Pydantic](https://docs.pydantic.dev/latest/concepts/types/). Você verá alguns exemplos no próximo capítulo.
Por exemplo, no modelo `Image` nós temos um campo `url`, nós podemos declará-lo como um `HttpUrl` do Pydantic invés de como uma `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
A string será verificada para se tornar uma URL válida e documentada no JSON Schema / OpenAPI como tal.
## Atributos como listas de submodelos { #attributes-with-lists-of-submodels }
Você também pode usar modelos Pydantic como subtipos de `list`, `set`, etc:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Isso vai esperar (converter, validar, documentar, etc) um corpo JSON tal qual:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Informação
Observe como a chave `images` agora tem uma lista de objetos de imagem.
///
## Modelos profundamente aninhados { #deeply-nested-models }
Você pode definir modelos profundamente aninhados de forma arbitrária:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Informação
Observe como `Offer` tem uma lista de `Item`s, que por sua vez têm uma lista opcional de `Image`s
///
## Corpos de listas puras { #bodies-of-pure-lists }
Se o valor de primeiro nível do corpo JSON que você espera for um `array` do JSON (uma` lista` do Python), você pode declarar o tipo no parâmetro da função, da mesma forma que nos modelos do Pydantic:
```Python
images: list[Image]
```
como em:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Suporte de editor em todo canto { #editor-support-everywhere }
E você obtém suporte do editor em todos os lugares.
Mesmo para itens dentro de listas:
Você não conseguiria este tipo de suporte de editor se estivesse trabalhando diretamente com `dict` em vez de modelos Pydantic.
Mas você também não precisa se preocupar com eles, os dicts de entrada são convertidos automaticamente e sua saída é convertida automaticamente para JSON também.
## Corpos de `dict`s arbitrários { #bodies-of-arbitrary-dicts }
Você também pode declarar um corpo como um `dict` com chaves de algum tipo e valores de outro tipo.
Sem ter que saber de antemão quais são os nomes de campos/atributos válidos (como seria o caso dos modelos Pydantic).
Isso seria útil se você deseja receber chaves que ainda não conhece.
---
Outro caso útil é quando você deseja ter chaves de outro tipo, por exemplo, `int`.
É isso que vamos ver aqui.
Neste caso, você aceitaria qualquer `dict`, desde que tenha chaves` int` com valores `float`:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Dica
Leve em consideração que o JSON só suporta `str` como chaves.
Mas o Pydantic tem conversão automática de dados.
Isso significa que, embora os clientes da API só possam enviar strings como chaves, desde que essas strings contenham inteiros puros, o Pydantic irá convertê-los e validá-los.
E o `dict` que você recebe como `weights` terá, na verdade, chaves `int` e valores` float`.
///
## Recapitulação { #recap }
Com **FastAPI** você tem a flexibilidade máxima fornecida pelos modelos Pydantic, enquanto seu código é mantido simples, curto e elegante.
Mas com todos os benefícios:
* Suporte do editor (preenchimento automático em todo canto!)
* Conversão de dados (parsing/serialização)
* Validação de dados
* Documentação dos esquemas
* Documentação automática
================================================
FILE: docs/pt/docs/tutorial/body-updates.md
================================================
# Corpo - Atualizações { #body-updates }
## Atualização substituindo com `PUT` { #update-replacing-with-put }
Para atualizar um item, você pode usar a operação [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT).
Você pode usar `jsonable_encoder` para converter os dados de entrada em dados que podem ser armazenados como JSON (por exemplo, com um banco de dados NoSQL). Por exemplo, convertendo `datetime` em `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` é usado para receber dados que devem substituir os dados existentes.
### Aviso sobre a substituição { #warning-about-replacing }
Isso significa que, se você quiser atualizar o item `bar` usando `PUT` com um corpo contendo:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
como ele não inclui o atributo já armazenado `"tax": 20.2`, o modelo de entrada assumiria o valor padrão de `"tax": 10.5`.
E os dados seriam salvos com esse "novo" `tax` de `10.5`.
## Atualizações parciais com `PATCH` { #partial-updates-with-patch }
Você também pode usar a operação [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) para atualizar dados *parcialmente*.
Isso significa que você pode enviar apenas os dados que deseja atualizar, deixando o restante intacto.
/// note | Nota
`PATCH` é menos comumente usado e conhecido do que `PUT`.
E muitas equipes usam apenas `PUT`, mesmo para atualizações parciais.
Você é **livre** para usá-los como preferir, **FastAPI** não impõe restrições.
Mas este guia mostra, mais ou menos, como eles são destinados a serem usados.
///
### Usando o parâmetro `exclude_unset` do Pydantic { #using-pydantics-exclude-unset-parameter }
Se você quiser receber atualizações parciais, é muito útil usar o parâmetro `exclude_unset` no `.model_dump()` do modelo do Pydantic.
Como `item.model_dump(exclude_unset=True)`.
Isso geraria um `dict` com apenas os dados que foram definidos ao criar o modelo `item`, excluindo os valores padrão.
Então, você pode usar isso para gerar um `dict` com apenas os dados definidos (enviados na solicitação), omitindo valores padrão:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Usando o parâmetro `update` do Pydantic { #using-pydantics-update-parameter }
Agora, você pode criar uma cópia do modelo existente usando `.model_copy()`, e passar o parâmetro `update` com um `dict` contendo os dados para atualizar.
Como `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Recapitulando as atualizações parciais { #partial-updates-recap }
Resumindo, para aplicar atualizações parciais você deveria:
* (Opcionalmente) usar `PATCH` em vez de `PUT`.
* Recuperar os dados armazenados.
* Colocar esses dados em um modelo do Pydantic.
* Gerar um `dict` sem valores padrão a partir do modelo de entrada (usando `exclude_unset`).
* Dessa forma, você pode atualizar apenas os valores realmente definidos pelo usuário, em vez de substituir valores já armazenados por valores padrão do modelo.
* Criar uma cópia do modelo armazenado, atualizando seus atributos com as atualizações parciais recebidas (usando o parâmetro `update`).
* Converter o modelo copiado em algo que possa ser armazenado no seu BD (por exemplo, usando o `jsonable_encoder`).
* Isso é comparável a usar o método `.model_dump()` do modelo novamente, mas garante (e converte) os valores para tipos de dados que possam ser convertidos em JSON, por exemplo, `datetime` para `str`.
* Salvar os dados no seu BD.
* Retornar o modelo atualizado.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Dica
Você pode realmente usar essa mesma técnica com uma operação HTTP `PUT`.
Mas o exemplo aqui usa `PATCH` porque foi criado para esses casos de uso.
///
/// note | Nota
Observe que o modelo de entrada ainda é validado.
Portanto, se você quiser receber atualizações parciais que possam omitir todos os atributos, você precisa ter um modelo com todos os atributos marcados como opcionais (com valores padrão ou `None`).
Para distinguir entre os modelos com todos os valores opcionais para **atualizações** e modelos com valores obrigatórios para **criação**, você pode usar as ideias descritas em [Modelos Adicionais](extra-models.md).
///
================================================
FILE: docs/pt/docs/tutorial/body.md
================================================
# Corpo da requisição { #request-body }
Quando você precisa enviar dados de um cliente (como de um navegador) para sua API, você os envia como um **corpo da requisição**.
O corpo da **requisição** é a informação enviada pelo cliente para sua API. O corpo da **resposta** é a informação que sua API envia para o cliente.
Sua API quase sempre precisa enviar um corpo na **resposta**. Mas os clientes não necessariamente precisam enviar **corpos de requisição** o tempo todo, às vezes eles apenas requisitam um path, talvez com alguns parâmetros de consulta, mas não enviam um corpo.
Para declarar um corpo da **requisição**, você utiliza os modelos do [Pydantic](https://docs.pydantic.dev/) com todos os seus poderes e benefícios.
/// info | Informação
Para enviar dados, você deveria usar um dos: `POST` (o mais comum), `PUT`, `DELETE` ou `PATCH`.
Enviar um corpo em uma requisição `GET` não tem um comportamento definido nas especificações, porém é suportado pelo FastAPI, apenas para casos de uso bem complexos/extremos.
Como é desencorajado, a documentação interativa com Swagger UI não irá mostrar a documentação para o corpo da requisição ao usar `GET`, e proxies intermediários podem não suportá-lo.
///
## Importe o `BaseModel` do Pydantic { #import-pydantics-basemodel }
Primeiro, você precisa importar `BaseModel` do `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Crie seu modelo de dados { #create-your-data-model }
Então você declara seu modelo de dados como uma classe que herda `BaseModel`.
Utilize os tipos Python padrão para todos os atributos:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Assim como quando declaramos parâmetros de consulta, quando um atributo do modelo possui um valor padrão, ele não é obrigatório. Caso contrário, é obrigatório. Use `None` para torná-lo apenas opcional.
Por exemplo, o modelo acima declara um JSON "`object`" (ou `dict` no Python) como esse:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...como `description` e `tax` são opcionais (com um valor padrão de `None`), esse JSON "`object`" também é válido:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Declare como um parâmetro { #declare-it-as-a-parameter }
Para adicioná-lo à sua *operação de rota*, declare-o da mesma maneira que você declarou parâmetros de rota e de consulta:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...e declare o seu tipo como o modelo que você criou, `Item`.
## Resultados { #results }
Apenas com essa declaração de tipos do Python, o **FastAPI** irá:
* Ler o corpo da requisição como JSON.
* Converter os tipos correspondentes (se necessário).
* Validar os dados.
* Se algum dado for inválido, irá retornar um erro bem claro, indicando exatamente onde e o que estava incorreto.
* Entregar a você a informação recebida no parâmetro `item`.
* Como você o declarou na função como do tipo `Item`, você também terá o suporte do editor (preenchimento automático, etc) para todos os atributos e seus tipos.
* Gerar definições de [JSON Schema](https://json-schema.org) para o seu modelo; você também pode usá-las em qualquer outro lugar se fizer sentido para o seu projeto.
* Esses schemas farão parte do esquema OpenAPI gerado, e serão usados pelas UIs de documentação automática.
## Documentação automática { #automatic-docs }
Os JSON Schemas dos seus modelos farão parte do esquema OpenAPI gerado para sua aplicação, e aparecerão na documentação interativa da API:
E também serão utilizados na documentação da API dentro de cada *operação de rota* que precisar deles:
## Suporte do editor { #editor-support }
No seu editor, dentro da função você receberá dicas de tipos e preenchimento automático em todo lugar (isso não aconteceria se você recebesse um `dict` em vez de um modelo Pydantic):
Você também poderá receber verificações de erros para operações de tipos incorretas:
Isso não é por acaso, todo o framework foi construído em volta deste design.
E foi imensamente testado na fase de design, antes de qualquer implementação, para garantir que funcionaria para todos os editores de texto.
Houveram mudanças no próprio Pydantic para que isso fosse possível.
As capturas de tela anteriores foram capturas no [Visual Studio Code](https://code.visualstudio.com).
Mas você terá o mesmo suporte do editor no [PyCharm](https://www.jetbrains.com/pycharm/) e na maioria dos editores Python:
/// tip | Dica
Se você utiliza o [PyCharm](https://www.jetbrains.com/pycharm/) como editor, você pode utilizar o [Plugin do Pydantic para o PyCharm](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
Melhora o suporte do editor para seus modelos Pydantic com:
* preenchimento automático
* verificação de tipos
* refatoração
* buscas
* inspeções
///
## Use o modelo { #use-the-model }
Dentro da função, você pode acessar todos os atributos do objeto do modelo diretamente:
{* ../../docs_src/body/tutorial002_py310.py *}
## Corpo da requisição + parâmetros de rota { #request-body-path-parameters }
Você pode declarar parâmetros de rota e corpo da requisição ao mesmo tempo.
O **FastAPI** irá reconhecer que os parâmetros da função que combinam com parâmetros de rota devem ser **retirados da rota**, e que parâmetros da função que são declarados como modelos Pydantic sejam **retirados do corpo da requisição**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Corpo da requisição + parâmetros de rota + parâmetros de consulta { #request-body-path-query-parameters }
Você também pode declarar parâmetros de **corpo**, **rota** e **consulta**, ao mesmo tempo.
O **FastAPI** irá reconhecer cada um deles e retirar a informação do local correto.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Os parâmetros da função serão reconhecidos conforme abaixo:
* Se o parâmetro também é declarado no **path**, será utilizado como um parâmetro de rota.
* Se o parâmetro é de um **tipo único** (como `int`, `float`, `str`, `bool`, etc) será interpretado como um parâmetro de **consulta**.
* Se o parâmetro é declarado como um **modelo Pydantic**, será interpretado como o **corpo** da requisição.
/// note | Nota
O FastAPI saberá que o valor de `q` não é obrigatório por causa do valor padrão `= None`.
O `str | None` não é utilizado pelo FastAPI para determinar que o valor não é obrigatório, ele saberá que não é obrigatório porque tem um valor padrão `= None`.
Mas adicionar as anotações de tipo permitirá ao seu editor oferecer um suporte melhor e detectar erros.
///
## Sem o Pydantic { #without-pydantic }
Se você não quer utilizar os modelos Pydantic, você também pode utilizar o parâmetro **Body**. Veja a documentação para [Body - Parâmetros múltiplos: Valores singulares no body](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/pt/docs/tutorial/cookie-param-models.md
================================================
# Modelos de Parâmetros de Cookie { #cookie-parameter-models }
Se você possui um grupo de **cookies** que estão relacionados, você pode criar um **modelo Pydantic** para declará-los. 🍪
Isso lhe permitiria **reutilizar o modelo** em **diversos lugares** e também declarar validações e metadata para todos os parâmetros de uma vez. 😎
/// note | Nota
Isso é suportado desde a versão `0.115.0` do FastAPI. 🤓
///
/// tip | Dica
Essa mesma técnica se aplica para `Query`, `Cookie`, e `Header`. 😎
///
## Cookies com Modelos Pydantic { #cookies-with-a-pydantic-model }
Declare os parâmetros de **cookie** de que você precisa em um **modelo Pydantic**, e depois declare o parâmetro como `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
O **FastAPI** irá **extrair** os dados para **cada campo** dos **cookies** recebidos na requisição e lhe fornecer o modelo Pydantic que você definiu.
## Verifique a Documentação { #check-the-docs }
Você pode ver os cookies definidos na IU da documentação em `/docs`:
/// info | Informação
Tenha em mente que, como os **navegadores lidam com cookies** de maneira especial e por baixo dos panos, eles **não** permitem facilmente que o **JavaScript** lidem com eles.
Se você for na **IU da documentação da API** em `/docs` você poderá ver a **documentação** para cookies das suas *operações de rotas*.
Mas mesmo que você **adicionar os dados** e clicar em "Executar", pelo motivo da IU da documentação trabalhar com **JavaScript**, os cookies não serão enviados, e você verá uma mensagem de **erro** como se você não tivesse escrito nenhum dado.
///
## Proibir Cookies Adicionais { #forbid-extra-cookies }
Em alguns casos especiais (provavelmente não muito comuns), você pode querer **restringir** os cookies que você deseja receber.
Agora a sua API possui o poder de controlar o seu próprio consentimento de cookie. 🤪🍪
Você pode utilizar a configuração do modelo Pydantic para `proibir` qualquer campo `extra`:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Se o cliente tentar enviar alguns **cookies extras**, eles receberão um retorno de **erro**.
Coitados dos banners de cookies com todo o seu esforço para obter o seu consentimento para a API rejeitá-lo. 🍪
Por exemplo, se o cliente tentar enviar um cookie `santa_tracker` com o valor de `good-list-please`, o cliente receberá uma resposta de **erro** informando que o `santa_tracker` cookie não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Resumo { #summary }
Você consegue utilizar **modelos Pydantic** para declarar **cookies** no **FastAPI**. 😎
================================================
FILE: docs/pt/docs/tutorial/cookie-params.md
================================================
# Parâmetros de Cookie { #cookie-parameters }
Você pode definir parâmetros de Cookie da mesma maneira que define parâmetros com `Query` e `Path`.
## Importe `Cookie` { #import-cookie }
Primeiro importe `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Declare parâmetros de `Cookie` { #declare-cookie-parameters }
Então declare os parâmetros de cookie usando a mesma estrutura que em `Path` e `Query`.
Você pode definir o valor padrão, assim como todas as validações extras ou parâmetros de anotação:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalhes Técnicos
`Cookie` é uma classe "irmã" de `Path` e `Query`. Ela também herda da mesma classe em comum `Param`.
Mas lembre-se que quando você importa `Query`, `Path`, `Cookie` e outras de `fastapi`, elas são na verdade funções que retornam classes especiais.
///
/// info | Informação
Para declarar cookies, você precisa usar `Cookie`, pois caso contrário, os parâmetros seriam interpretados como parâmetros de consulta.
///
/// info | Informação
Tenha em mente que, como os **navegadores lidam com cookies** de maneiras especiais e nos bastidores, eles **não** permitem facilmente que o **JavaScript** os acesse.
Se você for à **interface de documentação da API** em `/docs`, poderá ver a **documentação** de cookies para suas *operações de rota*.
Mas mesmo que você **preencha os dados** e clique em "Execute", como a interface de documentação funciona com **JavaScript**, os cookies não serão enviados e você verá uma mensagem de **erro** como se você não tivesse escrito nenhum valor.
///
## Recapitulando { #recap }
Declare cookies com `Cookie`, usando o mesmo padrão comum que utiliza-se em `Query` e `Path`.
================================================
FILE: docs/pt/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS ou "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) refere-se às situações em que um frontend rodando em um navegador possui um código JavaScript que se comunica com um backend, e o backend está em uma "origem" diferente do frontend.
## Origem { #origin }
Uma origem é a combinação de protocolo (`http`, `https`), domínio (`myapp.com`, `localhost`, `localhost.tiangolo.com`), e porta (`80`, `443`, `8080`).
Então, todos estes são origens diferentes:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Mesmo se todos estiverem em `localhost`, eles usam diferentes protocolos ou portas, portanto, são "origens" diferentes.
## Passos { #steps }
Então, digamos que você tenha um frontend rodando no seu navegador em `http://localhost:8080`, e seu JavaScript esteja tentando se comunicar com um backend rodando em `http://localhost` (como não especificamos uma porta, o navegador assumirá a porta padrão `80`).
Portanto, o navegador enviará uma requisição HTTP `OPTIONS` ao backend `:80`, e se o backend enviar os cabeçalhos apropriados autorizando a comunicação a partir dessa origem diferente (`http://localhost:8080`), então o navegador `:8080` permitirá que o JavaScript no frontend envie sua requisição para o backend `:80`.
Para conseguir isso, o backend `:80` deve ter uma lista de "origens permitidas".
Neste caso, a lista terá que incluir `http://localhost:8080` para o frontend `:8080` funcionar corretamente.
## Curingas { #wildcards }
É possível declarar a lista como `"*"` (um "curinga") para dizer que tudo está permitido.
Mas isso só permitirá certos tipos de comunicação, excluindo tudo que envolva credenciais: cookies, cabeçalhos de autorização como aqueles usados com Bearer Tokens, etc.
Então, para que tudo funcione corretamente, é melhor especificar explicitamente as origens permitidas.
## Usar `CORSMiddleware` { #use-corsmiddleware }
Você pode configurá-lo em sua aplicação **FastAPI** usando o `CORSMiddleware`.
* Importe `CORSMiddleware`.
* Crie uma lista de origens permitidas (como strings).
* Adicione-a como um "middleware" à sua aplicação **FastAPI**.
Você também pode especificar se o seu backend permite:
* Credenciais (Cabeçalhos de autorização, Cookies, etc).
* Métodos HTTP específicos (`POST`, `PUT`) ou todos eles com o curinga `"*"`.
* Cabeçalhos HTTP específicos ou todos eles com o curinga `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Os parâmetros padrão usados pela implementação `CORSMiddleware` são restritivos por padrão, então você precisará habilitar explicitamente as origens, métodos ou cabeçalhos específicos para que os navegadores tenham permissão para usá-los em um contexto cross domain.
Os seguintes argumentos são suportados:
* `allow_origins` - Uma lista de origens que devem ter permissão para fazer requisições de origem cruzada. Por exemplo, `['https://example.org', 'https://www.example.org']`. Você pode usar `['*']` para permitir qualquer origem.
* `allow_origin_regex` - Uma string regex para corresponder às origens que devem ter permissão para fazer requisições de origem cruzada. Por exemplo, `'https://.*\.example\.org'`.
* `allow_methods` - Uma lista de métodos HTTP que devem ser permitidos para requisições de origem cruzada. O padrão é `['GET']`. Você pode usar `['*']` para permitir todos os métodos padrão.
* `allow_headers` - Uma lista de cabeçalhos de solicitação HTTP que devem ter suporte para requisições de origem cruzada. O padrão é `[]`. Você pode usar `['*']` para permitir todos os cabeçalhos. Os cabeçalhos `Accept`, `Accept-Language`, `Content-Language` e `Content-Type` são sempre permitidos para [requisições CORS simples](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Indica que os cookies devem ser suportados para requisições de origem cruzada. O padrão é `False`.
Nenhum de `allow_origins`, `allow_methods` e `allow_headers` pode ser definido como `['*']` se `allow_credentials` estiver definido como `True`. Todos eles devem ser [especificados explicitamente](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Indica quaisquer cabeçalhos de resposta que devem ser disponibilizados ao navegador. O padrão é `[]`.
* `max_age` - Define um tempo máximo em segundos para os navegadores armazenarem em cache as respostas CORS. O padrão é `600`.
O middleware responde a dois tipos específicos de solicitação HTTP...
### Requisições CORS pré-voo (preflight) { #cors-preflight-requests }
Estas são quaisquer solicitações `OPTIONS` com cabeçalhos `Origin` e `Access-Control-Request-Method`.
Nesse caso, o middleware interceptará a solicitação recebida e responderá com cabeçalhos CORS apropriados e uma resposta `200` ou `400` para fins informativos.
### Requisições Simples { #simple-requests }
Qualquer solicitação com um cabeçalho `Origin`. Neste caso, o middleware passará a solicitação normalmente, mas incluirá cabeçalhos CORS apropriados na resposta.
## Mais informações { #more-info }
Para mais informações sobre CORS, consulte a [documentação do CORS da Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Detalhes Técnicos
Você também pode usar `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vêm diretamente da Starlette.
///
================================================
FILE: docs/pt/docs/tutorial/debugging.md
================================================
# Depuração { #debugging }
Você pode conectar o depurador no seu editor, por exemplo, com o Visual Studio Code ou PyCharm.
## Chamar `uvicorn` { #call-uvicorn }
Em sua aplicação FastAPI, importe e execute `uvicorn` diretamente:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Sobre `__name__ == "__main__"` { #about-name-main }
O objetivo principal de `__name__ == "__main__"` é ter algum código que seja executado quando seu arquivo for chamado com:
```console
$ python myapp.py
```
mas não é chamado quando outro arquivo o importa, como em:
```Python
from myapp import app
```
#### Mais detalhes { #more-details }
Digamos que seu arquivo se chama `myapp.py`.
Se você executá-lo com:
```console
$ python myapp.py
```
então a variável interna `__name__` no seu arquivo, criada automaticamente pelo Python, terá como valor a string `"__main__"`.
Então, a seção:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
vai executar.
---
Isso não acontecerá se você importar esse módulo (arquivo).
Então, se você tiver outro arquivo `importer.py` com:
```Python
from myapp import app
# Mais um pouco de código
```
nesse caso, a variável `__name__` criada automaticamente dentro de `myapp.py` não terá o valor `"__main__"`.
Então, a linha:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
não será executada.
/// info | Informação
Para mais informações, consulte [a documentação oficial do Python](https://docs.python.org/3/library/__main__.html).
///
## Execute seu código com seu depurador { #run-your-code-with-your-debugger }
Como você está executando o servidor Uvicorn diretamente do seu código, você pode chamar seu programa Python (sua aplicação FastAPI) diretamente do depurador.
---
Por exemplo, no Visual Studio Code, você pode:
* Ir para o painel "Debug".
* "Add configuration...".
* Selecionar "Python"
* Executar o depurador com a opção "`Python: Current File (Integrated Terminal)`".
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
Veja como pode parecer:
---
Se você usar o Pycharm, você pode:
* Abrir o menu "Executar".
* Selecionar a opção "Depurar...".
* Então um menu de contexto aparece.
* Selecionar o arquivo para depurar (neste caso, `main.py`).
Em seguida, ele iniciará o servidor com seu código **FastAPI**, parará em seus pontos de interrupção, etc.
Veja como pode parecer:
================================================
FILE: docs/pt/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Classes como Dependências { #classes-as-dependencies }
Antes de nos aprofundarmos no sistema de **Injeção de Dependência**, vamos melhorar o exemplo anterior.
## `dict` do exemplo anterior { #a-dict-from-the-previous-example }
No exemplo anterior, nós retornávamos um `dict` da nossa dependência ("injetável"):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Mas assim obtemos um `dict` como valor do parâmetro `commons` na *função de operação de rota*.
E sabemos que editores de texto não têm como oferecer muitas funcionalidades (como sugestões automáticas) para objetos do tipo `dict`, por que não há como eles saberem o tipo das chaves e dos valores.
Podemos fazer melhor...
## O que caracteriza uma dependência { #what-makes-a-dependency }
Até agora você apenas viu dependências declaradas como funções.
Mas essa não é a única forma de declarar dependências (mesmo que provavelmente seja a mais comum).
O fator principal para uma dependência é que ela deve ser "chamável"
Um objeto "chamável" em Python é qualquer coisa que o Python possa "chamar" como uma função
Então se você tiver um objeto `alguma_coisa` (que pode *não* ser uma função) que você possa "chamar" (executá-lo) dessa maneira:
```Python
something()
```
ou
```Python
something(some_argument, some_keyword_argument="foo")
```
Então esse objeto é um "chamável".
## Classes como dependências { #classes-as-dependencies_1 }
Você deve ter percebido que para criar um instância de uma classe em Python, a mesma sintaxe é utilizada.
Por exemplo:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
Nesse caso, `fluffy` é uma instância da classe `Cat`.
E para criar `fluffy`, você está "chamando" `Cat`.
Então, uma classe Python também é "chamável".
Então, no **FastAPI**, você pode utilizar uma classe Python como uma dependência.
O que o FastAPI realmente verifica, é se a dependência é algo chamável (função, classe, ou outra coisa) e os parâmetros que foram definidos.
Se você passar algo "chamável" como uma dependência do **FastAPI**, o framework irá analisar os parâmetros desse "chamável" e processá-los da mesma forma que os parâmetros de uma *função de operação de rota*. Incluindo as sub-dependências.
Isso também se aplica a objetos chamáveis que não recebem nenhum parâmetro. Da mesma forma que uma *função de operação de rota* sem parâmetros.
Então, podemos mudar o "injetável" na dependência `common_parameters` acima para a classe `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Observe o método `__init__` usado para criar uma instância da classe:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...ele possui os mesmos parâmetros que nosso `common_parameters` anterior:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Esses parâmetros são utilizados pelo **FastAPI** para "definir" a dependência.
Em ambos os casos teremos:
* Um parâmetro de consulta `q` opcional do tipo `str`.
* Um parâmetro de consulta `skip` do tipo `int`, com valor padrão `0`.
* Um parâmetro de consulta `limit` do tipo `int`, com valor padrão `100`.
Os dados serão convertidos, validados, documentados no esquema da OpenAPI e etc nos dois casos.
## Utilizando { #use-it }
Agora você pode declarar sua dependência utilizando essa classe.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
O **FastAPI** chama a classe `CommonQueryParams`. Isso cria uma "instância" dessa classe e é a instância que será passada para o parâmetro `commons` na sua função.
## Anotações de Tipo vs `Depends` { #type-annotation-vs-depends }
Perceba como escrevemos `CommonQueryParams` duas vezes no código abaixo:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
O último `CommonQueryParams`, em:
```Python
... Depends(CommonQueryParams)
```
...é o que o **FastAPI** irá realmente usar para saber qual é a dependência.
É a partir dele que o FastAPI irá extrair os parâmetros passados e será o que o FastAPI irá realmente chamar.
---
Nesse caso, o primeiro `CommonQueryParams`, em:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams ...
```
////
...não tem nenhum signficado especial para o **FastAPI**. O FastAPI não irá utilizá-lo para conversão dos dados, validação, etc (já que ele utiliza `Depends(CommonQueryParams)` para isso).
Na verdade você poderia escrever apenas:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...como em:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Mas declarar o tipo é encorajado por que é a forma que o seu editor de texto sabe o que será passado como valor do parâmetro `commons`, e assim ele pode ajudar com preenchimento automático, verificações de tipo, etc:
## Pegando um Atalho { #shortcut }
Mas você pode ver que temos uma repetição do código neste exemplo, escrevendo `CommonQueryParams` duas vezes:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
O **FastAPI** nos fornece um atalho para esses casos, onde a dependência é *especificamente* uma classe que o **FastAPI** irá "chamar" para criar uma instância da própria classe.
Para esses casos específicos, você pode fazer o seguinte:
Em vez de escrever:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...escreva:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Você declara a dependência como o tipo do parâmetro, e utiliza `Depends()` sem nenhum parâmetro, em vez de ter que escrever a classe *novamente* dentro de `Depends(CommonQueryParams)`.
O mesmo exemplo ficaria então dessa forma:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...e o **FastAPI** saberá o que fazer.
/// tip | Dica
Se isso parece mais confuso do que útil, não utilize, você não *precisa* disso.
É apenas um atalho. Por que o **FastAPI** se preocupa em ajudar a minimizar a repetição de código.
///
================================================
FILE: docs/pt/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Dependências em decoradores de operações de rota { #dependencies-in-path-operation-decorators }
Em alguns casos você não precisa necessariamente do valor de retorno de uma dependência dentro de uma *função de operação de rota*.
Ou a dependência não retorna nenhum valor.
Mas você ainda precisa que ela seja executada/resolvida.
Para esses casos, em vez de declarar um parâmetro em uma *função de operação de rota* com `Depends`, você pode adicionar um argumento `dependencies` do tipo `list` ao decorador da operação de rota.
## Adicione `dependencies` ao *decorador da operação de rota* { #add-dependencies-to-the-path-operation-decorator }
O *decorador da operação de rota* recebe um argumento opcional `dependencies`.
Ele deve ser uma lista de `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Essas dependências serão executadas/resolvidas da mesma forma que dependências comuns. Mas o valor delas (se existir algum) não será passado para a sua *função de operação de rota*.
/// tip | Dica
Alguns editores de texto checam parâmetros de funções não utilizados, e os mostram como erros.
Utilizando `dependencies` no *decorador da operação de rota* você pode garantir que elas serão executadas enquanto evita erros de editores/ferramentas.
Isso também pode ser útil para evitar confundir novos desenvolvedores que ao ver um parâmetro não usado no seu código podem pensar que ele é desnecessário.
///
/// info | Informação
Neste exemplo utilizamos cabeçalhos personalizados inventados `X-Key` e `X-Token`.
Mas em situações reais, como implementações de segurança, você pode obter mais vantagens em usar as [Ferramentas de segurança integradas (o próximo capítulo)](../security/index.md).
///
## Erros das dependências e valores de retorno { #dependencies-errors-and-return-values }
Você pode utilizar as mesmas *funções* de dependências que você usaria normalmente.
### Requisitos de Dependências { #dependency-requirements }
Dependências podem declarar requisitos de requisições (como cabeçalhos) ou outras subdependências:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Levantar exceções { #raise-exceptions }
Essas dependências podem `raise` exceções, da mesma forma que dependências comuns:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Valores de retorno { #return-values }
E elas também podem ou não retornar valores, eles não serão utilizados.
Então, você pode reutilizar uma dependência comum (que retorna um valor) que já seja utilizada em outro lugar, e mesmo que o valor não seja utilizado, a dependência será executada:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Dependências para um grupo de *operações de rota* { #dependencies-for-a-group-of-path-operations }
Mais a frente, quando você ler sobre como estruturar aplicações maiores ([Aplicações maiores - Múltiplos arquivos](../../tutorial/bigger-applications.md)), possivelmente com múltiplos arquivos, você aprenderá a declarar um único parâmetro `dependencies` para um grupo de *operações de rota*.
## Dependências globais { #global-dependencies }
No próximo passo veremos como adicionar dependências para uma aplicação `FastAPI` inteira, para que elas sejam aplicadas em toda *operação de rota*.
================================================
FILE: docs/pt/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Dependências com yield { #dependencies-with-yield }
O **FastAPI** possui suporte para dependências que realizam alguns passos extras ao finalizar.
Para fazer isso, utilize `yield` em vez de `return`, e escreva os passos extras (código) depois.
/// tip | Dica
Garanta utilizar `yield` apenas uma vez por dependência.
///
/// note | Detalhes Técnicos
Qualquer função que possa ser utilizada com:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) ou
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
pode ser utilizada como uma dependência do **FastAPI**.
Na realidade, o FastAPI utiliza esses dois decoradores internamente.
///
## Uma dependência de banco de dados com `yield` { #a-database-dependency-with-yield }
Por exemplo, você poderia utilizar isso para criar uma sessão do banco de dados, e fechá-la após terminar.
Apenas o código anterior à declaração com `yield` e o código contendo essa declaração são executados antes de criar uma resposta:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
O valor gerado (yielded) é o que é injetado nas *operações de rota* e outras dependências:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
O código após o `yield` é executado após a resposta:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Dica
Você pode usar funções assíncronas (`async`) ou funções comuns.
O **FastAPI** saberá o que fazer com cada uma, da mesma forma que as dependências comuns.
///
## Uma dependência com `yield` e `try` { #a-dependency-with-yield-and-try }
Se você utilizar um bloco `try` em uma dependência com `yield`, você irá capturar qualquer exceção que for lançada enquanto a dependência é utilizada.
Por exemplo, se algum código em um certo momento no meio, em outra dependência ou em uma *operação de rota*, fizer um "rollback" de uma transação de banco de dados ou causar qualquer outra exceção, você irá capturar a exceção em sua dependência.
Então, você pode procurar por essa exceção específica dentro da dependência com `except AlgumaExcecao`.
Da mesma forma, você pode utilizar `finally` para garantir que os passos de saída são executados, com ou sem exceções.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Subdependências com `yield` { #sub-dependencies-with-yield }
Você pode ter subdependências e "árvores" de subdependências de qualquer tamanho e forma, e qualquer uma ou todas elas podem utilizar `yield`.
O **FastAPI** garantirá que o "código de saída" em cada dependência com `yield` é executado na ordem correta.
Por exemplo, `dependency_c` pode depender de `dependency_b`, e `dependency_b` depender de `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
E todas elas podem utilizar `yield`.
Neste caso, `dependency_c`, para executar seu código de saída, precisa que o valor de `dependency_b` (nomeado de `dep_b` aqui) continue disponível.
E, por outro lado, `dependency_b` precisa que o valor de `dependency_a` (nomeado de `dep_a`) esteja disponível para executar seu código de saída.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Da mesma forma, você pode ter algumas dependências com `yield` e outras com `return` e ter uma relação de dependência entre algumas das duas.
E você poderia ter uma única dependência que precisa de diversas outras dependências com `yield`, etc.
Você pode ter qualquer combinação de dependências que você quiser.
O **FastAPI** se encarrega de executá-las na ordem certa.
/// note | Detalhes Técnicos
Tudo isso funciona graças aos [gerenciadores de contexto](https://docs.python.org/3/library/contextlib.html) do Python.
O **FastAPI** utiliza eles internamente para alcançar isso.
///
## Dependências com `yield` e `HTTPException` { #dependencies-with-yield-and-httpexception }
Você viu que pode usar dependências com `yield` e ter blocos `try` que tentam executar algum código e depois executar algum código de saída com `finally`.
Você também pode usar `except` para capturar a exceção que foi levantada e fazer algo com ela.
Por exemplo, você pode levantar uma exceção diferente, como `HTTPException`.
/// tip | Dica
Essa é uma técnica relativamente avançada, e na maioria dos casos você não vai precisar dela, já que você pode levantar exceções (incluindo `HTTPException`) dentro do resto do código da sua aplicação, por exemplo, na *função de operação de rota*.
Mas ela existe para ser utilizada caso você precise. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Se você quiser capturar exceções e criar uma resposta personalizada com base nisso, crie um [Manipulador de Exceções Customizado](../handling-errors.md#install-custom-exception-handlers).
## Dependências com `yield` e `except` { #dependencies-with-yield-and-except }
Se você capturar uma exceção com `except` em uma dependência que utilize `yield` e ela não for levantada novamente (ou uma nova exceção for levantada), o FastAPI não será capaz de identificar que houve uma exceção, da mesma forma que aconteceria com Python puro:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
Neste caso, o cliente irá ver uma resposta *HTTP 500 Internal Server Error* como deveria acontecer, já que não estamos levantando nenhuma `HTTPException` ou coisa parecida, mas o servidor **não terá nenhum log** ou qualquer outra indicação de qual foi o erro. 😱
### Sempre levante (`raise`) em Dependências com `yield` e `except` { #always-raise-in-dependencies-with-yield-and-except }
Se você capturar uma exceção em uma dependência com `yield`, a menos que você esteja levantando outra `HTTPException` ou coisa parecida, **você deve relançar a exceção original**.
Você pode relançar a mesma exceção utilizando `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Agora o cliente irá receber a mesma resposta *HTTP 500 Internal Server Error*, mas o servidor terá nosso `InternalError` personalizado nos logs. 😎
## Execução de dependências com `yield` { #execution-of-dependencies-with-yield }
A sequência de execução é mais ou menos como esse diagrama. O tempo passa do topo para baixo. E cada coluna é uma das partes interagindo ou executando código.
```mermaid
sequenceDiagram
participant client as Cliente
participant handler as Manipulador de exceções
participant dep as Dep com yield
participant operation as Operação de Rota
participant tasks as Tarefas de Background
Note over client,operation: pode lançar exceções, incluindo HTTPException
client ->> dep: Iniciar requisição
Note over dep: Executar código até o yield
opt lançar Exceção
dep -->> handler: lançar Exceção
handler -->> client: resposta de erro HTTP
end
dep ->> operation: Executar dependência, e.g. sessão de BD
opt raise
operation -->> dep: Lançar exceção (e.g. HTTPException)
opt handle
dep -->> dep: Pode capturar exceções, lançar uma nova HTTPException, lançar outras exceções
end
handler -->> client: resposta de erro HTTP
end
operation ->> client: Retornar resposta ao cliente
Note over client,operation: Resposta já foi enviada, e não pode ser modificada
opt Tarefas
operation -->> tasks: Enviar tarefas de background
end
opt Lançar outra exceção
tasks -->> tasks: Manipula exceções no código da tarefa de background
end
```
/// info | Informação
Apenas **uma resposta** será enviada para o cliente. Ela pode ser uma das respostas de erro, ou então a resposta da *operação de rota*.
Após uma dessas respostas ser enviada, nenhuma outra resposta pode ser enviada.
///
/// tip | Dica
Se você levantar qualquer exceção no código da *função de operação de rota*, ela será passada para as dependências com `yield`, incluindo `HTTPException`. Na maioria dos casos, você vai querer relançar essa mesma exceção ou uma nova a partir da dependência com `yield` para garantir que ela seja tratada adequadamente.
///
## Saída antecipada e `scope` { #early-exit-and-scope }
Normalmente, o código de saída das dependências com `yield` é executado **após a resposta** ser enviada ao cliente.
Mas se você sabe que não precisará usar a dependência depois de retornar da *função de operação de rota*, você pode usar `Depends(scope="function")` para dizer ao FastAPI que deve fechar a dependência depois que a *função de operação de rota* retornar, mas **antes** de a **resposta ser enviada**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` recebe um parâmetro `scope` que pode ser:
* `"function"`: iniciar a dependência antes da *função de operação de rota* que trata a requisição, encerrar a dependência depois que a *função de operação de rota* termina, mas **antes** de a resposta ser enviada de volta ao cliente. Assim, a função da dependência será executada **em torno** da *função de operação de rota*.
* `"request"`: iniciar a dependência antes da *função de operação de rota* que trata a requisição (semelhante a quando se usa `"function"`), mas encerrar **depois** que a resposta é enviada de volta ao cliente. Assim, a função da dependência será executada **em torno** do ciclo de **requisição** e resposta.
Se não for especificado e a dependência tiver `yield`, ela terá `scope` igual a `"request"` por padrão.
### `scope` para subdependências { #scope-for-sub-dependencies }
Quando você declara uma dependência com `scope="request"` (o padrão), qualquer subdependência também precisa ter `scope` igual a `"request"`.
Mas uma dependência com `scope` igual a `"function"` pode ter dependências com `scope` igual a `"function"` e com `scope` igual a `"request"`.
Isso porque qualquer dependência precisa conseguir executar seu código de saída antes das subdependências, pois pode ainda precisar usá-las durante seu código de saída.
```mermaid
sequenceDiagram
participant client as Cliente
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Operação de Rota
client ->> dep_req: Iniciar requisição
Note over dep_req: Executar código até o yield
dep_req ->> dep_func: Passar dependência
Note over dep_func: Executar código até o yield
dep_func ->> operation: Executar operação de rota com dependência
operation ->> dep_func: Retornar da operação de rota
Note over dep_func: Executar código após o yield
Note over dep_func: ✅ Dependência fechada
dep_func ->> client: Enviar resposta ao cliente
Note over client: Resposta enviada
Note over dep_req: Executar código após o yield
Note over dep_req: ✅ Dependência fechada
```
## Dependências com `yield`, `HTTPException`, `except` e Tarefas de Background { #dependencies-with-yield-httpexception-except-and-background-tasks }
Dependências com `yield` evoluíram ao longo do tempo para cobrir diferentes casos de uso e corrigir alguns problemas.
Se você quiser ver o que mudou em diferentes versões do FastAPI, você pode ler mais sobre isso no guia avançado, em [Dependências Avançadas - Dependências com `yield`, `HTTPException`, `except` e Tarefas de Background](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Gerenciadores de contexto { #context-managers }
### O que são "Gerenciadores de Contexto" { #what-are-context-managers }
"Gerenciadores de Contexto" são qualquer um dos objetos Python que podem ser utilizados com a declaração `with`.
Por exemplo, [você pode utilizar `with` para ler um arquivo](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Por baixo dos panos, o código `open("./somefile.txt")` cria um objeto que é chamado de "Gerenciador de Contexto".
Quando o bloco `with` finaliza, ele se certifica de fechar o arquivo, mesmo que tenha ocorrido alguma exceção.
Quando você cria uma dependência com `yield`, o **FastAPI** irá criar um gerenciador de contexto internamente para ela, e combiná-lo com algumas outras ferramentas relacionadas.
### Utilizando gerenciadores de contexto em dependências com `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Atenção
Isso é uma ideia mais ou menos "avançada".
Se você está apenas iniciando com o **FastAPI** você pode querer pular isso por enquanto.
///
Em Python, você pode criar Gerenciadores de Contexto ao [criar uma classe com dois métodos: `__enter__()` e `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
Você também pode usá-los dentro de dependências com `yield` do **FastAPI** ao utilizar
`with` ou `async with` dentro da função da dependência:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Dica
Outra forma de criar um gerenciador de contexto é utilizando:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) ou
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
Para decorar uma função com um único `yield`.
Isso é o que o **FastAPI** usa internamente para dependências com `yield`.
Mas você não precisa usar esses decoradores para as dependências do FastAPI (e você não deveria).
O FastAPI irá fazer isso para você internamente.
///
================================================
FILE: docs/pt/docs/tutorial/dependencies/global-dependencies.md
================================================
# Dependências Globais { #global-dependencies }
Para alguns tipos de aplicação você pode querer adicionar dependências para toda a aplicação.
De forma semelhante a [adicionar `dependencies` aos *decoradores de operação de rota*](dependencies-in-path-operation-decorators.md), você pode adicioná-las à aplicação `FastAPI`.
Nesse caso, elas serão aplicadas a todas as *operações de rota* da aplicação:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
E todos os conceitos apresentados na seção sobre [adicionar `dependencies` aos *decoradores de operação de rota*](dependencies-in-path-operation-decorators.md) ainda se aplicam, mas nesse caso, para todas as *operações de rota* da aplicação.
## Dependências para conjuntos de *operações de rota* { #dependencies-for-groups-of-path-operations }
Mais para a frente, quando você ler sobre como estruturar aplicações maiores ([Aplicações Maiores - Múltiplos Arquivos](../../tutorial/bigger-applications.md)), possivelmente com múltiplos arquivos, você irá aprender a declarar um único parâmetro `dependencies` para um conjunto de *operações de rota*.
================================================
FILE: docs/pt/docs/tutorial/dependencies/index.md
================================================
# Dependências { #dependencies }
O **FastAPI** possui um poderoso, mas intuitivo sistema de **Injeção de Dependência**.
Esse sistema foi pensado para ser fácil de usar, e permitir que qualquer desenvolvedor possa integrar facilmente outros componentes ao **FastAPI**.
## O que é "Injeção de Dependência" { #what-is-dependency-injection }
**"Injeção de Dependência"** no mundo da programação significa, que existe uma maneira de declarar no seu código (nesse caso, suas *funções de operação de rota*) para declarar as coisas que ele precisa para funcionar e que serão utilizadas: "dependências".
Então, esse sistema (nesse caso o **FastAPI**) se encarrega de fazer o que for preciso para fornecer essas dependências para o código ("injetando" as dependências).
Isso é bastante útil quando você precisa:
* Definir uma lógica compartilhada (mesmo formato de código repetidamente).
* Compartilhar conexões com banco de dados.
* Aplicar regras de segurança, autenticação, papéis de usuários, etc.
* E muitas outras coisas...
Tudo isso, enquanto minimizamos a repetição de código.
## Primeiros passos { #first-steps }
Vamos ver um exemplo simples. Tão simples que não será muito útil, por enquanto.
Mas dessa forma podemos focar em como o sistema de **Injeção de Dependência** funciona.
### Criando uma dependência, ou "dependable" { #create-a-dependency-or-dependable }
Primeiro vamos focar na dependência.
Ela é apenas uma função que pode receber os mesmos parâmetros de uma *função de operação de rota*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
E pronto.
**2 linhas**.
E com a mesma forma e estrutura de todas as suas *funções de operação de rota*.
Você pode pensar nela como uma *função de operação de rota* sem o "decorador" (sem a linha `@app.get("/some-path")`).
E com qualquer retorno que você desejar.
Neste caso, a dependência espera por:
* Um parâmetro de consulta opcional `q` do tipo `str`.
* Um parâmetro de consulta opcional `skip` do tipo `int`, e igual a `0` por padrão.
* Um parâmetro de consulta opcional `limit` do tipo `int`, e igual a `100` por padrão.
E então retorna um `dict` contendo esses valores.
/// info | Informação
FastAPI passou a suportar a notação `Annotated` (e começou a recomendá-la) na versão 0.95.0.
Se você utiliza uma versão anterior, ocorrerão erros ao tentar utilizar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) para pelo menos 0.95.1 antes de usar `Annotated`.
///
### Importando `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Declarando a dependência, no "dependente" { #declare-the-dependency-in-the-dependant }
Da mesma forma que você utiliza `Body`, `Query`, etc. Como parâmetros de sua *função de operação de rota*, utilize `Depends` com um novo parâmetro:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Ainda que `Depends` seja utilizado nos parâmetros da função da mesma forma que `Body`, `Query`, etc, `Depends` funciona de uma forma um pouco diferente.
Você fornece um único parâmetro para `Depends`.
Esse parâmetro deve ser algo como uma função.
Você **não chama a função** diretamente (não adicione os parênteses no final), apenas a passe como parâmetro de `Depends()`.
E essa função vai receber os parâmetros da mesma forma que uma *função de operação de rota*.
/// tip | Dica
Você verá quais outras "coisas", além de funções, podem ser usadas como dependências no próximo capítulo.
///
Sempre que uma nova requisição for realizada, o **FastAPI** se encarrega de:
* Chamar sua dependência ("dependable") com os parâmetros corretos.
* Obter o resultado da função.
* Atribuir esse resultado para o parâmetro em sua *função de operação de rota*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Assim, você escreve um código compartilhado apenas uma vez e o **FastAPI** se encarrega de chamá-lo em suas *operações de rota*.
/// check | Verifique
Perceba que você não precisa criar uma classe especial e enviar a dependência para algum outro lugar em que o **FastAPI** a "registre" ou realize qualquer operação similar.
Você apenas envia para `Depends` e o **FastAPI** sabe como fazer o resto.
///
## Compartilhando dependências `Annotated` { #share-annotated-dependencies }
Nos exemplos acima, você pode ver que existe uma pequena **duplicação de código**.
Quando você precisa utilizar a dependência `common_parameters()`, você precisa escrever o parâmetro inteiro com uma anotação de tipo e `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Mas como estamos utilizando `Annotated`, podemos guardar esse valor `Annotated` em uma variável e utilizá-la em múltiplos locais:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Dica
Isso é apenas Python padrão, essa funcionalidade é chamada de "type alias", e na verdade não é específica ao **FastAPI**.
Mas como o **FastAPI** se baseia em convenções do Python, incluindo `Annotated`, você pode incluir esse truque no seu código. 😎
///
As dependências continuarão funcionando como esperado, e a **melhor parte** é que a **informação sobre o tipo é preservada**, o que signfica que seu editor de texto ainda irá incluir **preenchimento automático**, **visualização de erros**, etc. O mesmo vale para ferramentas como `mypy`.
Isso é especialmente útil para uma **base de código grande** onde **as mesmas dependências** são utilizadas repetidamente em **muitas *operações de rota***.
## `Async` ou não, eis a questão { #to-async-or-not-to-async }
Como as dependências também serão chamadas pelo **FastAPI** (da mesma forma que *funções de operação de rota*), as mesmas regras se aplicam ao definir suas funções.
Você pode utilizar `async def` ou apenas `def`.
E você pode declarar dependências utilizando `async def` dentro de *funções de operação de rota* definidas com `def`, ou declarar dependências com `def` e utilizar dentro de *funções de operação de rota* definidas com `async def`, etc.
Não faz diferença. O **FastAPI** sabe o que fazer.
/// note | Nota
Caso você não conheça, veja em [Async: *"Com Pressa?"*](../../async.md#in-a-hurry) a sessão acerca de `async` e `await` na documentação.
///
## Integrando com OpenAPI { #integrated-with-openapi }
Todas as declarações de requisições, validações e requisitos para suas dependências (e sub-dependências) serão integradas em um mesmo esquema OpenAPI.
Então, a documentação interativa também terá toda a informação sobre essas dependências:
## Caso de Uso Simples { #simple-usage }
Se você parar para ver, *funções de operação de rota* são declaradas para serem usadas sempre que uma *rota* e uma *operação* se encaixam, e então o **FastAPI** se encarrega de chamar a função correspondente com os argumentos corretos, extraindo os dados da requisição.
Na verdade, todos (ou a maioria) dos frameworks web funcionam da mesma forma.
Você nunca chama essas funções diretamente. Elas são chamadas pelo framework utilizado (nesse caso, **FastAPI**).
Com o Sistema de Injeção de Dependência, você também pode informar ao **FastAPI** que sua *função de operação de rota* também "depende" em algo a mais que deve ser executado antes de sua *função de operação de rota*, e o **FastAPI** se encarrega de executar e "injetar" os resultados.
Outros termos comuns para essa mesma ideia de "injeção de dependência" são:
* recursos
* provedores
* serviços
* injetáveis
* componentes
## Plug-ins em **FastAPI** { #fastapi-plug-ins }
Integrações e "plug-ins" podem ser construídos com o sistema de **Injeção de Dependência**. Mas na verdade, **não há necessidade de criar "plug-ins"**, já que utilizando dependências é possível declarar um número infinito de integrações e interações que se tornam disponíveis para as suas *funções de operação de rota*.
E as dependências podem ser criadas de uma forma bastante simples e intuitiva que permite que você importe apenas os pacotes Python que forem necessários, e integrá-los com as funções de sua API em algumas linhas de código, *literalmente*.
Você verá exemplos disso nos próximos capítulos, acerca de bancos de dados relacionais e NoSQL, segurança, etc.
## Compatibilidade do **FastAPI** { #fastapi-compatibility }
A simplicidade do sistema de injeção de dependência do **FastAPI** faz ele compatível com:
* todos os bancos de dados relacionais
* bancos de dados NoSQL
* pacotes externos
* APIs externas
* sistemas de autenticação e autorização
* sistemas de monitoramento de uso para APIs
* sistemas de injeção de dados de resposta
* etc.
## Simples e Poderoso { #simple-and-powerful }
Mesmo que o sistema hierárquico de injeção de dependência seja simples de definir e utilizar, ele ainda é bastante poderoso.
Você pode definir dependências que por sua vez definem suas próprias dependências.
Por fim, uma árvore hierárquica de dependências é criada, e o sistema de **Injeção de Dependência** toma conta de resolver todas essas dependências (e as sub-dependências delas) para você, e provê (injeta) os resultados em cada passo.
Por exemplo, vamos supor que você possua 4 endpoints na sua API (*operações de rota*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
Você poderia adicionar diferentes requisitos de permissão para cada um deles utilizando apenas dependências e sub-dependências:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integração com **OpenAPI** { #integrated-with-openapi_1 }
Todas essas dependências, ao declarar os requisitos para suas *operações de rota*, também adicionam parâmetros, validações, etc.
O **FastAPI** se encarrega de adicionar tudo isso ao esquema OpenAPI, para que seja mostrado nos sistemas de documentação interativa.
================================================
FILE: docs/pt/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Subdependências { #sub-dependencies }
Você pode criar dependências que possuem **subdependências**.
Elas podem ter o nível de **profundidade** que você achar necessário.
O **FastAPI** se encarrega de resolver essas dependências.
## Primeira dependência "dependable" { #first-dependency-dependable }
Você pode criar uma primeira dependência ("dependable") dessa forma:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Esse código declara um parâmetro de consulta opcional, `q`, com o tipo `str`, e então retorna esse parâmetro.
Isso é bastante simples (e não muito útil), mas irá nos ajudar a focar em como as subdependências funcionam.
## Segunda dependência, "dependable" e "dependente" { #second-dependency-dependable-and-dependant }
Então, você pode criar uma outra função para uma dependência (um "dependable") que ao mesmo tempo declara sua própria dependência (o que faz dela um "dependente" também):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Vamos focar nos parâmetros declarados:
* Mesmo que essa função seja uma dependência ("dependable") por si mesma, ela também declara uma outra dependência (ela "depende" de outra coisa).
* Ela depende do `query_extractor`, e atribui o valor retornado pela função ao parâmetro `q`.
* Ela também declara um cookie opcional `last_query`, do tipo `str`.
* Se o usuário não passou nenhuma consulta `q`, a última consulta é utilizada, que foi salva em um cookie anteriormente.
## Utilizando a dependência { #use-the-dependency }
Então podemos utilizar a dependência com:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Informação
Perceba que nós estamos declarando apenas uma dependência na *função de operação de rota*, em `query_or_cookie_extractor`.
Mas o **FastAPI** saberá que precisa solucionar `query_extractor` primeiro, para passar o resultado para `query_or_cookie_extractor` enquanto chama a função.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Utilizando a mesma dependência múltiplas vezes { #using-the-same-dependency-multiple-times }
Se uma de suas dependências é declarada várias vezes para a mesma *operação de rota*, por exemplo, múltiplas dependências com uma mesma subdependência, o **FastAPI** irá chamar essa subdependência uma única vez para cada requisição.
E o valor retornado é salvo em um "cache" e repassado para todos os "dependentes" que precisam dele em uma requisição específica, em vez de chamar a dependência múltiplas vezes para uma mesma requisição.
Em um cenário avançado onde você precise que a dependência seja calculada em cada passo (possivelmente várias vezes) de uma requisição em vez de utilizar o valor em "cache", você pode definir o parâmetro `use_cache=False` em `Depends`:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ non-Annotated
/// tip | Dica
Utilize a versão com `Annotated` se possível.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Recapitulando { #recap }
Com exceção de todas as palavras complicadas usadas aqui, o sistema de **Injeção de Dependência** é bastante simples.
Consiste apenas de funções que parecem idênticas a *funções de operação de rota*.
Mas ainda assim, é bastante poderoso, e permite que você declare grafos (árvores) de dependências com uma profundidade arbitrária.
/// tip | Dica
Tudo isso pode não parecer muito útil com esses exemplos.
Mas você verá o quão útil isso é nos capítulos sobre **segurança**.
E você também verá a quantidade de código que você não precisara escrever.
///
================================================
FILE: docs/pt/docs/tutorial/encoder.md
================================================
# Codificador Compatível com JSON { #json-compatible-encoder }
Existem alguns casos em que você pode precisar converter um tipo de dados (como um modelo Pydantic) para algo compatível com JSON (como um `dict`, `list`, etc).
Por exemplo, se você precisar armazená-lo em um banco de dados.
Para isso, **FastAPI** fornece uma função `jsonable_encoder()`.
## Usando a função `jsonable_encoder` { #using-the-jsonable-encoder }
Vamos imaginar que você tenha um banco de dados `fake_db` que recebe apenas dados compatíveis com JSON.
Por exemplo, ele não recebe objetos `datetime`, pois estes objetos não são compatíveis com JSON.
Então, um objeto `datetime` teria que ser convertido em um `str` contendo os dados no [formato ISO](https://en.wikipedia.org/wiki/ISO_8601).
Da mesma forma, este banco de dados não receberia um modelo Pydantic (um objeto com atributos), apenas um `dict`.
Você pode usar a função `jsonable_encoder` para resolver isso.
A função recebe um objeto, como um modelo Pydantic e retorna uma versão compatível com JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
Neste exemplo, ele converteria o modelo Pydantic em um `dict`, e o `datetime` em um `str`.
O resultado de chamar a função é algo que pode ser codificado com o padrão do Python [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
A função não retorna um grande `str` contendo os dados no formato JSON (como uma string). Mas sim, retorna uma estrutura de dados padrão do Python (por exemplo, um `dict`) com valores e subvalores compatíveis com JSON.
/// note | Nota
`jsonable_encoder` é realmente usado pelo **FastAPI** internamente para converter dados. Mas também é útil em muitos outros cenários.
///
================================================
FILE: docs/pt/docs/tutorial/extra-data-types.md
================================================
# Tipos de dados extras { #extra-data-types }
Até agora, você tem usado tipos de dados comuns, tais como:
* `int`
* `float`
* `str`
* `bool`
Mas você também pode usar tipos de dados mais complexos.
E você ainda terá os mesmos recursos que viu até agora:
* Ótimo suporte do editor.
* Conversão de dados das requisições recebidas.
* Conversão de dados para os dados da resposta.
* Validação de dados.
* Anotação e documentação automáticas.
## Outros tipos de dados { #other-data-types }
Aqui estão alguns dos tipos de dados adicionais que você pode usar:
* `UUID`:
* Um "Identificador Universalmente Único" padrão, comumente usado como ID em muitos bancos de dados e sistemas.
* Em requisições e respostas será representado como uma `str`.
* `datetime.datetime`:
* O `datetime.datetime` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* O `datetime.date` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `2008-09-15`.
* `datetime.time`:
* O `datetime.time` do Python.
* Em requisições e respostas será representado como uma `str` no formato ISO 8601, exemplo: `14:23:55.003`.
* `datetime.timedelta`:
* O `datetime.timedelta` do Python.
* Em requisições e respostas será representado como um `float` de segundos totais.
* O Pydantic também permite representá-lo como uma "codificação ISO 8601 diferença de tempo", [cheque a documentação para mais informações](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* Em requisições e respostas, será tratado da mesma forma que um `set`:
* Nas requisições, uma lista será lida, eliminando duplicadas e convertendo-a em um `set`.
* Nas respostas, o `set` será convertido para uma `list`.
* O esquema gerado vai especificar que os valores do `set` são únicos (usando o `uniqueItems` do JSON Schema).
* `bytes`:
* O `bytes` padrão do Python.
* Em requisições e respostas será representado como uma `str`.
* O esquema gerado vai especificar que é uma `str` com o "formato" `binary`.
* `Decimal`:
* O `Decimal` padrão do Python.
* Em requisições e respostas será representado como um `float`.
* Você pode checar todos os tipos de dados válidos do Pydantic aqui: [Tipos de dados do Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
## Exemplo { #example }
Aqui está um exemplo de *operação de rota* com parâmetros utilizando-se de alguns dos tipos acima.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Note que os parâmetros dentro da função têm seu tipo de dados natural, e você pode, por exemplo, realizar manipulações normais de data, como:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/pt/docs/tutorial/extra-models.md
================================================
# Modelos Adicionais { #extra-models }
Continuando com o exemplo anterior, será comum ter mais de um modelo relacionado.
Isso é especialmente o caso para modelos de usuários, porque:
* O **modelo de entrada** precisa ser capaz de ter uma senha.
* O **modelo de saída** não deve ter uma senha.
* O **modelo de banco de dados** provavelmente precisaria ter uma senha com hash.
/// danger | Cuidado
Nunca armazene senhas em texto simples dos usuários. Sempre armazene uma "hash segura" que você pode verificar depois.
Se não souber, você aprenderá o que é uma "senha hash" nos [capítulos de segurança](security/simple-oauth2.md#password-hashing).
///
## Múltiplos modelos { #multiple-models }
Aqui está uma ideia geral de como os modelos poderiam parecer com seus campos de senha e os lugares onde são usados:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Sobre `**user_in.model_dump()` { #about-user-in-model-dump }
#### O `.model_dump()` do Pydantic { #pydantics-model-dump }
`user_in` é um modelo Pydantic da classe `UserIn`.
Os modelos Pydantic possuem um método `.model_dump()` que retorna um `dict` com os dados do modelo.
Então, se criarmos um objeto Pydantic `user_in` como:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
e depois chamarmos:
```Python
user_dict = user_in.model_dump()
```
agora temos um `dict` com os dados na variável `user_dict` (é um `dict` em vez de um objeto de modelo Pydantic).
E se chamarmos:
```Python
print(user_dict)
```
teríamos um `dict` Python com:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Desembrulhando um `dict` { #unpacking-a-dict }
Se tomarmos um `dict` como `user_dict` e passarmos para uma função (ou classe) com `**user_dict`, o Python irá "desembrulhá-lo". Ele passará as chaves e valores do `user_dict` diretamente como argumentos chave-valor.
Então, continuando com o `user_dict` acima, escrevendo:
```Python
UserInDB(**user_dict)
```
Resultaria em algo equivalente a:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Ou mais exatamente, usando `user_dict` diretamente, com qualquer conteúdo que ele possa ter no futuro:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Um modelo Pydantic a partir do conteúdo de outro { #a-pydantic-model-from-the-contents-of-another }
Como no exemplo acima, obtivemos o `user_dict` a partir do `user_in.model_dump()`, este código:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
seria equivalente a:
```Python
UserInDB(**user_in.model_dump())
```
...porque `user_in.model_dump()` é um `dict`, e depois fazemos o Python "desembrulhá-lo" passando-o para `UserInDB` precedido por `**`.
Então, obtemos um modelo Pydantic a partir dos dados em outro modelo Pydantic.
#### Desembrulhando um `dict` e palavras-chave extras { #unpacking-a-dict-and-extra-keywords }
E, então, adicionando o argumento de palavra-chave extra `hashed_password=hashed_password`, como em:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...acaba sendo como:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Atenção
As funções adicionais de suporte `fake_password_hasher` e `fake_save_user` servem apenas para demonstrar um fluxo possível dos dados, mas é claro que elas não fornecem segurança real.
///
## Reduzir duplicação { #reduce-duplication }
Reduzir a duplicação de código é uma das ideias principais no **FastAPI**.
A duplicação de código aumenta as chances de bugs, problemas de segurança, problemas de desincronização de código (quando você atualiza em um lugar, mas não em outros), etc.
E esses modelos estão compartilhando muitos dos dados e duplicando nomes e tipos de atributos.
Nós poderíamos fazer melhor.
Podemos declarar um modelo `UserBase` que serve como base para nossos outros modelos. E então podemos fazer subclasses desse modelo que herdam seus atributos (declarações de tipo, validação, etc.).
Toda conversão de dados, validação, documentação, etc. ainda funcionará normalmente.
Dessa forma, podemos declarar apenas as diferenças entre os modelos (com `password` em texto claro, com `hashed_password` e sem senha):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` ou `anyOf` { #union-or-anyof }
Você pode declarar uma resposta como o `Union` de dois ou mais tipos, o que significa que a resposta seria qualquer um deles.
Isso será definido no OpenAPI com `anyOf`.
Para fazer isso, use a anotação de tipo padrão do Python [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note | Nota
Ao definir um [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions), inclua o tipo mais específico primeiro, seguido pelo tipo menos específico. No exemplo abaixo, o tipo mais específico `PlaneItem` vem antes de `CarItem` em `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` no Python 3.10 { #union-in-python-3-10 }
Neste exemplo, passamos `Union[PlaneItem, CarItem]` como o valor do argumento `response_model`.
Dado que estamos passando-o como um **valor para um argumento** em vez de colocá-lo em uma **anotação de tipo**, precisamos usar `Union` mesmo no Python 3.10.
Se estivesse em uma anotação de tipo, poderíamos ter usado a barra vertical, como:
```Python
some_variable: PlaneItem | CarItem
```
Mas se colocarmos isso na atribuição `response_model=PlaneItem | CarItem`, teríamos um erro, pois o Python tentaria executar uma **operação inválida** entre `PlaneItem` e `CarItem` em vez de interpretar isso como uma anotação de tipo.
## Lista de modelos { #list-of-models }
Da mesma forma, você pode declarar respostas de listas de objetos.
Para isso, use o padrão Python `list`:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Resposta com `dict` arbitrário { #response-with-arbitrary-dict }
Você também pode declarar uma resposta usando um simples `dict` arbitrário, declarando apenas o tipo das chaves e valores, sem usar um modelo Pydantic.
Isso é útil se você não souber os nomes de campo / atributo válidos (que seriam necessários para um modelo Pydantic) antecipadamente.
Neste caso, você pode usar `dict`:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Recapitulação { #recap }
Use vários modelos Pydantic e herde livremente para cada caso.
Não é necessário ter um único modelo de dados por entidade se essa entidade precisar ter diferentes "estados". No caso da "entidade" de usuário com um estado que inclui `password`, `password_hash` e sem senha.
================================================
FILE: docs/pt/docs/tutorial/first-steps.md
================================================
# Primeiros Passos { #first-steps }
O arquivo FastAPI mais simples pode se parecer com:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Copie o conteúdo para um arquivo `main.py`.
Execute o servidor ao vivo:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Na saída, há uma linha com algo como:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Essa linha mostra a URL onde a sua aplicação está sendo servida, na sua máquina local.
### Confira { #check-it }
Abra o seu navegador em [http://127.0.0.1:8000](http://127.0.0.1:8000).
Você verá essa resposta em JSON:
```JSON
{"message": "Hello World"}
```
### Documentação Interativa de APIs { #interactive-api-docs }
Agora vá para [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá a documentação interativa automática da API (fornecida por [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Documentação Alternativa de APIs { #alternative-api-docs }
E agora, vá para [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Você verá a documentação alternativa automática (fornecida por [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
O **FastAPI** gera um "*schema*" com toda a sua API usando o padrão **OpenAPI** para definir APIs.
#### "*Schema*" { #schema }
Um "*schema*" é uma definição ou descrição de algo. Não o código que o implementa, mas apenas uma descrição abstrata.
#### API "*schema*" { #api-schema }
Nesse caso, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) é uma especificação que determina como definir um *schema* da sua API.
Esta definição de *schema* inclui os paths da sua API, os parâmetros possíveis que eles usam, etc.
#### "*Schema*" de dados { #data-schema }
O termo "*schema*" também pode se referir à forma de alguns dados, como um conteúdo JSON.
Nesse caso, significaria os atributos JSON e os tipos de dados que eles possuem, etc.
#### OpenAPI e JSON Schema { #openapi-and-json-schema }
OpenAPI define um *schema* de API para sua API. E esse *schema* inclui definições (ou "*schemas*") dos dados enviados e recebidos por sua API usando **JSON Schema**, o padrão para *schemas* de dados JSON.
#### Verifique o `openapi.json` { #check-the-openapi-json }
Se você está curioso(a) sobre a aparência do *schema* bruto OpenAPI, o FastAPI gera automaticamente um JSON (*schema*) com as descrições de toda a sua API.
Você pode ver isso diretamente em: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Ele mostrará um JSON começando com algo como:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Para que serve o OpenAPI { #what-is-openapi-for }
O *schema* OpenAPI é o que possibilita os dois sistemas de documentação interativos mostrados.
E existem dezenas de alternativas, todas baseadas em OpenAPI. Você pode facilmente adicionar qualquer uma dessas alternativas à sua aplicação criada com **FastAPI**.
Você também pode usá-lo para gerar código automaticamente para clientes que se comunicam com sua API. Por exemplo, aplicativos front-end, móveis ou IoT.
### Configure o `entrypoint` da aplicação em `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Você pode configurar onde sua aplicação está localizada em um arquivo `pyproject.toml`, assim:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Esse `entrypoint` dirá ao comando `fastapi` que ele deve importar a aplicação assim:
```python
from main import app
```
Se o seu código estiver estruturado assim:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Então você definiria o `entrypoint` como:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
o que seria equivalente a:
```python
from backend.main import app
```
### `fastapi dev` com path { #fastapi-dev-with-path }
Você também pode passar o path do arquivo para o comando `fastapi dev`, e ele vai deduzir o objeto de aplicação FastAPI a ser usado:
```console
$ fastapi dev main.py
```
Mas você teria que lembrar de passar o path correto toda vez que chamar o comando `fastapi`.
Além disso, outras ferramentas podem não conseguir encontrá-la, por exemplo, a [Extensão do VS Code](../editor-support.md) ou a [FastAPI Cloud](https://fastapicloud.com), então é recomendado usar o `entrypoint` no `pyproject.toml`.
### Faça o deploy da sua aplicação (opcional) { #deploy-your-app-optional }
Você pode, opcionalmente, fazer o deploy da sua aplicação FastAPI na [FastAPI Cloud](https://fastapicloud.com); acesse e entre na lista de espera, se ainda não entrou. 🚀
Se você já tem uma conta na **FastAPI Cloud** (nós convidamos você da lista de espera 😉), pode fazer o deploy da sua aplicação com um único comando.
Antes do deploy, certifique-se de que está autenticado:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Em seguida, faça o deploy da sua aplicação:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
É isso! Agora você pode acessar sua aplicação nessa URL. ✨
## Recapitulando, passo a passo { #recap-step-by-step }
### Passo 1: importe `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` é uma classe Python que fornece todas as funcionalidades para sua API.
/// note | Detalhes Técnicos
`FastAPI` é uma classe que herda diretamente de `Starlette`.
Você pode usar todas as funcionalidades do [Starlette](https://www.starlette.dev/) com `FastAPI` também.
///
### Passo 2: crie uma "instância" de `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Aqui, a variável `app` será uma "instância" da classe `FastAPI`.
Este será o principal ponto de interação para criar toda a sua API.
### Passo 3: crie uma operação de rota { #step-3-create-a-path-operation }
#### Path { #path }
"Path" aqui se refere à última parte da URL, começando do primeiro `/`.
Então, em uma URL como:
```
https://example.com/items/foo
```
...o path seria:
```
/items/foo
```
/// info | Informação
Um "path" também é comumente chamado de "endpoint" ou de "rota".
///
Ao construir uma API, o "path" é a principal forma de separar "preocupações" e "recursos".
#### Operação { #operation }
"Operação" aqui se refere a um dos "métodos" HTTP.
Um dos:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...e os mais exóticos:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
No protocolo HTTP, você pode se comunicar com cada path usando um (ou mais) desses "métodos".
---
Ao construir APIs, você normalmente usa esses métodos HTTP para executar uma ação específica.
Normalmente você usa:
* `POST`: para criar dados.
* `GET`: para ler dados.
* `PUT`: para atualizar dados.
* `DELETE`: para deletar dados.
Portanto, no OpenAPI, cada um dos métodos HTTP é chamado de "operação".
Vamos chamá-los de "**operações**" também.
#### Defina um decorador de operação de rota { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
O `@app.get("/")` diz ao **FastAPI** que a função logo abaixo é responsável por tratar as requisições que vão para:
* o path `/`
* usando uma get operação
/// info | Informações sobre `@decorator`
Essa sintaxe `@alguma_coisa` em Python é chamada de "decorador".
Você o coloca em cima de uma função. Como um chapéu decorativo (acho que é daí que vem o termo).
Um "decorador" pega a função abaixo e faz algo com ela.
Em nosso caso, este decorador informa ao **FastAPI** que a função abaixo corresponde ao **path** `/` com uma **operação** `get`.
É o "**decorador de operação de rota**".
///
Você também pode usar as outras operações:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
E os mais exóticos:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Dica
Você está livre para usar cada operação (método HTTP) como desejar.
O **FastAPI** não impõe nenhum significado específico.
As informações aqui são apresentadas como uma orientação, não uma exigência.
Por exemplo, ao usar GraphQL, você normalmente executa todas as ações usando apenas operações `POST`.
///
### Passo 4: defina a **função de operação de rota** { #step-4-define-the-path-operation-function }
Esta é a nossa "**função de operação de rota**":
* **path**: é `/`.
* **operação**: é `get`.
* **função**: é a função abaixo do "decorador" (abaixo do `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Esta é uma função Python.
Ela será chamada pelo **FastAPI** sempre que receber uma requisição para a URL "`/`" usando uma operação `GET`.
Neste caso, é uma função `async`.
---
Você também pode defini-la como uma função normal em vez de `async def`:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Nota
Se você não sabe a diferença, verifique o [Async: *"Com pressa?"*](../async.md#in-a-hurry).
///
### Passo 5: retorne o conteúdo { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Você pode retornar um `dict`, `list` e valores singulares como `str`, `int`, etc.
Você também pode devolver modelos Pydantic ( você verá mais sobre isso mais tarde).
Existem muitos outros objetos e modelos que serão convertidos automaticamente para JSON (incluindo ORMs, etc). Tente usar seus favoritos, é altamente provável que já sejam compatíveis.
### Passo 6: Faça o deploy { #step-6-deploy-it }
Faça o deploy da sua aplicação para a **[FastAPI Cloud](https://fastapicloud.com)** com um comando: `fastapi deploy`. 🎉
#### Sobre o FastAPI Cloud { #about-fastapi-cloud }
A **[FastAPI Cloud](https://fastapicloud.com)** é construída pelo mesmo autor e equipe por trás do **FastAPI**.
Ela simplifica o processo de **construir**, **fazer deploy** e **acessar** uma API com o mínimo de esforço.
Traz a mesma **experiência do desenvolvedor** de criar aplicações com FastAPI para **fazer o deploy** delas na nuvem. 🎉
A FastAPI Cloud é a principal patrocinadora e financiadora dos projetos open source do ecossistema *FastAPI and friends*. ✨
#### Faça o deploy em outros provedores de nuvem { #deploy-to-other-cloud-providers }
FastAPI é open source e baseado em padrões. Você pode fazer deploy de aplicações FastAPI em qualquer provedor de nuvem que preferir.
Siga os tutoriais do seu provedor de nuvem para fazer deploy de aplicações FastAPI com eles. 🤓
## Recapitulando { #recap }
* Importe `FastAPI`.
* Crie uma instância do `app`.
* Escreva um **decorador de operação de rota** usando decoradores como `@app.get("/")`.
* Defina uma **função de operação de rota**; por exemplo, `def root(): ...`.
* Execute o servidor de desenvolvimento usando o comando `fastapi dev`.
* Opcionalmente, faça o deploy da sua aplicação com `fastapi deploy`.
================================================
FILE: docs/pt/docs/tutorial/handling-errors.md
================================================
# Manipulação de erros { #handling-errors }
Há diversas situações em que você precisa notificar um erro a um cliente que está utilizando a sua API.
Esse cliente pode ser um browser com um frontend, o código de outra pessoa, um dispositivo IoT, etc.
Pode ser que você precise comunicar ao cliente que:
* O cliente não tem direitos para realizar aquela operação.
* O cliente não tem acesso aquele recurso.
* O item que o cliente está tentando acessar não existe.
* etc.
Nesses casos, você normalmente retornaria um **HTTP status code** próximo ao status code na faixa do status code **400** (do 400 ao 499).
Isso é bastante similar ao caso do HTTP status code 200 (do 200 ao 299). Esses "200" status codes significam que, de algum modo, houve sucesso na requisição.
Os status codes na faixa dos 400 significam que houve um erro por parte do cliente.
Você se lembra de todos aqueles erros (e piadas) a respeito do "**404 Not Found**"?
## Use o `HTTPException` { #use-httpexception }
Para retornar ao cliente *responses* HTTP com erros, use o `HTTPException`.
### Import `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Lance o `HTTPException` no seu código { #raise-an-httpexception-in-your-code }
`HTTPException`, ao fundo, nada mais é do que a conjunção entre uma exceção comum do Python e informações adicionais relevantes para APIs.
E porque é uma exceção do Python, você não **retorna** (return) o `HTTPException`, você lança o (raise) no seu código.
Isso também significa que, se você está escrevendo uma função de utilidade, a qual você está chamando dentro da sua função de operação de rota, e você lança o `HTTPException` dentro da função de utilidade, o resto do seu código não será executado dentro da função de operação de rota. Ao contrário, o `HTTPException` irá finalizar a requisição no mesmo instante e enviará o erro HTTP oriundo do `HTTPException` para o cliente.
O benefício de lançar uma exceção em vez de retornar um valor ficará mais evidente na seção sobre Dependências e Segurança.
Neste exemplo, quando o cliente pede, na requisição, por um item cujo ID não existe, a exceção com o status code `404` é lançada:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### A response resultante { #the-resulting-response }
Se o cliente faz uma requisição para `http://example.com/items/foo` (um `item_id` `"foo"`), esse cliente receberá um HTTP status code 200, e uma resposta JSON:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Mas se o cliente faz uma requisição para `http://example.com/items/bar` (ou seja, um não existente `item_id "bar"`), esse cliente receberá um HTTP status code 404 (o erro "não encontrado" — *not found error*), e uma resposta JSON:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Dica
Quando você lançar um `HTTPException`, você pode passar qualquer valor convertível em JSON como parâmetro de `detail`, e não apenas `str`.
Você pode passar um `dict` ou um `list`, etc.
Esses tipos de dados são manipulados automaticamente pelo **FastAPI** e convertidos em JSON.
///
## Adicione headers customizados { #add-custom-headers }
Há certas situações em que é bastante útil poder adicionar headers customizados no HTTP error. Exemplo disso seria adicionar headers customizados para tipos de segurança.
Você provavelmente não precisará utilizar esses headers diretamente no seu código.
Mas caso você precise, para um cenário mais complexo, você pode adicionar headers customizados:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Instale manipuladores de exceções customizados { #install-custom-exception-handlers }
Você pode adicionar manipuladores de exceção customizados com [a mesma seção de utilidade de exceções presentes no Starlette](https://www.starlette.dev/exceptions/).
Digamos que você tenha uma exceção customizada `UnicornException` que você (ou uma biblioteca que você use) precise lançar (`raise`).
Nesse cenário, se você precisa manipular essa exceção de modo global com o FastAPI, você pode adicionar um manipulador de exceção customizada com `@app.exception_handler()`.
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Nesse cenário, se você fizer uma requisição para `/unicorns/yolo`, a *operação de rota* vai lançar (`raise`) o `UnicornException`.
Essa exceção será manipulada, contudo, pelo `unicorn_exception_handler`.
Dessa forma você receberá um erro "limpo", com o HTTP status code `418` e um JSON com o conteúdo:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Detalhes Técnicos
Você também pode usar `from starlette.requests import Request` e `from starlette.responses import JSONResponse`.
**FastAPI** disponibiliza o mesmo `starlette.responses` através do `fastapi.responses` por conveniência ao desenvolvedor. Contudo, a maior parte das respostas disponíveis vem diretamente do Starlette. O mesmo acontece com o `Request`.
///
## Sobrescreva os manipuladores de exceções padrão { #override-the-default-exception-handlers }
**FastAPI** tem alguns manipuladores padrão de exceções.
Esses manipuladores são os responsáveis por retornar o JSON padrão de respostas quando você lança (`raise`) o `HTTPException` e quando a requisição tem dados inválidos.
Você pode sobrescrever esses manipuladores de exceção com os seus próprios manipuladores.
### Sobrescreva exceções de validação da requisição { #override-request-validation-exceptions }
Quando a requisição contém dados inválidos, **FastAPI** internamente lança para o `RequestValidationError`.
E também inclui um manipulador de exceções padrão para ele.
Para sobrescrevê-lo, importe o `RequestValidationError` e use-o com o `@app.exception_handler(RequestValidationError)` para decorar o manipulador de exceções.
O manipulador de exceções receberá um `Request` e a exceção.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Se você for ao `/items/foo`, em vez de receber o JSON padrão com o erro:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
você receberá a versão em texto:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Sobrescreva o manipulador de erro `HTTPException` { #override-the-httpexception-error-handler }
Do mesmo modo, você pode sobrescrever o `HTTPException`.
Por exemplo, você pode querer retornar uma *response* em *plain text* ao invés de um JSON para os seguintes erros:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Detalhes Técnicos
Você pode usar `from starlette.responses import PlainTextResponse`.
**FastAPI** disponibiliza o mesmo `starlette.responses` como `fastapi.responses`, como conveniência a você, desenvolvedor. Contudo, a maior parte das respostas disponíveis vem diretamente do Starlette.
///
/// warning | Atenção
Tenha em mente que o `RequestValidationError` contém as informações do nome do arquivo e da linha onde o erro de validação acontece, para que você possa mostrá-las nos seus logs com as informações relevantes, se quiser.
Mas isso significa que, se você simplesmente convertê-lo para uma string e retornar essa informação diretamente, você pode acabar vazando um pouco de informação sobre o seu sistema; por isso, aqui o código extrai e mostra cada erro de forma independente.
///
### Use o body do `RequestValidationError` { #use-the-requestvalidationerror-body }
O `RequestValidationError` contém o `body` que ele recebeu de dados inválidos.
Você pode utilizá-lo enquanto desenvolve seu app para registrar o *body* e debugá-lo, e assim retorná-lo ao usuário, etc.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Tente enviar um item inválido como este:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Você receberá uma *response* informando-o de que os dados são inválidos, e contendo o *body* recebido:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### O `HTTPException` do FastAPI vs o `HTTPException` do Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
O **FastAPI** tem o seu próprio `HTTPException`.
E a classe de erro `HTTPException` do **FastAPI** herda da classe de erro do `HTTPException` do Starlette.
A única diferença é que o `HTTPException` do **FastAPI** aceita qualquer dado que possa ser convertido em JSON para o campo `detail`, enquanto o `HTTPException` do Starlette aceita apenas strings para esse campo.
Portanto, você pode continuar lançando o `HTTPException` do **FastAPI** normalmente no seu código.
Porém, quando você registrar um manipulador de exceção, você deve registrá-lo através do `HTTPException` do Starlette.
Dessa forma, se qualquer parte do código interno, extensão ou plug-in do Starlette lançar um `HTTPException` do Starlette, o seu manipulador poderá capturar e tratá-lo.
Neste exemplo, para poder ter ambos os `HTTPException` no mesmo código, a exceção do Starlette é renomeada para `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Reutilize os manipuladores de exceção do **FastAPI** { #reuse-fastapis-exception-handlers }
Se você quer usar a exceção em conjunto com o mesmo manipulador de exceção *default* do **FastAPI**, você pode importar e re-usar esses manipuladores de exceção do `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
Nesse exemplo você apenas imprime (`print`) o erro com uma mensagem expressiva. Mesmo assim, dá para pegar a ideia. Você pode usar a exceção e então apenas re-usar o manipulador de exceção *default*.
================================================
FILE: docs/pt/docs/tutorial/header-param-models.md
================================================
# Modelos de Parâmetros do Cabeçalho { #header-parameter-models }
Se você possui um grupo de **parâmetros de cabeçalho** relacionados, você pode criar um **modelo do Pydantic** para declará-los.
Isso vai lhe permitir **reusar o modelo** em **múltiplos lugares** e também declarar validações e metadados para todos os parâmetros de uma vez. 😎
/// note | Nota
Isso é possível desde a versão `0.115.0` do FastAPI. 🤓
///
## Parâmetros do Cabeçalho com um Modelo Pydantic { #header-parameters-with-a-pydantic-model }
Declare os **parâmetros de cabeçalho** que você precisa em um **modelo do Pydantic**, e então declare o parâmetro como `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
O **FastAPI** irá **extrair** os dados de **cada campo** a partir dos **cabeçalhos** da requisição e te retornará o modelo do Pydantic que você definiu.
## Checando a documentação { #check-the-docs }
Você pode ver os headers necessários na interface gráfica da documentação em `/docs`:
## Proibindo Cabeçalhos adicionais { #forbid-extra-headers }
Em alguns casos de uso especiais (provavelmente não muito comuns), você pode querer **restringir** os cabeçalhos que você quer receber.
Você pode usar a configuração dos modelos do Pydantic para proibir (`forbid`) quaisquer campos `extra`:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Se um cliente tentar enviar alguns **cabeçalhos extra**, eles irão receber uma resposta de **erro**.
Por exemplo, se o cliente tentar enviar um cabeçalho `tool` com o valor `plumbus`, ele irá receber uma resposta de **erro** informando que o parâmetro do cabeçalho `tool` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Desativar conversão de underscores { #disable-convert-underscores }
Da mesma forma que com parâmetros de cabeçalho normais, quando você tem caracteres de sublinhado nos nomes dos parâmetros, eles são **automaticamente convertidos em hifens**.
Por exemplo, se você tem um parâmetro de cabeçalho `save_data` no código, o cabeçalho HTTP esperado será `save-data`, e ele aparecerá assim na documentação.
Se por algum motivo você precisar desativar essa conversão automática, também poderá fazê-lo para modelos do Pydantic para parâmetros de cabeçalho.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Atenção
Antes de definir `convert_underscores` como `False`, tenha em mente que alguns proxies e servidores HTTP não permitem o uso de cabeçalhos com sublinhados.
///
## Resumo { #summary }
Você pode utilizar **modelos do Pydantic** para declarar **cabeçalhos** no **FastAPI**. 😎
================================================
FILE: docs/pt/docs/tutorial/header-params.md
================================================
# Parâmetros de Cabeçalho { #header-parameters }
Você pode definir parâmetros de Cabeçalho da mesma maneira que define paramêtros com `Query`, `Path` e `Cookie`.
## Importe `Header` { #import-header }
Primeiro importe `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Declare parâmetros de `Header` { #declare-header-parameters }
Então declare os paramêtros de cabeçalho usando a mesma estrutura que em `Path`, `Query` e `Cookie`.
Você pode definir o valor padrão, assim como todas as validações extras ou parâmetros de anotação:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Detalhes Técnicos
`Header` é uma classe "irmã" de `Path`, `Query` e `Cookie`. Ela também herda da mesma classe em comum `Param`.
Mas lembre-se que quando você importa `Query`, `Path`, `Header`, e outras de `fastapi`, elas são na verdade funções que retornam classes especiais.
///
/// info | Informação
Para declarar headers, você precisa usar `Header`, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta.
///
## Conversão automática { #automatic-conversion }
`Header` tem algumas funcionalidades a mais em relação a `Path`, `Query` e `Cookie`.
A maioria dos cabeçalhos padrão são separados pelo caractere "hífen", também conhecido como "sinal de menos" (`-`).
Mas uma variável como `user-agent` é inválida em Python.
Portanto, por padrão, `Header` converterá os caracteres de nomes de parâmetros de sublinhado (`_`) para hífen (`-`) para extrair e documentar os cabeçalhos.
Além disso, os cabeçalhos HTTP não diferenciam maiúsculas de minúsculas, portanto, você pode declará-los com o estilo padrão do Python (também conhecido como "snake_case").
Portanto, você pode usar `user_agent` como faria normalmente no código Python, em vez de precisar colocar as primeiras letras em maiúsculas como `User_Agent` ou algo semelhante.
Se por algum motivo você precisar desabilitar a conversão automática de sublinhados para hífens, defina o parâmetro `convert_underscores` de `Header` para `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Atenção
Antes de definir `convert_underscores` como `False`, lembre-se de que alguns proxies e servidores HTTP não permitem o uso de cabeçalhos com sublinhados.
///
## Cabeçalhos duplicados { #duplicate-headers }
É possível receber cabeçalhos duplicados. Isso significa, o mesmo cabeçalho com vários valores.
Você pode definir esses casos usando uma lista na declaração de tipo.
Você receberá todos os valores do cabeçalho duplicado como uma `list` Python.
Por exemplo, para declarar um cabeçalho de `X-Token` que pode aparecer mais de uma vez, você pode escrever:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Se você se comunicar com essa *operação de rota* enviando dois cabeçalhos HTTP como:
```
X-Token: foo
X-Token: bar
```
A resposta seria como:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Recapitulando { #recap }
Declare cabeçalhos com `Header`, usando o mesmo padrão comum que utiliza-se em `Query`, `Path` e `Cookie`.
E não se preocupe com sublinhados em suas variáveis, **FastAPI** cuidará da conversão deles.
================================================
FILE: docs/pt/docs/tutorial/index.md
================================================
# Tutorial - Guia de Usuário { #tutorial-user-guide }
Esse tutorial mostra como usar o **FastAPI** com a maior parte de seus recursos, passo a passo.
Cada seção constrói, gradualmente, sobre as anteriores, mas sua estrutura são tópicos separados, para que você possa ir a qualquer um específico e resolver suas necessidades específicas de API.
Ele também foi construído para servir como uma referência futura, então você pode voltar e ver exatamente o que você precisa.
## Rode o código { #run-the-code }
Todos os blocos de código podem ser copiados e utilizados diretamente (eles são, na verdade, arquivos Python testados).
Para rodar qualquer um dos exemplos, copie o código para um arquivo `main.py`, e inicie o `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
É **ALTAMENTE recomendado** que você escreva ou copie o código, edite-o e rode-o localmente.
Usá-lo em seu editor é o que realmente te mostra os benefícios do FastAPI, ver quão pouco código você tem que escrever, todas as conferências de tipo, preenchimento automático, etc.
---
## Instale o FastAPI { #install-fastapi }
O primeiro passo é instalar o FastAPI.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e então **instalar o FastAPI**:
/// note | Nota
Quando você instala com `pip install "fastapi[standard]"`, ele vem com algumas dependências opcionais padrão, incluindo `fastapi-cloud-cli`, que permite fazer deploy na [FastAPI Cloud](https://fastapicloud.com).
Se você não quiser ter essas dependências opcionais, pode instalar `pip install fastapi` em vez disso.
Se você quiser instalar as dependências padrão, mas sem o `fastapi-cloud-cli`, você pode instalar com `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip | Dica
O FastAPI tem uma [extensão oficial para o VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (e para o Cursor), que fornece vários recursos, incluindo um explorador de operações de rota, busca de operações de rota, navegação CodeLens em testes (ir para a definição a partir dos testes) e deploy e logs da FastAPI Cloud, tudo direto do seu editor.
///
## Guia Avançado de Usuário { #advanced-user-guide }
Há também um **Guia Avançado de Usuário** que você pode ler após esse **Tutorial - Guia de Usuário**.
O **Guia Avançado de Usuário** constrói sobre esse, usa os mesmos conceitos e te ensina algumas funcionalidades extras.
Mas você deveria ler primeiro o **Tutorial - Guia de Usuário** (que você está lendo agora).
Ele foi projetado para que você possa construir uma aplicação completa com apenas o **Tutorial - Guia de Usuário**, e então estendê-la de diferentes formas, dependendo das suas necessidades, usando algumas ideias adicionais do **Guia Avançado de Usuário**.
================================================
FILE: docs/pt/docs/tutorial/metadata.md
================================================
# Metadados e URLs da Documentação { #metadata-and-docs-urls }
Você pode personalizar várias configurações de metadados na sua aplicação **FastAPI**.
## Metadados para API { #metadata-for-api }
Você pode definir os seguintes campos que são usados na especificação OpenAPI e nas interfaces automáticas de documentação da API:
| Parâmetro | Tipo | Descrição |
|------------|------|-------------|
| `title` | `str` | O título da API. |
| `summary` | `str` | Um breve resumo da API. Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Uma breve descrição da API. Pode usar Markdown. |
| `version` | `string` | A versão da API. Esta é a versão da sua aplicação, não do OpenAPI. Por exemplo, `2.5.0`. |
| `terms_of_service` | `str` | Uma URL para os Termos de Serviço da API. Se fornecido, deve ser uma URL. |
| `contact` | `dict` | As informações de contato da API exposta. Pode conter vários campos. Campos de contact
Parâmetro
Tipo
Descrição
name
str
O nome identificador da pessoa/organização de contato.
url
str
A URL que aponta para as informações de contato. DEVE estar no formato de uma URL.
email
str
O endereço de e-mail da pessoa/organização de contato. DEVE estar no formato de um endereço de e-mail.
|
| `license_info` | `dict` | As informações de licença para a API exposta. Ela pode conter vários campos. Campos de license_info
Parâmetro
Tipo
Descrição
name
str
OBRIGATÓRIO (se um license_info for definido). O nome da licença usada para a API.
identifier
str
Uma expressão de licença [SPDX](https://spdx.org/licenses/) para a API. O campo identifier é mutuamente exclusivo do campo url. Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
Uma URL para a licença usada para a API. DEVE estar no formato de uma URL.
|
Você pode defini-los da seguinte maneira:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Dica
Você pode escrever Markdown no campo `description` e ele será renderizado na saída.
///
Com essa configuração, a documentação automática da API se pareceria com:
## Identificador de Licença { #license-identifier }
Desde o OpenAPI 3.1.0 e FastAPI 0.99.0, você também pode definir o `license_info` com um `identifier` em vez de uma `url`.
Por exemplo:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Metadados para tags { #metadata-for-tags }
Você também pode adicionar metadados adicionais para as diferentes tags usadas para agrupar suas operações de rota com o parâmetro `openapi_tags`.
Ele recebe uma lista contendo um dicionário para cada tag.
Cada dicionário pode conter:
* `name` (**obrigatório**): uma `str` com o mesmo nome da tag que você usa no parâmetro `tags` nas suas *operações de rota* e `APIRouter`s.
* `description`: uma `str` com uma breve descrição da tag. Pode conter Markdown e será exibido na interface de documentação.
* `externalDocs`: um `dict` descrevendo a documentação externa com:
* `description`: uma `str` com uma breve descrição da documentação externa.
* `url` (**obrigatório**): uma `str` com a URL da documentação externa.
### Criar Metadados para tags { #create-metadata-for-tags }
Vamos tentar isso em um exemplo com tags para `users` e `items`.
Crie metadados para suas tags e passe-os para o parâmetro `openapi_tags`:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Observe que você pode usar Markdown dentro das descrições. Por exemplo, "login" será exibido em negrito (**login**) e "fancy" será exibido em itálico (_fancy_).
/// tip | Dica
Você não precisa adicionar metadados para todas as tags que você usa.
///
### Use suas tags { #use-your-tags }
Use o parâmetro `tags` com suas *operações de rota* (e `APIRouter`s) para atribuí-los a diferentes tags:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Informação
Leia mais sobre tags em [Configuração de operação de rota](path-operation-configuration.md#tags).
///
### Cheque os documentos { #check-the-docs }
Agora, se você verificar a documentação, ela exibirá todos os metadados adicionais:
### Ordem das tags { #order-of-tags }
A ordem de cada dicionário de metadados de tag também define a ordem exibida na interface de documentação.
Por exemplo, embora `users` apareça após `items` em ordem alfabética, ele é exibido antes deles, porque adicionamos seus metadados como o primeiro dicionário na lista.
## URL da OpenAPI { #openapi-url }
Por padrão, o esquema OpenAPI é servido em `/openapi.json`.
Mas você pode configurá-lo com o parâmetro `openapi_url`.
Por exemplo, para defini-lo para ser servido em `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Se você quiser desativar completamente o esquema OpenAPI, pode definir `openapi_url=None`, o que também desativará as interfaces de documentação que o utilizam.
## URLs da Documentação { #docs-urls }
Você pode configurar as duas interfaces de documentação incluídas:
* **Swagger UI**: acessível em `/docs`.
* Você pode definir sua URL com o parâmetro `docs_url`.
* Você pode desativá-la definindo `docs_url=None`.
* **ReDoc**: acessível em `/redoc`.
* Você pode definir sua URL com o parâmetro `redoc_url`.
* Você pode desativá-la definindo `redoc_url=None`.
Por exemplo, para definir o Swagger UI para ser servido em `/documentation` e desativar o ReDoc:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/pt/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
Você pode adicionar middleware à suas aplicações **FastAPI**.
Um "middleware" é uma função que manipula cada **requisição** antes de ser processada por qualquer *operação de rota* específica. E também cada **resposta** antes de retorná-la.
* Ele pega cada **requisição** que chega ao seu aplicativo.
* Ele pode então fazer algo com essa **requisição** ou executar qualquer código necessário.
* Então ele passa a **requisição** para ser processada pelo resto do aplicativo (por alguma *operação de rota*).
* Ele então pega a **resposta** gerada pelo aplicativo (por alguma *operação de rota*).
* Ele pode fazer algo com essa **resposta** ou executar qualquer código necessário.
* Então ele retorna a **resposta**.
/// note | Detalhes Técnicos
Se você tiver dependências com `yield`, o código de saída será executado *depois* do middleware.
Se houver alguma tarefa em segundo plano (abordada na seção [Tarefas em segundo plano](background-tasks.md), que você verá mais adiante), ela será executada *depois* de todo o middleware.
///
## Criar um middleware { #create-a-middleware }
Para criar um middleware, use o decorador `@app.middleware("http")` logo acima de uma função.
A função middleware recebe:
* A `request`.
* Uma função `call_next` que receberá o `request` como um parâmetro.
* Esta função passará a `request` para a *operação de rota* correspondente.
* Então ela retorna a `response` gerada pela *operação de rota* correspondente.
* Você pode então modificar ainda mais o `response` antes de retorná-lo.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Dica
Tenha em mente que cabeçalhos proprietários personalizados podem ser adicionados [usando o prefixo `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Mas se você tiver cabeçalhos personalizados desejando que um cliente em um navegador esteja apto a ver, você precisa adicioná-los às suas configurações CORS ([CORS (Cross-Origin Resource Sharing)](cors.md)) usando o parâmetro `expose_headers` documentado na [Documentação CORS da Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Detalhes Técnicos
Você também pode usar `from starlette.requests import Request`.
**FastAPI** fornece isso como uma conveniência para você, o desenvolvedor. Mas vem diretamente da Starlette.
///
### Antes e depois da `response` { #before-and-after-the-response }
Você pode adicionar código para ser executado com a `request`, antes que qualquer *operação de rota* o receba.
E também depois que a `response` é gerada, antes de retorná-la.
Por exemplo, você pode adicionar um cabeçalho personalizado `X-Process-Time` contendo o tempo em segundos que levou para processar a solicitação e gerar uma resposta:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Dica
Aqui usamos [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) em vez de `time.time()` porque ele pode ser mais preciso para esses casos de uso. 🤓
///
## Ordem de execução de múltiplos middlewares { #multiple-middleware-execution-order }
Quando você adiciona múltiplos middlewares usando o decorador `@app.middleware()` ou o método `app.add_middleware()`, cada novo middleware envolve a aplicação, formando uma pilha. O último middleware adicionado é o mais externo, e o primeiro é o mais interno.
No caminho da requisição, o middleware mais externo roda primeiro.
No caminho da resposta, ele roda por último.
Por exemplo:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Isso resulta na seguinte ordem de execução:
* **Requisição**: MiddlewareB → MiddlewareA → rota
* **Resposta**: rota → MiddlewareA → MiddlewareB
Esse comportamento de empilhamento garante que os middlewares sejam executados em uma ordem previsível e controlável.
## Outros middlewares { #other-middlewares }
Mais tarde, você pode ler mais sobre outros middlewares no [Guia do usuário avançado: Middleware avançado](../advanced/middleware.md).
Você lerá sobre como manipular CORS com um middleware na próxima seção.
================================================
FILE: docs/pt/docs/tutorial/path-operation-configuration.md
================================================
# Configuração da Operação de Rota { #path-operation-configuration }
Existem vários parâmetros que você pode passar para o seu *decorador de operação de rota* para configurá-lo.
/// warning | Atenção
Observe que esses parâmetros são passados diretamente para o *decorador de operação de rota*, não para a sua *função de operação de rota*.
///
## Código de Status da Resposta { #response-status-code }
Você pode definir o `status_code` (HTTP) para ser usado na resposta da sua *operação de rota*.
Você pode passar diretamente o código `int`, como `404`.
Mas se você não se lembrar o que cada código numérico significa, pode usar as constantes de atalho em `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Esse código de status será usado na resposta e será adicionado ao esquema OpenAPI.
/// note | Detalhes Técnicos
Você também poderia usar `from starlette import status`.
**FastAPI** fornece o mesmo `starlette.status` como `fastapi.status` apenas como uma conveniência para você, o desenvolvedor. Mas vem diretamente do Starlette.
///
## Tags { #tags }
Você pode adicionar tags para sua *operação de rota*, passe o parâmetro `tags` com uma `list` de `str` (comumente apenas um `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Eles serão adicionados ao esquema OpenAPI e usados pelas interfaces de documentação automática:
### Tags com Enums { #tags-with-enums }
Se você tem uma grande aplicação, você pode acabar acumulando **várias tags**, e você gostaria de ter certeza de que você sempre usa a ** mesma tag** para *operações de rota* relacionadas.
Nestes casos, pode fazer sentido armazenar as tags em um `Enum`.
**FastAPI** suporta isso da mesma maneira que com strings simples:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Resumo e descrição { #summary-and-description }
Você pode adicionar um `summary` e uma `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Descrição do docstring { #description-from-docstring }
Como as descrições tendem a ser longas e cobrir várias linhas, você pode declarar a descrição da *operação de rota* na docstring da função e o **FastAPI** irá lê-la de lá.
Você pode escrever [Markdown](https://en.wikipedia.org/wiki/Markdown) na docstring, ele será interpretado e exibido corretamente (levando em conta a indentação da docstring).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Ela será usada nas documentações interativas:
## Descrição da resposta { #response-description }
Você pode especificar a descrição da resposta com o parâmetro `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Informação
Note que `response_description` se refere especificamente à resposta, a `description` se refere à *operação de rota* em geral.
///
/// check | Verifique
OpenAPI especifica que cada *operação de rota* requer uma descrição de resposta.
Então, se você não fornecer uma, o **FastAPI** irá gerar automaticamente uma de "Resposta bem-sucedida".
///
## Descontinuar uma *operação de rota* { #deprecate-a-path-operation }
Se você precisar marcar uma *operação de rota* como descontinuada, mas sem removê-la, passe o parâmetro `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Ela será claramente marcada como descontinuada nas documentações interativas:
Verifique como *operações de rota* descontinuadas e não descontinuadas se parecem:
## Resumindo { #recap }
Você pode configurar e adicionar metadados para suas *operações de rota* facilmente passando parâmetros para os *decoradores de operação de rota*.
================================================
FILE: docs/pt/docs/tutorial/path-params-numeric-validations.md
================================================
# Parâmetros de path e validações numéricas { #path-parameters-and-numeric-validations }
Da mesma forma que você pode declarar mais validações e metadados para parâmetros de consulta com `Query`, você pode declarar o mesmo tipo de validações e metadados para parâmetros de path com `Path`.
## Importe `Path` { #import-path }
Primeiro, importe `Path` de `fastapi`, e importe `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Informação
O FastAPI adicionou suporte a `Annotated` (e passou a recomendá-lo) na versão 0.95.0.
Se você tiver uma versão mais antiga, verá erros ao tentar usar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) para pelo menos 0.95.1 antes de usar `Annotated`.
///
## Declare metadados { #declare-metadata }
Você pode declarar todos os mesmos parâmetros que em `Query`.
Por exemplo, para declarar um valor de metadado `title` para o parâmetro de path `item_id` você pode digitar:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Nota
Um parâmetro de path é sempre obrigatório, pois precisa fazer parte do path. Mesmo que você o declare como `None` ou defina um valor padrão, isso não afetaria nada, ele ainda seria sempre obrigatório.
///
## Ordene os parâmetros de acordo com sua necessidade { #order-the-parameters-as-you-need }
/// tip | Dica
Isso provavelmente não é tão importante ou necessário se você usar `Annotated`.
///
Vamos supor que você queira declarar o parâmetro de consulta `q` como uma `str` obrigatória.
E você não precisa declarar mais nada para esse parâmetro, então você realmente não precisa usar `Query`.
Mas você ainda precisa usar `Path` para o parâmetro de path `item_id`. E você não quer usar `Annotated` por algum motivo.
O Python vai reclamar se você colocar um valor com “padrão” antes de um valor que não tem “padrão”.
Mas você pode reordená-los e colocar primeiro o valor sem padrão (o parâmetro de consulta `q`).
Isso não faz diferença para o **FastAPI**. Ele vai detectar os parâmetros pelos seus nomes, tipos e declarações de padrão (`Query`, `Path`, etc.), sem se importar com a ordem.
Então, você pode declarar sua função assim:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Mas tenha em mente que, se você usar `Annotated`, você não terá esse problema, não fará diferença, pois você não está usando valores padrão de parâmetros de função para `Query()` ou `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Ordene os parâmetros de acordo com sua necessidade, truques { #order-the-parameters-as-you-need-tricks }
/// tip | Dica
Isso provavelmente não é tão importante ou necessário se você usar `Annotated`.
///
Aqui vai um pequeno truque que pode ser útil, mas você não vai precisar dele com frequência.
Se você quiser:
* declarar o parâmetro de consulta `q` sem um `Query` nem qualquer valor padrão
* declarar o parâmetro de path `item_id` usando `Path`
* tê-los em uma ordem diferente
* não usar `Annotated`
...o Python tem uma pequena sintaxe especial para isso.
Passe `*`, como o primeiro parâmetro da função.
O Python não fará nada com esse `*`, mas saberá que todos os parâmetros seguintes devem ser chamados como argumentos nomeados (pares chave-valor), também conhecidos como kwargs. Mesmo que eles não tenham um valor padrão.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Melhor com `Annotated` { #better-with-annotated }
Tenha em mente que, se você usar `Annotated`, como você não está usando valores padrão de parâmetros de função, você não terá esse problema e provavelmente não precisará usar `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Validações numéricas: maior que ou igual { #number-validations-greater-than-or-equal }
Com `Query` e `Path` (e outras que você verá depois) você pode declarar restrições numéricas.
Aqui, com `ge=1`, `item_id` precisará ser um número inteiro “`g`reater than or `e`qual” a `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Validações numéricas: maior que e menor que ou igual { #number-validations-greater-than-and-less-than-or-equal }
O mesmo se aplica a:
* `gt`: maior que (`g`reater `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Validações numéricas: floats, maior que e menor que { #number-validations-floats-greater-than-and-less-than }
Validações numéricas também funcionam para valores `float`.
Aqui é onde se torna importante poder declarar gt e não apenas ge. Com isso você pode exigir, por exemplo, que um valor seja maior que `0`, mesmo que seja menor que `1`.
Assim, `0.5` seria um valor válido. Mas `0.0` ou `0` não seriam.
E o mesmo para lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Recapitulando { #recap }
Com `Query`, `Path` (e outras que você ainda não viu) você pode declarar metadados e validações de string do mesmo modo que em [Parâmetros de consulta e validações de string](query-params-str-validations.md).
E você também pode declarar validações numéricas:
* `gt`: maior que (`g`reater `t`han)
* `ge`: maior que ou igual (`g`reater than or `e`qual)
* `lt`: menor que (`l`ess `t`han)
* `le`: menor que ou igual (`l`ess than or `e`qual)
/// info | Informação
`Query`, `Path` e outras classes que você verá depois são subclasses de uma classe comum `Param`.
Todas elas compartilham os mesmos parâmetros para validação adicional e metadados que você viu.
///
/// note | Detalhes Técnicos
Quando você importa `Query`, `Path` e outras de `fastapi`, elas são na verdade funções.
Que, quando chamadas, retornam instâncias de classes de mesmo nome.
Então, você importa `Query`, que é uma função. E quando você a chama, ela retorna uma instância de uma classe também chamada `Query`.
Essas funções existem (em vez de usar diretamente as classes) para que seu editor não marque erros sobre seus tipos.
Dessa forma, você pode usar seu editor e ferramentas de codificação normais sem precisar adicionar configurações personalizadas para desconsiderar esses erros.
///
================================================
FILE: docs/pt/docs/tutorial/path-params.md
================================================
# Parâmetros de path { #path-parameters }
Você pode declarar "parâmetros" ou "variáveis" de path com a mesma sintaxe usada por strings de formatação do Python:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
O valor do parâmetro de path `item_id` será passado para a sua função como o argumento `item_id`.
Então, se você executar este exemplo e acessar [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), você verá uma resposta:
```JSON
{"item_id":"foo"}
```
## Parâmetros de path com tipos { #path-parameters-with-types }
Você pode declarar o tipo de um parâmetro de path na função, usando as anotações de tipo padrão do Python:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Neste caso, `item_id` é declarado como um `int`.
/// check | Verifique
Isso fornecerá suporte do editor dentro da sua função, com verificações de erros, preenchimento automático, etc.
///
## Dados conversão { #data-conversion }
Se você executar este exemplo e abrir seu navegador em [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), você verá uma resposta:
```JSON
{"item_id":3}
```
/// check | Verifique
Perceba que o valor que sua função recebeu (e retornou) é `3`, como um `int` do Python, não uma string `"3"`.
Então, com essa declaração de tipo, o **FastAPI** fornece "parsing" automático do request.
///
## Validação de dados { #data-validation }
Mas se você for no navegador para [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), verá um bom erro HTTP:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
porque o parâmetro de path `item_id` tinha o valor `"foo"`, que não é um `int`.
O mesmo erro apareceria se você fornecesse um `float` em vez de um `int`, como em: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Verifique
Então, com a mesma declaração de tipo do Python, o **FastAPI** fornece validação de dados.
Observe que o erro também declara claramente exatamente o ponto onde a validação não passou.
Isso é incrivelmente útil ao desenvolver e depurar código que interage com sua API.
///
## Documentação { #documentation }
E quando você abrir seu navegador em [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), você verá documentação automática, interativa, da API como:
/// check | Verifique
Novamente, apenas com a mesma declaração de tipo do Python, o **FastAPI** fornece documentação automática e interativa (integrando o Swagger UI).
Observe que o parâmetro de path está declarado como um inteiro.
///
## Benefícios baseados em padrões, documentação alternativa { #standards-based-benefits-alternative-documentation }
E como o schema gerado é do padrão [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md), existem muitas ferramentas compatíveis.
Por causa disso, o próprio **FastAPI** fornece uma documentação alternativa da API (usando ReDoc), que você pode acessar em [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc):
Da mesma forma, existem muitas ferramentas compatíveis. Incluindo ferramentas de geração de código para muitas linguagens.
## Pydantic { #pydantic }
Toda a validação de dados é realizada nos bastidores pelo [Pydantic](https://docs.pydantic.dev/), então você recebe todos os benefícios disso. E você sabe que está em boas mãos.
Você pode usar as mesmas declarações de tipo com `str`, `float`, `bool` e muitos outros tipos de dados complexos.
Vários deles são explorados nos próximos capítulos do tutorial.
## A ordem importa { #order-matters }
Ao criar *operações de rota*, você pode encontrar situações em que tem um path fixo.
Como `/users/me`, digamos que seja para obter dados sobre o usuário atual.
E então você também pode ter um path `/users/{user_id}` para obter dados sobre um usuário específico por algum ID de usuário.
Como as *operações de rota* são avaliadas em ordem, você precisa garantir que o path para `/users/me` seja declarado antes do de `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Caso contrário, o path para `/users/{user_id}` também corresponderia a `/users/me`, "achando" que está recebendo um parâmetro `user_id` com o valor `"me"`.
Da mesma forma, você não pode redefinir uma operação de rota:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
A primeira sempre será usada, já que o path corresponde primeiro.
## Valores predefinidos { #predefined-values }
Se você tem uma *operação de rota* que recebe um *parâmetro de path*, mas quer que os valores válidos possíveis do *parâmetro de path* sejam predefinidos, você pode usar um `Enum` padrão do Python.
### Crie uma classe `Enum` { #create-an-enum-class }
Importe `Enum` e crie uma subclasse que herde de `str` e de `Enum`.
Ao herdar de `str`, a documentação da API saberá que os valores devem ser do tipo `string` e poderá renderizá-los corretamente.
Em seguida, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Dica
Se você está se perguntando, "AlexNet", "ResNet" e "LeNet" são apenas nomes de modelos de Aprendizado de Máquina modelos.
///
### Declare um parâmetro de path { #declare-a-path-parameter }
Em seguida, crie um *parâmetro de path* com anotação de tipo usando a classe enum que você criou (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Verifique a documentação { #check-the-docs }
Como os valores disponíveis para o *parâmetro de path* são predefinidos, a documentação interativa pode mostrá-los de forma agradável:
### Trabalhando com *enumerações* do Python { #working-with-python-enumerations }
O valor do *parâmetro de path* será um *membro de enumeração*.
#### Compare membros de enumeração { #compare-enumeration-members }
Você pode compará-lo com o *membro de enumeração* no seu enum `ModelName` criado:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Obtenha o valor da enumeração { #get-the-enumeration-value }
Você pode obter o valor real (um `str` neste caso) usando `model_name.value`, ou, em geral, `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Dica
Você também pode acessar o valor `"lenet"` com `ModelName.lenet.value`.
///
#### Retorne membros de enumeração { #return-enumeration-members }
Você pode retornar *membros de enum* da sua *operação de rota*, até mesmo aninhados em um corpo JSON (por exemplo, um `dict`).
Eles serão convertidos para seus valores correspondentes (strings neste caso) antes de serem retornados ao cliente:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
No seu cliente, você receberá uma resposta JSON como:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Parâmetros de path que contêm paths { #path-parameters-containing-paths }
Digamos que você tenha uma *operação de rota* com um path `/files/{file_path}`.
Mas você precisa que o próprio `file_path` contenha um *path*, como `home/johndoe/myfile.txt`.
Então, a URL para esse arquivo seria algo como: `/files/home/johndoe/myfile.txt`.
### Suporte do OpenAPI { #openapi-support }
O OpenAPI não oferece suporte a uma maneira de declarar um *parâmetro de path* que contenha um *path* dentro, pois isso poderia levar a cenários difíceis de testar e definir.
Ainda assim, você pode fazer isso no **FastAPI**, usando uma das ferramentas internas do Starlette.
E a documentação continuará funcionando, embora não adicione nenhuma informação dizendo que o parâmetro deve conter um path.
### Conversor de path { #path-convertor }
Usando uma opção diretamente do Starlette você pode declarar um *parâmetro de path* contendo um *path* usando uma URL como:
```
/files/{file_path:path}
```
Nesse caso, o nome do parâmetro é `file_path`, e a última parte, `:path`, diz que o parâmetro deve corresponder a qualquer *path*.
Então, você pode usá-lo com:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Dica
Você pode precisar que o parâmetro contenha `/home/johndoe/myfile.txt`, com uma barra inicial (`/`).
Nesse caso, a URL seria: `/files//home/johndoe/myfile.txt`, com uma barra dupla (`//`) entre `files` e `home`.
///
## Recapitulação { #recap }
Com o **FastAPI**, ao usar declarações de tipo do Python curtas, intuitivas e padrão, você obtém:
- Suporte no editor: verificações de erro, preenchimento automático, etc.
- "parsing" de dados
- Validação de dados
- Anotação da API e documentação automática
E você só precisa declará-los uma vez.
Essa é provavelmente a principal vantagem visível do **FastAPI** em comparação com frameworks alternativos (além do desempenho bruto).
================================================
FILE: docs/pt/docs/tutorial/query-param-models.md
================================================
# Modelos de Parâmetros de Consulta { #query-parameter-models }
Se você possui um grupo de **parâmetros de consultas** que são relacionados, você pode criar um **modelo Pydantic** para declará-los.
Isso permitiria que você **reutilizasse o modelo** em **diversos lugares**, e também declarasse validações e metadados de todos os parâmetros de uma única vez. 😎
/// note | Nota
Isso é suportado desde o FastAPI versão `0.115.0`. 🤓
///
## Parâmetros de Consulta com um Modelo Pydantic { #query-parameters-with-a-pydantic-model }
Declare os **parâmetros de consulta** que você precisa em um **modelo Pydantic**, e então declare o parâmetro como `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
O **FastAPI** **extrairá** os dados para **cada campo** dos **parâmetros de consulta** presentes na requisição, e fornecerá o modelo Pydantic que você definiu.
## Verifique a Documentação { #check-the-docs }
Você pode ver os parâmetros de consulta na IU da documentação em `/docs`:
## Restrinja Parâmetros de Consulta Extras { #forbid-extra-query-parameters }
Em alguns casos especiais (provavelmente não muito comuns), você queira **restringir** os parâmetros de consulta que deseja receber.
Você pode usar a configuração do modelo Pydantic para `forbid` qualquer campo `extra`:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Caso um cliente tente enviar alguns dados **extras** nos **parâmetros de consulta**, eles receberão um retorno de **erro**.
Por exemplo, se o cliente tentar enviar um parâmetro de consulta `tool` com o valor `plumbus`, como:
```http
https://example.com/items/?limit=10&tool=plumbus
```
Eles receberão um retorno de **erro** informando-os que o parâmetro de consulta `tool` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Resumo { #summary }
Você pode utilizar **modelos Pydantic** para declarar **parâmetros de consulta** no **FastAPI**. 😎
/// tip | Dica
Alerta de spoiler: você também pode utilizar modelos Pydantic para declarar cookies e cabeçalhos, mas você irá ler sobre isso mais a frente no tutorial. 🤫
///
================================================
FILE: docs/pt/docs/tutorial/query-params-str-validations.md
================================================
# Parâmetros de consulta e validações de string { #query-parameters-and-string-validations }
O **FastAPI** permite declarar informações adicionais e validações para os seus parâmetros.
Vamos usar esta aplicação como exemplo:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
O parâmetro de consulta `q` é do tipo `str | None`, isso significa que é do tipo `str`, mas também pode ser `None`, e de fato, o valor padrão é `None`, então o FastAPI saberá que não é obrigatório.
/// note | Nota
O FastAPI saberá que o valor de `q` não é obrigatório por causa do valor padrão `= None`.
Ter `str | None` permitirá que seu editor lhe ofereça melhor suporte e detecte erros.
///
## Validação adicional { #additional-validation }
Vamos impor que, embora `q` seja opcional, sempre que for fornecido, seu comprimento não exceda 50 caracteres.
### Importe `Query` e `Annotated` { #import-query-and-annotated }
Para isso, primeiro importe:
* `Query` de `fastapi`
* `Annotated` de `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Informação
O FastAPI adicionou suporte a `Annotated` (e passou a recomendá-lo) na versão 0.95.0.
Se você tiver uma versão mais antiga, teria erros ao tentar usar `Annotated`.
Certifique-se de [Atualizar a versão do FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) para pelo menos 0.95.1 antes de usar `Annotated`.
///
## Use `Annotated` no tipo do parâmetro `q` { #use-annotated-in-the-type-for-the-q-parameter }
Lembra que eu disse antes que `Annotated` pode ser usado para adicionar metadados aos seus parâmetros na [Introdução aos tipos do Python](../python-types.md#type-hints-with-metadata-annotations)?
Agora é a hora de usá-lo com FastAPI. 🚀
Tínhamos esta anotação de tipo:
```Python
q: str | None = None
```
O que faremos é envolver isso com `Annotated`, para que fique assim:
```Python
q: Annotated[str | None] = None
```
Ambas as versões significam a mesma coisa, `q` é um parâmetro que pode ser `str` ou `None`, e por padrão é `None`.
Agora vamos pular para a parte divertida. 🎉
## Adicione `Query` ao `Annotated` no parâmetro `q` { #add-query-to-annotated-in-the-q-parameter }
Agora que temos esse `Annotated` onde podemos colocar mais informações (neste caso, uma validação adicional), adicione `Query` dentro de `Annotated` e defina o parâmetro `max_length` como `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Perceba que o valor padrão continua sendo `None`, então o parâmetro ainda é opcional.
Mas agora, com `Query(max_length=50)` dentro de `Annotated`, estamos dizendo ao FastAPI que queremos validação adicional para este valor, queremos que tenha no máximo 50 caracteres. 😎
/// tip | Dica
Aqui estamos usando `Query()` porque este é um parâmetro de consulta. Mais adiante veremos outros como `Path()`, `Body()`, `Header()` e `Cookie()`, que também aceitam os mesmos argumentos que `Query()`.
///
Agora o FastAPI vai:
* Validar os dados garantindo que o comprimento máximo seja de 50 caracteres
* Mostrar um erro claro para o cliente quando os dados não forem válidos
* Documentar o parâmetro na operação de rota do esquema OpenAPI (então ele aparecerá na UI de docs automática)
## Alternativa (antiga): `Query` como valor padrão { #alternative-old-query-as-the-default-value }
Versões anteriores do FastAPI (antes de 0.95.0) exigiam que você usasse `Query` como valor padrão do seu parâmetro, em vez de colocá-lo em `Annotated`, há uma grande chance de você ver código usando isso por aí, então vou explicar.
/// tip | Dica
Para código novo e sempre que possível, use `Annotated` como explicado acima. Há múltiplas vantagens (explicadas abaixo) e nenhuma desvantagem. 🍰
///
É assim que você usaria `Query()` como valor padrão do parâmetro da sua função, definindo o parâmetro `max_length` como 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Como neste caso (sem usar `Annotated`) temos que substituir o valor padrão `None` na função por `Query()`, agora precisamos definir o valor padrão com o parâmetro `Query(default=None)`, ele serve ao mesmo propósito de definir esse valor padrão (pelo menos para o FastAPI).
Então:
```Python
q: str | None = Query(default=None)
```
...torna o parâmetro opcional, com um valor padrão de `None`, o mesmo que:
```Python
q: str | None = None
```
Mas a versão com `Query` o declara explicitamente como sendo um parâmetro de consulta.
Então, podemos passar mais parâmetros para `Query`. Neste caso, o parâmetro `max_length` que se aplica a strings:
```Python
q: str | None = Query(default=None, max_length=50)
```
Isso validará os dados, mostrará um erro claro quando os dados não forem válidos e documentará o parâmetro na operação de rota do esquema OpenAPI.
### `Query` como valor padrão ou em `Annotated` { #query-as-the-default-value-or-in-annotated }
Tenha em mente que, ao usar `Query` dentro de `Annotated`, você não pode usar o parâmetro `default` de `Query`.
Em vez disso, use o valor padrão real do parâmetro da função. Caso contrário, haveria inconsistência.
Por exemplo, isto não é permitido:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...porque não está claro se o valor padrão deveria ser `"rick"` ou `"morty"`.
Então, você usaria (preferencialmente):
```Python
q: Annotated[str, Query()] = "rick"
```
...ou em bases de código mais antigas você encontrará:
```Python
q: str = Query(default="rick")
```
### Vantagens de `Annotated` { #advantages-of-annotated }
Usar `Annotated` é recomendado em vez do valor padrão nos parâmetros da função, é melhor por vários motivos. 🤓
O valor padrão do parâmetro da função é o valor padrão real, isso é mais intuitivo com Python em geral. 😌
Você poderia chamar essa mesma função em outros lugares sem FastAPI, e ela funcionaria como esperado. Se houver um parâmetro obrigatório (sem valor padrão), seu editor vai avisar com um erro, e o Python também reclamará se você executá-la sem passar o parâmetro obrigatório.
Quando você não usa `Annotated` e em vez disso usa o estilo de valor padrão (antigo), se você chamar essa função sem FastAPI em outros lugares, terá que lembrar de passar os argumentos para a função para que funcione corretamente, caso contrário os valores serão diferentes do esperado (por exemplo, `QueryInfo` ou algo parecido em vez de `str`). E seu editor não vai avisar, e o Python também não vai reclamar ao executar a função, apenas quando as operações internas falharem.
Como `Annotated` pode ter mais de uma anotação de metadados, você agora pode até usar a mesma função com outras ferramentas, como o [Typer](https://typer.tiangolo.com/). 🚀
## Adicione mais validações { #add-more-validations }
Você também pode adicionar um parâmetro `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Adicione expressões regulares { #add-regular-expressions }
Você pode definir um `pattern` de expressão regular que o parâmetro deve corresponder:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Esse padrão específico de expressão regular verifica se o valor recebido no parâmetro:
* `^`: começa com os caracteres seguintes, não tem caracteres antes.
* `fixedquery`: tem exatamente o valor `fixedquery`.
* `$`: termina ali, não tem mais caracteres depois de `fixedquery`.
Se você se sentir perdido com essas ideias de "expressão regular", não se preocupe. Esse é um assunto difícil para muitas pessoas. Você ainda pode fazer muitas coisas sem precisar de expressões regulares por enquanto.
Agora você sabe que, sempre que precisar delas, pode usá-las no FastAPI.
## Valores padrão { #default-values }
Você pode, claro, usar valores padrão diferentes de `None`.
Digamos que você queira declarar o parâmetro de consulta `q` com `min_length` de `3` e ter um valor padrão de `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Nota
Ter um valor padrão de qualquer tipo, incluindo `None`, torna o parâmetro opcional (não obrigatório).
///
## Parâmetros obrigatórios { #required-parameters }
Quando não precisamos declarar mais validações ou metadados, podemos tornar o parâmetro de consulta `q` obrigatório simplesmente não declarando um valor padrão, assim:
```Python
q: str
```
em vez de:
```Python
q: str | None = None
```
Mas agora estamos declarando com `Query`, por exemplo assim:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Então, quando você precisa declarar um valor como obrigatório usando `Query`, você pode simplesmente não declarar um valor padrão:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Obrigatório, pode ser `None` { #required-can-be-none }
Você pode declarar que um parâmetro pode aceitar `None`, mas que ainda assim é obrigatório. Isso forçaria os clientes a enviarem um valor, mesmo que o valor seja `None`.
Para isso, você pode declarar que `None` é um tipo válido, mas simplesmente não declarar um valor padrão:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Lista de parâmetros de consulta / múltiplos valores { #query-parameter-list-multiple-values }
Quando você define explicitamente um parâmetro de consulta com `Query`, você também pode declará-lo para receber uma lista de valores, ou seja, receber múltiplos valores.
Por exemplo, para declarar um parâmetro de consulta `q` que pode aparecer várias vezes na URL, você pode escrever:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Então, com uma URL como:
```
http://localhost:8000/items/?q=foo&q=bar
```
você receberia os múltiplos valores dos parâmetros de consulta `q` (`foo` e `bar`) em uma `list` Python dentro da sua função de operação de rota, no parâmetro da função `q`.
Assim, a resposta para essa URL seria:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Dica
Para declarar um parâmetro de consulta com tipo `list`, como no exemplo acima, você precisa usar explicitamente `Query`, caso contrário seria interpretado como um corpo da requisição.
///
A documentação interativa da API será atualizada de acordo, permitindo múltiplos valores:
### Lista de parâmetros de consulta / múltiplos valores com valores padrão { #query-parameter-list-multiple-values-with-defaults }
Você também pode definir uma `list` de valores padrão caso nenhum seja fornecido:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Se você for até:
```
http://localhost:8000/items/
```
o valor padrão de `q` será: `["foo", "bar"]` e sua resposta será:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Usando apenas `list` { #using-just-list }
Você também pode usar `list` diretamente em vez de `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Nota
Tenha em mente que, neste caso, o FastAPI não verificará o conteúdo da lista.
Por exemplo, `list[int]` verificaria (and documentaria) que os conteúdos da lista são inteiros. Mas `list` sozinho não.
///
## Declare mais metadados { #declare-more-metadata }
Você pode adicionar mais informações sobre o parâmetro.
Essas informações serão incluídas no OpenAPI gerado e usadas pelas interfaces de documentação e por ferramentas externas.
/// note | Nota
Tenha em mente que ferramentas diferentes podem ter níveis diferentes de suporte ao OpenAPI.
Algumas delas podem ainda não mostrar todas as informações extras declaradas, embora na maioria dos casos a funcionalidade ausente já esteja planejada para desenvolvimento.
///
Você pode adicionar um `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
E uma `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Parâmetros com alias { #alias-parameters }
Imagine que você queira que o parâmetro seja `item-query`.
Assim:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Mas `item-query` não é um nome de variável Python válido.
O mais próximo seria `item_query`.
Mas você ainda precisa que seja exatamente `item-query`...
Então você pode declarar um `alias`, e esse alias será usado para encontrar o valor do parâmetro:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Descontinuando parâmetros { #deprecating-parameters }
Agora digamos que você não gosta mais desse parâmetro.
Você tem que deixá-lo por um tempo, pois há clientes usando-o, mas quer que a documentação mostre claramente que ele está descontinuado.
Então passe o parâmetro `deprecated=True` para `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
A documentação vai mostrar assim:
## Excluir parâmetros do OpenAPI { #exclude-parameters-from-openapi }
Para excluir um parâmetro de consulta do OpenAPI gerado (e portanto, dos sistemas de documentação automáticos), defina o parâmetro `include_in_schema` de `Query` como `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Validação personalizada { #custom-validation }
Podem existir casos em que você precise fazer alguma validação personalizada que não pode ser feita com os parâmetros mostrados acima.
Nesses casos, você pode usar uma função validadora personalizada que é aplicada após a validação normal (por exemplo, depois de validar que o valor é uma `str`).
Você pode fazer isso usando o [`AfterValidator` do Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) dentro de `Annotated`.
/// tip | Dica
O Pydantic também tem [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) e outros. 🤓
///
Por exemplo, este validador personalizado verifica se o ID do item começa com `isbn-` para um número de livro ISBN ou com `imdb-` para um ID de URL de filme IMDB:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Informação
Isso está disponível com a versão 2 do Pydantic ou superior. 😎
///
/// tip | Dica
Se você precisar fazer qualquer tipo de validação que exija comunicação com algum componente externo, como um banco de dados ou outra API, você deveria usar Dependências do FastAPI em vez disso; você aprenderá sobre elas mais adiante.
Esses validadores personalizados são para coisas que podem ser verificadas apenas com os mesmos dados fornecidos na requisição.
///
### Entenda esse código { #understand-that-code }
O ponto importante é apenas usar `AfterValidator` com uma função dentro de `Annotated`. Sinta-se à vontade para pular esta parte. 🤸
---
Mas se você estiver curioso sobre este exemplo de código específico e ainda entretido, aqui vão alguns detalhes extras.
#### String com `value.startswith()` { #string-with-value-startswith }
Percebeu? Uma string usando `value.startswith()` pode receber uma tupla, e verificará cada valor na tupla:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Um item aleatório { #a-random-item }
Com `data.items()` obtemos um objeto iterável com tuplas contendo a chave e o valor de cada item do dicionário.
Convertimos esse objeto iterável em uma `list` adequada com `list(data.items())`.
Em seguida, com `random.choice()` podemos obter um valor aleatório da lista, então obtemos uma tupla com `(id, name)`. Será algo como `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Depois atribuímos esses dois valores da tupla às variáveis `id` e `name`.
Assim, se o usuário não fornecer um ID de item, ele ainda receberá uma sugestão aleatória.
...fazemos tudo isso em uma única linha simples. 🤯 Você não ama Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Recapitulando { #recap }
Você pode declarar validações adicionais e metadados para seus parâmetros.
Validações e metadados genéricos:
* `alias`
* `title`
* `description`
* `deprecated`
Validações específicas para strings:
* `min_length`
* `max_length`
* `pattern`
Validações personalizadas usando `AfterValidator`.
Nestes exemplos você viu como declarar validações para valores `str`.
Veja os próximos capítulos para aprender a declarar validações para outros tipos, como números.
================================================
FILE: docs/pt/docs/tutorial/query-params.md
================================================
# Parâmetros de Consulta { #query-parameters }
Quando você declara outros parâmetros na função que não fazem parte dos parâmetros da rota, esses parâmetros são automaticamente interpretados como parâmetros de "consulta".
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
A consulta é o conjunto de pares chave-valor que vai depois de `?` na URL, separado pelo caractere `&`.
Por exemplo, na URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...os parâmetros da consulta são:
* `skip`: com o valor `0`
* `limit`: com o valor `10`
Como eles são parte da URL, eles são "naturalmente" strings.
Mas quando você declara eles com os tipos do Python (no exemplo acima, como `int`), eles são convertidos para aquele tipo e validados em relação a ele.
Todo o processo que era aplicado para parâmetros de rota também é aplicado para parâmetros de consulta:
* Suporte do editor (obviamente)
* "análise" de dados
* Validação de dados
* Documentação automática
## Valores padrão { #defaults }
Como os parâmetros de consulta não são uma parte fixa da rota, eles podem ser opcionais e podem ter valores padrão.
No exemplo acima eles tem valores padrão de `skip=0` e `limit=10`.
Então, se você for até a URL:
```
http://127.0.0.1:8000/items/
```
Seria o mesmo que ir para:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Mas, se por exemplo você for para:
```
http://127.0.0.1:8000/items/?skip=20
```
Os valores dos parâmetros na sua função serão:
* `skip=20`: Por que você definiu isso na URL
* `limit=10`: Por que esse era o valor padrão
## Parâmetros opcionais { #optional-parameters }
Da mesma forma, você pode declarar parâmetros de consulta opcionais, definindo o valor padrão para `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
Nesse caso, o parâmetro da função `q` será opcional, e `None` será o padrão.
/// check | Verifique
Você também pode notar que o **FastAPI** é esperto o suficiente para perceber que o parâmetro da rota `item_id` é um parâmetro da rota, e `q` não é, portanto, `q` é o parâmetro de consulta.
///
## Conversão dos tipos de parâmetros de consulta { #query-parameter-type-conversion }
Você também pode declarar tipos `bool`, e eles serão convertidos:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Nesse caso, se você for para:
```
http://127.0.0.1:8000/items/foo?short=1
```
ou
```
http://127.0.0.1:8000/items/foo?short=True
```
ou
```
http://127.0.0.1:8000/items/foo?short=true
```
ou
```
http://127.0.0.1:8000/items/foo?short=on
```
ou
```
http://127.0.0.1:8000/items/foo?short=yes
```
ou qualquer outra variação (tudo em maiúscula, primeira letra em maiúscula, etc), a sua função vai ver o parâmetro `short` com um valor `bool` de `True`. Caso contrário `False`.
## Múltiplos parâmetros de rota e consulta { #multiple-path-and-query-parameters }
Você pode declarar múltiplos parâmetros de rota e parâmetros de consulta ao mesmo tempo, o **FastAPI** vai saber o quê é o quê.
E você não precisa declarar eles em nenhuma ordem específica.
Eles serão detectados pelo nome:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Parâmetros de consulta obrigatórios { #required-query-parameters }
Quando você declara um valor padrão para parâmetros que não são de rota (até agora, nós vimos apenas parâmetros de consulta), então eles não são obrigatórios.
Caso você não queira adicionar um valor específico mas queira apenas torná-lo opcional, defina o valor padrão como `None`.
Porém, quando você quiser fazer com que o parâmetro de consulta seja obrigatório, você pode simplesmente não declarar nenhum valor como padrão.
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Aqui o parâmetro da consulta `needy` é um valor obrigatório, do tipo `str`.
Se você abrir no seu navegador a URL:
```
http://127.0.0.1:8000/items/foo-item
```
... sem adicionar o parâmetro obrigatório `needy`, você verá um erro como:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Como `needy` é um parâmetro obrigatório, você precisaria defini-lo na URL:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...isso deve funcionar:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
E claro, você pode definir alguns parâmetros como obrigatórios, alguns possuindo um valor padrão, e outros sendo totalmente opcionais:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
Nesse caso, existem 3 parâmetros de consulta:
* `needy`, um `str` obrigatório.
* `skip`, um `int` com o valor padrão `0`.
* `limit`, um `int` opcional.
/// tip | Dica
Você também poderia usar `Enum`s da mesma forma que com [Parâmetros de rota](path-params.md#predefined-values).
///
================================================
FILE: docs/pt/docs/tutorial/request-files.md
================================================
# Arquivos de Requisição { #request-files }
Você pode definir arquivos para serem enviados pelo cliente usando `File`.
/// info | Informação
Para receber arquivos enviados, primeiro instale [`python-multipart`](https://github.com/Kludex/python-multipart).
Garanta que você criou um [ambiente virtual](../virtual-environments.md), o ativou e então o instalou, por exemplo:
```console
$ pip install python-multipart
```
Isso é necessário, visto que os arquivos enviados são enviados como "dados de formulário".
///
## Importe `File` { #import-file }
Importe `File` e `UploadFile` de `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Definir Parâmetros `File` { #define-file-parameters }
Crie parâmetros de arquivo da mesma forma que você faria para `Body` ou `Form`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Informação
`File` é uma classe que herda diretamente de `Form`.
Mas lembre-se que quando você importa `Query`, `Path`, `File` e outros de `fastapi`, eles são, na verdade, funções que retornam classes especiais.
///
/// tip | Dica
Para declarar corpos de arquivos, você precisa usar `File`, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta ou parâmetros de corpo (JSON).
///
Os arquivos serão enviados como "dados de formulário".
Se você declarar o tipo do parâmetro da função da sua *operação de rota* como `bytes`, o **FastAPI** lerá o arquivo para você e você receberá o conteúdo como `bytes`.
Mantenha em mente que isso significa que todo o conteúdo será armazenado na memória. Isso funcionará bem para arquivos pequenos.
Mas há muitos casos em que você pode se beneficiar do uso de `UploadFile`.
## Parâmetros de Arquivo com `UploadFile` { #file-parameters-with-uploadfile }
Defina um parâmetro de arquivo com um tipo de `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Utilizar `UploadFile` tem várias vantagens sobre `bytes`:
* Você não precisa utilizar o `File()` no valor padrão do parâmetro.
* Ele utiliza um arquivo "spooled":
* Um arquivo armazenado na memória até um limite máximo de tamanho, e após passar esse limite, ele será armazenado no disco.
* Isso significa que funcionará bem para arquivos grandes como imagens, vídeos, binários grandes, etc., sem consumir toda a memória.
* Você pode receber metadados do arquivo enviado.
* Ele tem uma [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) interface `assíncrona`.
* Ele expõe um objeto python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) que você pode passar diretamente para outras bibliotecas que esperam um objeto semelhante a um arquivo.
### `UploadFile` { #uploadfile }
`UploadFile` tem os seguintes atributos:
* `filename`: Uma `str` com o nome do arquivo original que foi enviado (por exemplo, `myimage.jpg`).
* `content_type`: Uma `str` com o tipo de conteúdo (MIME type / media type) (por exemplo, `image/jpeg`).
* `file`: Um [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (um [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) objeto). Este é o objeto de arquivo Python que você pode passar diretamente para outras funções ou bibliotecas que esperam um objeto semelhante a um arquivo.
`UploadFile` tem os seguintes métodos `assíncronos`. Todos eles chamam os métodos de arquivo correspondentes por baixo dos panos (usando o `SpooledTemporaryFile` interno).
* `write(data)`: Escreve `data` (`str` ou `bytes`) no arquivo.
* `read(size)`: Lê `size` (`int`) bytes/caracteres do arquivo.
* `seek(offset)`: Vai para o byte na posição `offset` (`int`) no arquivo.
* Por exemplo, `await myfile.seek(0)` irá para o início do arquivo.
* Isso é especialmente útil se você executar `await myfile.read()` uma vez e precisar ler o conteúdo novamente.
* `close()`: Fecha o arquivo.
Como todos esses métodos são métodos `assíncronos`, você precisa "aguardar" por eles.
Por exemplo, dentro de uma função de *operação de rota* `assíncrona`, você pode obter o conteúdo com:
```Python
contents = await myfile.read()
```
Se você estiver dentro de uma função de *operação de rota* normal `def`, você pode acessar o `UploadFile.file` diretamente, por exemplo:
```Python
contents = myfile.file.read()
```
/// note | Detalhes Técnicos do `async`
Quando você usa os métodos `async`, o **FastAPI** executa os métodos de arquivo em um threadpool e aguarda por eles.
///
/// note | Detalhes Técnicos do Starlette
O `UploadFile` do **FastAPI** herda diretamente do `UploadFile` do **Starlette**, mas adiciona algumas partes necessárias para torná-lo compatível com o **Pydantic** e as outras partes do FastAPI.
///
## O que é "Form Data" { #what-is-form-data }
O jeito que os formulários HTML (``) enviam os dados para o servidor normalmente usa uma codificação "especial" para esses dados, a qual é diferente do JSON.
**FastAPI** se certificará de ler esses dados do lugar certo, ao invés de JSON.
/// note | Detalhes Técnicos
Dados de formulários normalmente são codificados usando o "media type" `application/x-www-form-urlencoded` quando não incluem arquivos.
Mas quando o formulário inclui arquivos, ele é codificado como `multipart/form-data`. Se você usar `File`, o **FastAPI** saberá que tem que pegar os arquivos da parte correta do corpo da requisição.
Se você quiser ler mais sobre essas codificações e campos de formulário, vá para a [MDN web docs para `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Atenção
Você pode declarar múltiplos parâmetros `File` e `Form` em uma *operação de rota*, mas você não pode declarar campos `Body` que você espera receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
///
## Upload de Arquivo Opcional { #optional-file-upload }
Você pode tornar um arquivo opcional usando anotações de tipo padrão e definindo um valor padrão de `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` com Metadados Adicionais { #uploadfile-with-additional-metadata }
Você também pode usar `File()` com `UploadFile`, por exemplo, para definir metadados adicionais:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Uploads de Múltiplos Arquivos { #multiple-file-uploads }
É possível realizar o upload de vários arquivos ao mesmo tempo.
Eles serão associados ao mesmo "campo de formulário" enviado usando "dados de formulário".
Para usar isso, declare uma lista de `bytes` ou `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Você receberá, tal como declarado, uma `list` de `bytes` ou `UploadFile`.
/// note | Detalhes Técnicos
Você também pode usar `from starlette.responses import HTMLResponse`.
**FastAPI** providencia o mesmo `starlette.responses` que `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
///
### Uploads de Múltiplos Arquivos com Metadados Adicionais { #multiple-file-uploads-with-additional-metadata }
Da mesma forma de antes, você pode usar `File()` para definir parâmetros adicionais, mesmo para `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Recapitulando { #recap }
Utilize `File`, `bytes` e `UploadFile` para declarar arquivos a serem enviados na requisição, enviados como dados de formulário.
================================================
FILE: docs/pt/docs/tutorial/request-form-models.md
================================================
# Modelos de Formulários { #form-models }
Você pode utilizar **Modelos Pydantic** para declarar **campos de formulários** no FastAPI.
/// info | Informação
Para utilizar formulários, instale primeiramente o [`python-multipart`](https://github.com/Kludex/python-multipart).
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo, e então instalar. Por exemplo:
```console
$ pip install python-multipart
```
///
/// note | Nota
Isto é suportado desde a versão `0.113.0` do FastAPI. 🤓
///
## Modelos Pydantic para Formulários { #pydantic-models-for-forms }
Você precisa apenas declarar um **modelo Pydantic** com os campos que deseja receber como **campos de formulários**, e então declarar o parâmetro como um `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
O **FastAPI** irá **extrair** as informações para **cada campo** dos **dados do formulário** na requisição e dar para você o modelo Pydantic que você definiu.
## Confira os Documentos { #check-the-docs }
Você pode verificar na UI de documentação em `/docs`:
## Proibir Campos Extras de Formulários { #forbid-extra-form-fields }
Em alguns casos de uso especiais (provavelmente não muito comum), você pode desejar **restringir** os campos do formulário para aceitar apenas os declarados no modelo Pydantic. E **proibir** qualquer campo **extra**.
/// note | Nota
Isso é suportado desde a versão `0.114.0` do FastAPI. 🤓
///
Você pode utilizar a configuração de modelo do Pydantic para `proibir` qualquer campo `extra`:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Caso um cliente tente enviar informações adicionais, ele receberá um retorno de **erro**.
Por exemplo, se o cliente tentar enviar os campos de formulário:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Ele receberá um retorno de erro informando-o que o campo `extra` não é permitido:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Resumo { #summary }
Você pode utilizar modelos Pydantic para declarar campos de formulários no FastAPI. 😎
================================================
FILE: docs/pt/docs/tutorial/request-forms-and-files.md
================================================
# Formulários e Arquivos da Requisição { #request-forms-and-files }
Você pode definir arquivos e campos de formulário ao mesmo tempo usando `File` e `Form`.
/// info | Informação
Para receber arquivos carregados e/ou dados de formulário, primeiro instale [`python-multipart`](https://github.com/Kludex/python-multipart).
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e então instalar, por exemplo:
```console
$ pip install python-multipart
```
///
## Importe `File` e `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Defina parâmetros de `File` e `Form` { #define-file-and-form-parameters }
Crie parâmetros de arquivo e formulário da mesma forma que você faria para `Body` ou `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Os arquivos e campos de formulário serão carregados como dados de formulário e você receberá os arquivos e campos de formulário.
E você pode declarar alguns dos arquivos como `bytes` e alguns como `UploadFile`.
/// warning | Atenção
Você pode declarar vários parâmetros `File` e `Form` em uma *operação de rota*, mas não é possível declarar campos `Body` para receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
///
## Recapitulando { #recap }
Use `File` e `Form` juntos quando precisar receber dados e arquivos na mesma requisição.
================================================
FILE: docs/pt/docs/tutorial/request-forms.md
================================================
# Dados do formulário { #form-data }
Quando você precisar receber campos de formulário em vez de JSON, você pode usar `Form`.
/// info | Informação
Para usar formulários, primeiro instale [`python-multipart`](https://github.com/Kludex/python-multipart).
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e então instalá-lo, por exemplo:
```console
$ pip install python-multipart
```
///
## Importe `Form` { #import-form }
Importe `Form` de `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Defina parâmetros de `Form` { #define-form-parameters }
Crie parâmetros de formulário da mesma forma que você faria para `Body` ou `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Por exemplo, em uma das maneiras que a especificação OAuth2 pode ser usada (chamada "fluxo de senha"), é necessário enviar um `username` e uma `password` como campos do formulário.
A especificação exige que os campos sejam exatamente nomeados como `username` e `password` e sejam enviados como campos de formulário, não JSON.
Com `Form` você pode declarar as mesmas configurações que com `Body` (e `Query`, `Path`, `Cookie`), incluindo validação, exemplos, um alias (por exemplo, `user-name` em vez de `username`), etc.
/// info | Informação
`Form` é uma classe que herda diretamente de `Body`.
///
/// tip | Dica
Para declarar corpos de formulário, você precisa usar `Form` explicitamente, porque sem ele os parâmetros seriam interpretados como parâmetros de consulta ou parâmetros de corpo (JSON).
///
## Sobre "Campos de formulário" { #about-form-fields }
A forma como os formulários HTML (``) enviam os dados para o servidor normalmente usa uma codificação "especial" para esses dados, é diferente do JSON.
O **FastAPI** fará a leitura desses dados no lugar certo em vez de JSON.
/// note | Detalhes Técnicos
Os dados dos formulários são normalmente codificados usando o "media type" `application/x-www-form-urlencoded`.
Mas quando o formulário inclui arquivos, ele é codificado como `multipart/form-data`. Você lerá sobre como lidar com arquivos no próximo capítulo.
Se você quiser ler mais sobre essas codificações e campos de formulário, vá para o [MDN web docs para `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Atenção
Você pode declarar vários parâmetros `Form` em uma *operação de rota*, mas não pode declarar campos `Body` que espera receber como JSON, pois a requisição terá o corpo codificado usando `application/x-www-form-urlencoded` em vez de `application/json`.
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
///
## Recapitulando { #recap }
Use `Form` para declarar os parâmetros de entrada de dados de formulário.
================================================
FILE: docs/pt/docs/tutorial/response-model.md
================================================
# Modelo de resposta - Tipo de retorno { #response-model-return-type }
Você pode declarar o tipo usado para a resposta anotando o **tipo de retorno** da *função de operação de rota*.
Você pode usar **anotações de tipo** da mesma forma que usaria para dados de entrada em **parâmetros** de função, você pode usar modelos Pydantic, listas, dicionários, valores escalares como inteiros, booleanos, etc.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
O FastAPI usará este tipo de retorno para:
* **Validar** os dados retornados.
* Se os dados forem inválidos (por exemplo, se estiver faltando um campo), significa que o código do *seu* aplicativo está quebrado, não retornando o que deveria, e retornará um erro de servidor em vez de retornar dados incorretos. Dessa forma, você e seus clientes podem ter certeza de que receberão os dados e o formato de dados esperados.
* Adicionar um **JSON Schema** para a resposta, na *operação de rota* do OpenAPI.
* Isso será usado pela **documentação automática**.
* Também será usado por ferramentas de geração automática de código do cliente.
* **Serializar** os dados retornados para JSON usando Pydantic, que é escrito em **Rust**, então será **muito mais rápido**.
Mas o mais importante:
* Ele **limitará e filtrará** os dados de saída para o que está definido no tipo de retorno.
* Isso é particularmente importante para a **segurança**, veremos mais sobre isso abaixo.
## Parâmetro `response_model` { #response-model-parameter }
Existem alguns casos em que você precisa ou deseja retornar alguns dados que não são exatamente o que o tipo declara.
Por exemplo, você pode querer **retornar um dicionário** ou um objeto de banco de dados, mas **declará-lo como um modelo Pydantic**. Dessa forma, o modelo Pydantic faria toda a documentação de dados, validação, etc. para o objeto que você retornou (por exemplo, um dicionário ou objeto de banco de dados).
Se você adicionasse a anotação do tipo de retorno, ferramentas e editores reclamariam com um erro (correto) informando que sua função está retornando um tipo (por exemplo, um dict) diferente do que você declarou (por exemplo, um modelo Pydantic).
Nesses casos, você pode usar o parâmetro `response_model` do *decorador de operação de rota* em vez do tipo de retorno.
Você pode usar o parâmetro `response_model` em qualquer uma das *operações de rota*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Nota
Observe que `response_model` é um parâmetro do método "decorator" (`get`, `post`, etc). Não da sua *função de operação de rota*, como todos os parâmetros e corpo.
///
`response_model` recebe o mesmo tipo que você declararia para um campo de modelo Pydantic, então, pode ser um modelo Pydantic, mas também pode ser, por exemplo, uma `list` de modelos Pydantic, como `List[Item]`.
O FastAPI usará este `response_model` para fazer toda a documentação de dados, validação, etc. e também para **converter e filtrar os dados de saída** para sua declaração de tipo.
/// tip | Dica
Se você tiver verificações de tipo rigorosas em seu editor, mypy, etc, você pode declarar o tipo de retorno da função como `Any`.
Dessa forma, você diz ao editor que está retornando qualquer coisa intencionalmente. Mas o FastAPI ainda fará a documentação de dados, validação, filtragem, etc. com o `response_model`.
///
### Prioridade `response_model` { #response-model-priority }
Se você declarar tanto um tipo de retorno quanto um `response_model`, o `response_model` terá prioridade e será usado pelo FastAPI.
Dessa forma, você pode adicionar anotações de tipo corretas às suas funções, mesmo quando estiver retornando um tipo diferente do modelo de resposta, para ser usado pelo editor e ferramentas como mypy. E ainda assim você pode fazer com que o FastAPI faça a validação de dados, documentação, etc. usando o `response_model`.
Você também pode usar `response_model=None` para desabilitar a criação de um modelo de resposta para essa *operação de rota*, você pode precisar fazer isso se estiver adicionando anotações de tipo para coisas que não são campos Pydantic válidos, você verá um exemplo disso em uma das seções abaixo.
## Retorne os mesmos dados de entrada { #return-the-same-input-data }
Aqui estamos declarando um modelo `UserIn`, ele conterá uma senha em texto simples:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Informação
Para usar `EmailStr`, primeiro instale [`email-validator`](https://github.com/JoshData/python-email-validator).
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ative-o e então instale-o, por exemplo:
```console
$ pip install email-validator
```
ou com:
```console
$ pip install "pydantic[email]"
```
///
E estamos usando este modelo para declarar nossa entrada e o mesmo modelo para declarar nossa saída:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Agora, sempre que um navegador estiver criando um usuário com uma senha, a API retornará a mesma senha na resposta.
Neste caso, pode não ser um problema, porque é o mesmo usuário enviando a senha.
Mas se usarmos o mesmo modelo para outra *operação de rota*, poderíamos estar enviando as senhas dos nossos usuários para todos os clientes.
/// danger | Cuidado
Nunca armazene a senha simples de um usuário ou envie-a em uma resposta como esta, a menos que você saiba todas as ressalvas e saiba o que está fazendo.
///
## Adicione um modelo de saída { #add-an-output-model }
Podemos, em vez disso, criar um modelo de entrada com a senha em texto simples e um modelo de saída sem ela:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Aqui, embora nossa *função de operação de rota* esteja retornando o mesmo usuário de entrada que contém a senha:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...declaramos o `response_model` como nosso modelo `UserOut`, que não inclui a senha:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Então, **FastAPI** cuidará de filtrar todos os dados que não são declarados no modelo de saída (usando Pydantic).
### `response_model` ou Tipo de Retorno { #response-model-or-return-type }
Neste caso, como os dois modelos são diferentes, se anotássemos o tipo de retorno da função como `UserOut`, o editor e as ferramentas reclamariam que estamos retornando um tipo inválido, pois são classes diferentes.
É por isso que neste exemplo temos que declará-lo no parâmetro `response_model`.
...mas continue lendo abaixo para ver como superar isso.
## Tipo de Retorno e Filtragem de Dados { #return-type-and-data-filtering }
Vamos continuar do exemplo anterior. Queríamos **anotar a função com um tipo**, mas queríamos poder retornar da função algo que realmente incluísse **mais dados**.
Queremos que o FastAPI continue **filtrando** os dados usando o modelo de resposta. Para que, embora a função retorne mais dados, a resposta inclua apenas os campos declarados no modelo de resposta.
No exemplo anterior, como as classes eram diferentes, tivemos que usar o parâmetro `response_model`. Mas isso também significa que não temos suporte do editor e das ferramentas verificando o tipo de retorno da função.
Mas na maioria dos casos em que precisamos fazer algo assim, queremos que o modelo apenas **filtre/remova** alguns dados como neste exemplo.
E nesses casos, podemos usar classes e herança para aproveitar as **anotações de tipo** de função para obter melhor suporte no editor e nas ferramentas, e ainda obter a **filtragem de dados** FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Com isso, temos suporte de ferramentas, de editores e mypy, pois este código está correto em termos de tipos, mas também obtemos a filtragem de dados do FastAPI.
Como isso funciona? Vamos verificar. 🤓
### Anotações de tipo e ferramentas { #type-annotations-and-tooling }
Primeiro, vamos ver como editores, mypy e outras ferramentas veriam isso.
`BaseUser` tem os campos base. Então `UserIn` herda de `BaseUser` e adiciona o campo `password`, então, ele incluirá todos os campos de ambos os modelos.
Anotamos o tipo de retorno da função como `BaseUser`, mas na verdade estamos retornando uma instância `UserIn`.
O editor, mypy e outras ferramentas não reclamarão disso porque, em termos de digitação, `UserIn` é uma subclasse de `BaseUser`, o que significa que é um tipo *válido* quando o que é esperado é qualquer coisa que seja um `BaseUser`.
### Filtragem de dados FastAPI { #fastapi-data-filtering }
Agora, para FastAPI, ele verá o tipo de retorno e garantirá que o que você retornar inclua **apenas** os campos que são declarados no tipo.
O FastAPI faz várias coisas internamente com o Pydantic para garantir que essas mesmas regras de herança de classe não sejam usadas para a filtragem de dados retornados, caso contrário, você pode acabar retornando muito mais dados do que o esperado.
Dessa forma, você pode obter o melhor dos dois mundos: anotações de tipo com **suporte a ferramentas** e **filtragem de dados**.
## Veja na documentação { #see-it-in-the-docs }
Quando você vê a documentação automática, pode verificar se o modelo de entrada e o modelo de saída terão seus próprios esquemas JSON:
E ambos os modelos serão usados para a documentação interativa da API:
## Outras anotações de tipo de retorno { #other-return-type-annotations }
Pode haver casos em que você retorna algo que não é um campo Pydantic válido e anota na função, apenas para obter o suporte fornecido pelas ferramentas (o editor, mypy, etc).
### Retorne uma Response diretamente { #return-a-response-directly }
O caso mais comum seria [retornar uma Response diretamente, conforme explicado posteriormente na documentação avançada](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Este caso simples é tratado automaticamente pelo FastAPI porque a anotação do tipo de retorno é a classe (ou uma subclasse de) `Response`.
E as ferramentas também ficarão felizes porque `RedirectResponse` e `JSONResponse` são subclasses de `Response`, então a anotação de tipo está correta.
### Anote uma subclasse de Response { #annotate-a-response-subclass }
Você também pode usar uma subclasse de `Response` na anotação de tipo:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Isso também funcionará porque `RedirectResponse` é uma subclasse de `Response`, e o FastAPI tratará automaticamente este caso simples.
### Anotações de Tipo de Retorno Inválido { #invalid-return-type-annotations }
Mas quando você retorna algum outro objeto arbitrário que não é um tipo Pydantic válido (por exemplo, um objeto de banco de dados) e você o anota dessa forma na função, o FastAPI tentará criar um modelo de resposta Pydantic a partir dessa anotação de tipo e falhará.
O mesmo aconteceria se você tivesse algo como uma união entre tipos diferentes onde um ou mais deles não são tipos Pydantic válidos, por exemplo, isso falharia 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...isso falha porque a anotação de tipo não é um tipo Pydantic e não é apenas uma única classe ou subclasse `Response`, é uma união (qualquer uma das duas) entre um `Response` e um `dict`.
### Desative o modelo de resposta { #disable-response-model }
Continuando com o exemplo acima, você pode não querer ter a validação de dados padrão, documentação, filtragem, etc. que é realizada pelo FastAPI.
Mas você pode querer manter a anotação do tipo de retorno na função para obter o suporte de ferramentas como editores e verificadores de tipo (por exemplo, mypy).
Neste caso, você pode desabilitar a geração do modelo de resposta definindo `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Isso fará com que o FastAPI pule a geração do modelo de resposta e, dessa forma, você pode ter quaisquer anotações de tipo de retorno que precisar sem afetar seu aplicativo FastAPI. 🤓
## Parâmetros de codificação do modelo de resposta { #response-model-encoding-parameters }
Seu modelo de resposta pode ter valores padrão, como:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (ou `str | None = None` no Python 3.10) tem um padrão de `None`.
* `tax: float = 10.5` tem um padrão de `10.5`.
* `tags: List[str] = []` tem um padrão de uma lista vazia: `[]`.
mas você pode querer omiti-los do resultado se eles não foram realmente armazenados.
Por exemplo, se você tem modelos com muitos atributos opcionais em um banco de dados NoSQL, mas não quer enviar respostas JSON muito longas cheias de valores padrão.
### Use o parâmetro `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Você pode definir o parâmetro `response_model_exclude_unset=True` do *decorador de operação de rota*:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
e esses valores padrão não serão incluídos na resposta, apenas os valores realmente definidos.
Então, se você enviar uma solicitação para essa *operação de rota* para o item com ID `foo`, a resposta (sem incluir valores padrão) será:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Informação
Você também pode usar:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
conforme descrito na [documentação do Pydantic](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) para `exclude_defaults` e `exclude_none`.
///
#### Dados com valores para campos com padrões { #data-with-values-for-fields-with-defaults }
Mas se seus dados tiverem valores para os campos do modelo com valores padrões, como o item com ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
eles serão incluídos na resposta.
#### Dados com os mesmos valores que os padrões { #data-with-the-same-values-as-the-defaults }
Se os dados tiverem os mesmos valores que os padrões, como o item com ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
O FastAPI é inteligente o suficiente (na verdade, o Pydantic é inteligente o suficiente) para perceber que, embora `description`, `tax` e `tags` tenham os mesmos valores que os padrões, eles foram definidos explícita e diretamente (em vez de retirados dos padrões).
Portanto, eles serão incluídos na resposta JSON.
/// tip | Dica
Observe que os valores padrão podem ser qualquer coisa, não apenas `None`.
Eles podem ser uma lista (`[]`), um `float` de `10.5`, etc.
///
### `response_model_include` e `response_model_exclude` { #response-model-include-and-response-model-exclude }
Você também pode usar os parâmetros `response_model_include` e `response_model_exclude` do *decorador de operação de rota*.
Eles pegam um `set` de `str` com o nome dos atributos para incluir (omitindo o resto) ou para excluir (incluindo o resto).
Isso pode ser usado como um atalho rápido se você tiver apenas um modelo Pydantic e quiser remover alguns dados da saída.
/// tip | Dica
Mas ainda é recomendado usar as ideias acima, usando várias classes, em vez desses parâmetros.
Isso ocorre porque o JSON Schema gerado no OpenAPI do seu aplicativo (e a documentação) ainda será o único para o modelo completo, mesmo que você use `response_model_include` ou `response_model_exclude` para omitir alguns atributos.
Isso também se aplica ao `response_model_by_alias` que funciona de forma semelhante.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Dica
A sintaxe `{"name", "description"}` cria um `set` com esses dois valores.
É equivalente a `set(["name", "description"])`.
///
#### Usando `list`s em vez de `set`s { #using-lists-instead-of-sets }
Se você esquecer de usar um `set` e usar uma `list` ou `tuple` em vez disso, o FastAPI ainda o converterá em um `set` e funcionará corretamente:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Recapitulação { #recap }
Use o parâmetro `response_model` do *decorador de operação de rota* para definir modelos de resposta e, especialmente, para garantir que dados privados sejam filtrados.
Use `response_model_exclude_unset` para retornar apenas os valores definidos explicitamente.
================================================
FILE: docs/pt/docs/tutorial/response-status-code.md
================================================
# Código de status de resposta { #response-status-code }
Da mesma forma que você pode especificar um modelo de resposta, você também pode declarar o código de status HTTP usado para a resposta com o parâmetro `status_code` em qualquer uma das *operações de rota*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* etc.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Nota
Observe que `status_code` é um parâmetro do método "decorador" (`get`, `post`, etc). Não da sua função de *operação de rota*, como todos os parâmetros e corpo.
///
O parâmetro `status_code` recebe um número com o código de status HTTP.
/// info | Informação
`status_code` também pode receber um `IntEnum`, como [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) do Python.
///
Dessa forma:
* Este código de status será retornado na resposta.
* Será documentado como tal no esquema OpenAPI (e, portanto, nas interfaces do usuário):
/// note | Nota
Alguns códigos de resposta (consulte a próxima seção) indicam que a resposta não possui um corpo.
O FastAPI sabe disso e produzirá documentos OpenAPI informando que não há corpo de resposta.
///
## Sobre os códigos de status HTTP { #about-http-status-codes }
/// note | Nota
Se você já sabe o que são códigos de status HTTP, pule para a próxima seção.
///
Em HTTP, você envia um código de status numérico de 3 dígitos como parte da resposta.
Esses códigos de status têm um nome associado para reconhecê-los, mas o importante é o número.
Resumidamente:
* `100 - 199` são para "Informações". Você raramente os usa diretamente. As respostas com esses códigos de status não podem ter um corpo.
* **`200 - 299`** são para respostas "Bem-sucedidas". Estes são os que você mais usaria.
* `200` é o código de status padrão, o que significa que tudo estava "OK".
* Outro exemplo seria `201`, "Criado". É comumente usado após a criação de um novo registro no banco de dados.
* Um caso especial é `204`, "Sem Conteúdo". Essa resposta é usada quando não há conteúdo para retornar ao cliente e, portanto, a resposta não deve ter um corpo.
* **`300 - 399`** são para "Redirecionamento". As respostas com esses códigos de status podem ou não ter um corpo, exceto `304`, "Não modificado", que não deve ter um.
* **`400 - 499`** são para respostas de "Erro do cliente". Este é o segundo tipo que você provavelmente mais usaria.
* Um exemplo é `404`, para uma resposta "Não encontrado".
* Para erros genéricos do cliente, você pode usar apenas `400`.
* `500 - 599` são para erros do servidor. Você quase nunca os usa diretamente. Quando algo der errado em alguma parte do código do seu aplicativo ou servidor, ele retornará automaticamente um desses códigos de status.
/// tip | Dica
Para saber mais sobre cada código de status e qual código serve para quê, verifique a [documentação do MDN sobre códigos de status HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Atalho para lembrar os nomes { #shortcut-to-remember-the-names }
Vamos ver o exemplo anterior novamente:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` é o código de status para "Criado".
Mas você não precisa memorizar o que cada um desses códigos significa.
Você pode usar as variáveis de conveniência de `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Eles são apenas uma conveniência, eles possuem o mesmo número, mas dessa forma você pode usar o preenchimento automático do editor para encontrá-los:
/// note | Detalhes Técnicos
Você também pode usar `from starlette import status`.
**FastAPI** fornece o mesmo `starlette.status` como `fastapi.status` apenas como uma conveniência para você, o desenvolvedor. Mas vem diretamente da Starlette.
///
## Alterando o padrão { #changing-the-default }
Mais tarde, no [Guia do Usuário Avançado](../advanced/response-change-status-code.md), você verá como retornar um código de status diferente do padrão que você está declarando aqui.
================================================
FILE: docs/pt/docs/tutorial/schema-extra-example.md
================================================
# Declare dados de exemplo da requisição { #declare-request-example-data }
Você pode declarar exemplos dos dados que sua aplicação pode receber.
Aqui estão várias maneiras de fazer isso.
## Dados extras de JSON Schema em modelos Pydantic { #extra-json-schema-data-in-pydantic-models }
Você pode declarar `examples` para um modelo Pydantic que serão adicionados ao JSON Schema gerado.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Essas informações extras serão adicionadas como estão ao **JSON Schema** de saída para esse modelo e serão usadas na documentação da API.
Você pode usar o atributo `model_config`, que recebe um `dict`, conforme descrito na [documentação do Pydantic: Configuration](https://docs.pydantic.dev/latest/api/config/).
Você pode definir `"json_schema_extra"` com um `dict` contendo quaisquer dados adicionais que você queira que apareçam no JSON Schema gerado, incluindo `examples`.
/// tip | Dica
Você poderia usar a mesma técnica para estender o JSON Schema e adicionar suas próprias informações extras personalizadas.
Por exemplo, você poderia usá-la para adicionar metadados para uma interface de usuário de front-end, etc.
///
/// info | Informação
O OpenAPI 3.1.0 (usado desde o FastAPI 0.99.0) adicionou suporte a `examples`, que faz parte do padrão **JSON Schema**.
Antes disso, ele suportava apenas a palavra‑chave `example` com um único exemplo. Isso ainda é suportado pelo OpenAPI 3.1.0, mas é descontinuado e não faz parte do padrão JSON Schema. Portanto, você é incentivado a migrar de `example` para `examples`. 🤓
Você pode ler mais no final desta página.
///
## Argumentos adicionais de `Field` { #field-additional-arguments }
Ao usar `Field()` com modelos Pydantic, você também pode declarar `examples` adicionais:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` no JSON Schema - OpenAPI { #examples-in-json-schema-openapi }
Ao usar qualquer um de:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
você também pode declarar um grupo de `examples` com informações adicionais que serão adicionadas aos seus **JSON Schemas** dentro do **OpenAPI**.
### `Body` com `examples` { #body-with-examples }
Aqui passamos `examples` contendo um exemplo dos dados esperados em `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Exemplo na UI da documentação { #example-in-the-docs-ui }
Com qualquer um dos métodos acima, ficaria assim em `/docs`:
### `Body` com vários `examples` { #body-with-multiple-examples }
Você também pode, é claro, passar vários `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Quando fizer isso, os exemplos farão parte do **JSON Schema** interno para esses dados do body.
No entanto, no momento em que isto foi escrito, o Swagger UI, a ferramenta responsável por exibir a UI da documentação, não suporta mostrar vários exemplos para os dados no **JSON Schema**. Mas leia abaixo para uma solução alternativa.
### `examples` específicos do OpenAPI { #openapi-specific-examples }
Antes do **JSON Schema** suportar `examples`, o OpenAPI já tinha suporte para um campo diferente também chamado `examples`.
Esse `examples` **específico do OpenAPI** vai em outra seção da especificação OpenAPI. Ele fica nos **detalhes de cada *operação de rota***, não dentro de cada JSON Schema.
E o Swagger UI tem suportado esse campo `examples` particular há algum tempo. Então, você pode usá-lo para **mostrar** diferentes **exemplos na UI da documentação**.
O formato desse campo `examples` específico do OpenAPI é um `dict` com **vários exemplos** (em vez de uma `list`), cada um com informações extras que também serão adicionadas ao **OpenAPI**.
Isso não vai dentro de cada JSON Schema contido no OpenAPI, vai fora, diretamente na *operação de rota*.
### Usando o parâmetro `openapi_examples` { #using-the-openapi-examples-parameter }
Você pode declarar o `examples` específico do OpenAPI no FastAPI com o parâmetro `openapi_examples` para:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
As chaves do `dict` identificam cada exemplo, e cada valor é outro `dict`.
Cada `dict` de exemplo específico em `examples` pode conter:
* `summary`: Descrição curta do exemplo.
* `description`: Uma descrição longa que pode conter texto em Markdown.
* `value`: Este é o exemplo em si, por exemplo, um `dict`.
* `externalValue`: Alternativa a `value`, uma URL apontando para o exemplo. Embora isso possa não ser suportado por tantas ferramentas quanto `value`.
Você pode usá-lo assim:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Exemplos do OpenAPI na UI da documentação { #openapi-examples-in-the-docs-ui }
Com `openapi_examples` adicionado a `Body()`, o `/docs` ficaria assim:
## Detalhes Técnicos { #technical-details }
/// tip | Dica
Se você já está usando o **FastAPI** na versão **0.99.0 ou superior**, você provavelmente pode **pular** esses detalhes.
Eles são mais relevantes para versões antigas, antes de o OpenAPI 3.1.0 estar disponível.
Você pode considerar isto uma breve **aula de história** sobre OpenAPI e JSON Schema. 🤓
///
/// warning | Atenção
Estes são detalhes muito técnicos sobre os padrões **JSON Schema** e **OpenAPI**.
Se as ideias acima já funcionam para você, isso pode ser suficiente, e você provavelmente não precisa desses detalhes, sinta-se à vontade para pular.
///
Antes do OpenAPI 3.1.0, o OpenAPI usava uma versão mais antiga e modificada do **JSON Schema**.
O JSON Schema não tinha `examples`, então o OpenAPI adicionou seu próprio campo `example` à sua versão modificada.
O OpenAPI também adicionou os campos `example` e `examples` a outras partes da especificação:
* [`Parameter Object` (na especificação)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object), usado no FastAPI por:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, no campo `content`, no `Media Type Object` (na especificação)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object), usado no FastAPI por:
* `Body()`
* `File()`
* `Form()`
/// info | Informação
Esse parâmetro antigo `examples` específico do OpenAPI agora é `openapi_examples` desde o FastAPI `0.103.0`.
///
### Campo `examples` do JSON Schema { #json-schemas-examples-field }
Depois, o JSON Schema adicionou um campo [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) em uma nova versão da especificação.
E então o novo OpenAPI 3.1.0 passou a se basear na versão mais recente (JSON Schema 2020-12), que incluiu esse novo campo `examples`.
E agora esse novo campo `examples` tem precedência sobre o antigo campo único (e customizado) `example`, que agora está descontinuado.
Esse novo campo `examples` no JSON Schema é **apenas uma `list`** de exemplos, não um dict com metadados extras como nos outros lugares do OpenAPI (descritos acima).
/// info | Informação
Mesmo após o lançamento do OpenAPI 3.1.0 com essa nova integração mais simples com o JSON Schema, por um tempo o Swagger UI, a ferramenta que fornece a documentação automática, não suportava OpenAPI 3.1.0 (passou a suportar desde a versão 5.0.0 🎉).
Por causa disso, versões do FastAPI anteriores à 0.99.0 ainda usavam versões do OpenAPI inferiores à 3.1.0.
///
### `examples` no Pydantic e no FastAPI { #pydantic-and-fastapi-examples }
Quando você adiciona `examples` dentro de um modelo Pydantic, usando `schema_extra` ou `Field(examples=["something"])`, esse exemplo é adicionado ao **JSON Schema** para esse modelo Pydantic.
E esse **JSON Schema** do modelo Pydantic é incluído no **OpenAPI** da sua API e, então, é usado na UI da documentação.
Em versões do FastAPI anteriores à 0.99.0 (0.99.0 e superiores usam o novo OpenAPI 3.1.0), quando você usava `example` ou `examples` com qualquer uma das outras utilidades (`Query()`, `Body()`, etc.), esses exemplos não eram adicionados ao JSON Schema que descreve esses dados (nem mesmo à versão própria do JSON Schema do OpenAPI), eles eram adicionados diretamente à declaração da *operação de rota* no OpenAPI (fora das partes do OpenAPI que usam o JSON Schema).
Mas agora que o FastAPI 0.99.0 e superiores usam o OpenAPI 3.1.0, que usa o JSON Schema 2020-12, e o Swagger UI 5.0.0 e superiores, tudo é mais consistente e os exemplos são incluídos no JSON Schema.
### Swagger UI e `examples` específicos do OpenAPI { #swagger-ui-and-openapi-specific-examples }
Agora, como o Swagger UI não suportava vários exemplos no JSON Schema (em 2023-08-26), os usuários não tinham uma forma de mostrar vários exemplos na documentação.
Para resolver isso, o FastAPI `0.103.0` **adicionou suporte** para declarar o mesmo antigo campo **específico do OpenAPI** `examples` com o novo parâmetro `openapi_examples`. 🤓
### Resumo { #summary }
Eu costumava dizer que não gostava tanto de história... e olha eu aqui agora dando aulas de "história tech". 😅
Em resumo, **atualize para o FastAPI 0.99.0 ou superior**, e as coisas ficam muito mais **simples, consistentes e intuitivas**, e você não precisa saber todos esses detalhes históricos. 😎
================================================
FILE: docs/pt/docs/tutorial/security/first-steps.md
================================================
# Segurança - Primeiros Passos { #security-first-steps }
Vamos imaginar que você tem a sua API de **backend** em algum domínio.
E você tem um **frontend** em outro domínio ou em um path diferente no mesmo domínio (ou em uma aplicação mobile).
E você quer uma maneira de o frontend autenticar com o backend, usando um **username** e **password**.
Podemos usar **OAuth2** para construir isso com o **FastAPI**.
Mas vamos poupar o seu tempo de ler toda a especificação extensa apenas para achar as pequenas informações de que você precisa.
Vamos usar as ferramentas fornecidas pelo **FastAPI** para lidar com segurança.
## Como Parece { #how-it-looks }
Vamos primeiro usar o código e ver como funciona, e depois voltaremos para entender o que está acontecendo.
## Crie um `main.py` { #create-main-py }
Copie o exemplo em um arquivo `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Execute-o { #run-it }
/// info | Informação
O pacote [`python-multipart`](https://github.com/Kludex/python-multipart) é instalado automaticamente com o **FastAPI** quando você executa o comando `pip install "fastapi[standard]"`.
Entretanto, se você usar o comando `pip install fastapi`, o pacote `python-multipart` não é incluído por padrão.
Para instalá-lo manualmente, certifique-se de criar um [ambiente virtual](../../virtual-environments.md), ativá-lo e então instalá-lo com:
```console
$ pip install python-multipart
```
Isso ocorre porque o **OAuth2** usa "form data" para enviar o `username` e o `password`.
///
Execute o exemplo com:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Verifique-o { #check-it }
Vá até a documentação interativa em: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá algo deste tipo:
/// check | Botão Autorizar!
Você já tem um novo botão 'Authorize'.
E sua operação de rota tem um pequeno cadeado no canto superior direito em que você pode clicar.
///
E se você clicar, verá um pequeno formulário de autorização para digitar um `username` e um `password` (e outros campos opcionais):
/// note | Nota
Não importa o que você digite no formulário, ainda não vai funcionar. Mas nós vamos chegar lá.
///
Claro que este não é o frontend para os usuários finais, mas é uma ótima ferramenta automática para documentar interativamente toda a sua API.
Pode ser usada pelo time de frontend (que pode ser você mesmo).
Pode ser usada por aplicações e sistemas de terceiros.
E também pode ser usada por você mesmo, para depurar, verificar e testar a mesma aplicação.
## O fluxo de `password` { #the-password-flow }
Agora vamos voltar um pouco e entender o que é isso tudo.
O "fluxo" `password` é uma das formas ("fluxos") definidas no OAuth2 para lidar com segurança e autenticação.
O OAuth2 foi projetado para que o backend ou a API pudesse ser independente do servidor que autentica o usuário.
Mas, neste caso, a mesma aplicação **FastAPI** irá lidar com a API e com a autenticação.
Então, vamos rever de um ponto de vista simplificado:
* O usuário digita o `username` e o `password` no frontend e pressiona `Enter`.
* O frontend (rodando no navegador do usuário) envia esse `username` e `password` para uma URL específica na nossa API (declarada com `tokenUrl="token"`).
* A API verifica esse `username` e `password`, e responde com um "token" (ainda não implementamos nada disso).
* Um "token" é apenas uma string com algum conteúdo que podemos usar depois para verificar esse usuário.
* Normalmente, um token é definido para expirar depois de algum tempo.
* Então, o usuário terá que fazer login novamente em algum momento.
* E se o token for roubado, o risco é menor. Não é como uma chave permanente que funcionará para sempre (na maioria dos casos).
* O frontend armazena esse token temporariamente em algum lugar.
* O usuário clica no frontend para ir para outra seção do aplicativo web.
* O frontend precisa buscar mais dados da API.
* Mas precisa de autenticação para aquele endpoint específico.
* Então, para autenticar com nossa API, ele envia um header `Authorization` com o valor `Bearer ` mais o token.
* Se o token contém `foobar`, o conteúdo do header `Authorization` seria: `Bearer foobar`.
## O `OAuth2PasswordBearer` do **FastAPI** { #fastapis-oauth2passwordbearer }
O **FastAPI** fornece várias ferramentas, em diferentes níveis de abstração, para implementar essas funcionalidades de segurança.
Neste exemplo, vamos usar **OAuth2**, com o fluxo **Password**, usando um token **Bearer**. Fazemos isso usando a classe `OAuth2PasswordBearer`.
/// info | Informação
Um token "bearer" não é a única opção.
Mas é a melhor para o nosso caso de uso.
E pode ser a melhor para a maioria dos casos de uso, a menos que você seja um especialista em OAuth2 e saiba exatamente por que existe outra opção que se adapta melhor às suas necessidades.
Nesse caso, o **FastAPI** também fornece as ferramentas para construí-la.
///
Quando criamos uma instância da classe `OAuth2PasswordBearer`, passamos o parâmetro `tokenUrl`. Esse parâmetro contém a URL que o client (o frontend rodando no navegador do usuário) usará para enviar o `username` e o `password` para obter um token.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Dica
Aqui `tokenUrl="token"` refere-se a uma URL relativa `token` que ainda não criamos. Como é uma URL relativa, é equivalente a `./token`.
Como estamos usando uma URL relativa, se sua API estivesse localizada em `https://example.com/`, então se referiria a `https://example.com/token`. Mas se sua API estivesse localizada em `https://example.com/api/v1/`, então se referiria a `https://example.com/api/v1/token`.
Usar uma URL relativa é importante para garantir que sua aplicação continue funcionando mesmo em um caso de uso avançado como [Atrás de um Proxy](../../advanced/behind-a-proxy.md).
///
Esse parâmetro não cria aquele endpoint/operação de rota, mas declara que a URL `/token` será aquela que o client deve usar para obter o token. Essa informação é usada no OpenAPI e depois nos sistemas de documentação interativa da API.
Em breve também criaremos a operação de rota real.
/// info | Informação
Se você é um "Pythonista" muito rigoroso, pode não gostar do estilo do nome do parâmetro `tokenUrl` em vez de `token_url`.
Isso ocorre porque ele usa o mesmo nome da especificação do OpenAPI. Assim, se você precisar investigar mais sobre qualquer um desses esquemas de segurança, pode simplesmente copiar e colar para encontrar mais informações sobre isso.
///
A variável `oauth2_scheme` é uma instância de `OAuth2PasswordBearer`, mas também é um "callable".
Ela pode ser chamada como:
```Python
oauth2_scheme(some, parameters)
```
Então, pode ser usada com `Depends`.
### Use-o { #use-it }
Agora você pode passar esse `oauth2_scheme` em uma dependência com `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Essa dependência fornecerá uma `str` que é atribuída ao parâmetro `token` da função de operação de rota.
O **FastAPI** saberá que pode usar essa dependência para definir um "esquema de segurança" no esquema OpenAPI (e na documentação automática da API).
/// info | Detalhes Técnicos
O **FastAPI** saberá que pode usar a classe `OAuth2PasswordBearer` (declarada em uma dependência) para definir o esquema de segurança no OpenAPI porque ela herda de `fastapi.security.oauth2.OAuth2`, que por sua vez herda de `fastapi.security.base.SecurityBase`.
Todos os utilitários de segurança que se integram com o OpenAPI (e com a documentação automática da API) herdam de `SecurityBase`, é assim que o **FastAPI** sabe como integrá-los ao OpenAPI.
///
## O que ele faz { #what-it-does }
Ele irá procurar na requisição pelo header `Authorization`, verificar se o valor é `Bearer ` mais algum token e retornará o token como uma `str`.
Se não houver um header `Authorization`, ou se o valor não tiver um token `Bearer `, ele responderá diretamente com um erro de status 401 (`UNAUTHORIZED`).
Você nem precisa verificar se o token existe para retornar um erro. Você pode ter certeza de que, se sua função for executada, ela terá uma `str` nesse token.
Você já pode experimentar na documentação interativa:
Ainda não estamos verificando a validade do token, mas isso já é um começo.
## Recapitulando { #recap }
Então, com apenas 3 ou 4 linhas extras, você já tem alguma forma primitiva de segurança.
================================================
FILE: docs/pt/docs/tutorial/security/get-current-user.md
================================================
# Obter Usuário Atual { #get-current-user }
No capítulo anterior, o sistema de segurança (que é baseado no sistema de injeção de dependências) estava fornecendo à *função de operação de rota* um `token` como uma `str`:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Mas isso ainda não é tão útil.
Vamos fazer com que ele nos forneça o usuário atual.
## Criar um modelo de usuário { #create-a-user-model }
Primeiro, vamos criar um modelo de usuário com Pydantic.
Da mesma forma que usamos o Pydantic para declarar corpos, podemos usá-lo em qualquer outro lugar:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Criar uma dependência `get_current_user` { #create-a-get-current-user-dependency }
Vamos criar uma dependência chamada `get_current_user`.
Lembra que as dependências podem ter subdependências?
`get_current_user` terá uma dependência com o mesmo `oauth2_scheme` que criamos antes.
Da mesma forma que estávamos fazendo antes diretamente na *operação de rota*, a nossa nova dependência `get_current_user` receberá um `token` como uma `str` da subdependência `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Obter o usuário { #get-the-user }
`get_current_user` usará uma função utilitária (falsa) que criamos, que recebe um token como uma `str` e retorna nosso modelo Pydantic `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Injetar o usuário atual { #inject-the-current-user }
Então agora nós podemos usar o mesmo `Depends` com nosso `get_current_user` na *operação de rota*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Observe que nós declaramos o tipo de `current_user` como o modelo Pydantic `User`.
Isso nos ajudará dentro da função com todo o preenchimento automático e verificações de tipo.
/// tip | Dica
Você pode se lembrar que corpos de requisição também são declarados com modelos Pydantic.
Aqui, o **FastAPI** não ficará confuso porque você está usando `Depends`.
///
/// check | Verifique
A forma como esse sistema de dependências foi projetado nos permite ter diferentes dependências (diferentes "dependables") que retornam um modelo `User`.
Não estamos restritos a ter apenas uma dependência que possa retornar esse tipo de dado.
///
## Outros modelos { #other-models }
Agora você pode obter o usuário atual diretamente nas *funções de operação de rota* e lidar com os mecanismos de segurança no nível da **Injeção de Dependências**, usando `Depends`.
E você pode usar qualquer modelo ou dado para os requisitos de segurança (neste caso, um modelo Pydantic `User`).
Mas você não está restrito a usar um modelo de dados, classe ou tipo específico.
Você quer ter apenas um `id` e `email`, sem incluir nenhum `username` no modelo? Claro. Você pode usar essas mesmas ferramentas.
Você quer ter apenas uma `str`? Ou apenas um `dict`? Ou uma instância de modelo de classe de banco de dados diretamente? Tudo funciona da mesma forma.
Na verdade, você não tem usuários que fazem login no seu aplicativo, mas sim robôs, bots ou outros sistemas, que possuem apenas um token de acesso? Novamente, tudo funciona da mesma forma.
Apenas use qualquer tipo de modelo, qualquer tipo de classe, qualquer tipo de banco de dados que você precise para a sua aplicação. O **FastAPI** cobre tudo com o sistema de injeção de dependências.
## Tamanho do código { #code-size }
Este exemplo pode parecer verboso. Lembre-se de que estamos misturando segurança, modelos de dados, funções utilitárias e *operações de rota* no mesmo arquivo.
Mas aqui está o ponto principal.
O código relacionado à segurança e à injeção de dependências é escrito apenas uma vez.
E você pode torná-lo tão complexo quanto quiser. E ainda assim, tê-lo escrito apenas uma vez, em um único lugar. Com toda a flexibilidade.
Mas você pode ter milhares de endpoints (*operações de rota*) usando o mesmo sistema de segurança.
E todos eles (ou qualquer parte deles que você desejar) podem aproveitar o reuso dessas dependências ou de quaisquer outras dependências que você criar.
E todos esses milhares de *operações de rota* podem ter apenas 3 linhas:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Recapitulação { #recap }
Agora você pode obter o usuário atual diretamente na sua *função de operação de rota*.
Já estamos na metade do caminho.
Só precisamos adicionar uma *operação de rota* para que o usuário/cliente realmente envie o `username` e `password`.
Isso vem a seguir.
================================================
FILE: docs/pt/docs/tutorial/security/index.md
================================================
# Segurança { #security }
Há várias formas de lidar segurança, autenticação e autorização.
E isso normalmente é um tópico “difícil” e complexo.
Em muitos frameworks e sistemas, apenas lidar com segurança e autenticação exige muito esforço e código (em muitos casos isso pode ser 50% ou mais de todo o código escrito).
**FastAPI** tem muitas ferramentas para ajudar você com a parte de **Segurança** facilmente, rapidamente, de uma forma padrão, sem ter que estudar e aprender tudo sobre especificações de segurança.
Mas primeiro, vamos verificar alguns pequenos conceitos.
## Está com pressa? { #in-a-hurry }
Se você não se importa com qualquer um desses termos e só precisa adicionar segurança com autenticação baseada em usuário e senha _agora_, pule para os próximos capítulos.
## OAuth2 { #oauth2 }
OAuth2 é uma especificação que define várias formas para lidar com autenticação e autorização.
Ela é bastante extensiva na especificação e cobre casos de uso muito complexos.
Ela inclui uma forma para autenticação usando “third party”/aplicações de terceiros.
Isso é o que todos os sistemas com “Login with Facebook, Google, X (Twitter), GitHub” usam por baixo.
### OAuth 1 { #oauth-1 }
Havia um OAuth 1, que é bem diferente do OAuth2, e mais complexo, isso incluía diretamente as especificações de como criptografar a comunicação.
Não é muito popular ou usado nos dias atuais.
OAuth2 não especifica como criptografar a comunicação, ele espera que você tenha sua aplicação em um servidor HTTPS.
/// tip | Dica
Na seção sobre **deployment** você irá ver como configurar HTTPS de modo gratuito, usando Traefik e Let’s Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect é outra especificação, baseada em **OAuth2**.
Ela é apenas uma extensão do OAuth2 especificando algumas coisas que são relativamente ambíguas no OAuth2, para tentar torná-lo mais interoperável.
Por exemplo, o login do Google usa OpenID Connect (que por baixo dos panos usa OAuth2).
Mas o login do Facebook não tem suporte para OpenID Connect. Ele tem a própria implementação do OAuth2.
### OpenID (não "OpenID Connect") { #openid-not-openid-connect }
Houve também uma especificação “OpenID”. Ela tentou resolver a mesma coisa que a **OpenID Connect**, mas não baseada em OAuth2.
Então, ela foi um sistema adicional completo.
Ela não é muito popular ou usada nos dias de hoje.
## OpenAPI { #openapi }
OpenAPI (anteriormente conhecido como Swagger) é a especificação aberta para a criação de APIs (agora parte da Linux Foundation).
**FastAPI** é baseado no **OpenAPI**.
Isso é o que torna possível ter múltiplas automações interativas de interfaces de documentação, geração de código, etc.
OpenAPI tem uma forma para definir múltiplos “esquemas” de segurança.
Por usá-los, você pode ter vantagens de todas essas ferramentas baseadas nos padrões, incluindo os sistemas de documentação interativa.
OpenAPI define os seguintes esquemas de segurança:
* `apiKey`: uma chave específica de aplicação que pode vir de:
* Um parâmetro query.
* Um header.
* Um cookie.
* `http`: padrão HTTP de sistemas autenticação, incluindo:
* `bearer`: um header de `Authorization` com valor de `Bearer` adicionado de um token. Isso é herança do OAuth2.
* HTTP Basic authentication.
* HTTP Digest, etc.
* `oauth2`: todas as formas do OAuth2 para lidar com segurança (chamados "fluxos").
* Vários desses fluxos são apropriados para construir um provedor de autenticação OAuth2 (como Google, Facebook, X (Twitter), GitHub, etc):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Mas existe um “fluxo” específico que pode ser perfeitamente usado para resolver autenticação diretamente na mesma aplicação:
* `password`: alguns dos próximos capítulos tratarão disso.
* `openIdConnect`: tem uma forma para definir como descobrir automaticamente o dado da autenticação OAuth2.
* Essa descoberta automática é o que é definido na especificação OpenID Connect.
/// tip | Dica
Integração com outros provedores de autenticação/autorização como Google, Facebook, X (Twitter), GitHub, etc. é bem possível e relativamente fácil.
O problema mais complexo é criar um provedor de autenticação/autorização como eles, mas o FastAPI dá a você ferramentas para fazer isso facilmente, enquanto faz o trabalho pesado para você.
///
## **FastAPI** utilitários { #fastapi-utilities }
**FastAPI** fornece várias ferramentas para cada um desses esquemas de segurança no módulo `fastapi.security` que simplificam o uso desses mecanismos de segurança.
Nos próximos capítulos você irá ver como adicionar segurança à sua API usando essas ferramentas disponibilizadas pelo **FastAPI**.
E você irá ver também como isso é automaticamente integrado dentro do sistema de documentação interativo.
================================================
FILE: docs/pt/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 com Senha (e hashing), Bearer com tokens JWT { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Agora que temos todo o fluxo de segurança, vamos tornar a aplicação realmente segura, usando tokens JWT e hashing de senhas seguras.
Este código é algo que você pode realmente usar na sua aplicação, salvar os hashes das senhas no seu banco de dados, etc.
Vamos começar de onde paramos no capítulo anterior e incrementá-lo.
## Sobre o JWT { #about-jwt }
JWT significa "JSON Web Tokens".
É um padrão para codificar um objeto JSON em uma string longa e densa sem espaços. Ele se parece com isso:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Ele não é criptografado, então qualquer pessoa pode recuperar as informações do seu conteúdo.
Mas ele é assinado. Assim, quando você recebe um token que você emitiu, você pode verificar que foi realmente você quem o emitiu.
Dessa forma, você pode criar um token com um prazo de expiração, digamos, de 1 semana. E então, quando o usuário voltar no dia seguinte com o token, você sabe que ele ainda está logado no seu sistema.
Depois de uma semana, o token expirará e o usuário não estará autorizado, precisando fazer login novamente para obter um novo token. E se o usuário (ou uma terceira parte) tentar modificar o token para alterar a expiração, você seria capaz de descobrir isso, pois as assinaturas não iriam corresponder.
Se você quiser brincar com tokens JWT e ver como eles funcionam, visite [https://jwt.io](https://jwt.io/).
## Instalar `PyJWT` { #install-pyjwt }
Nós precisamos instalar o `PyJWT` para criar e verificar os tokens JWT em Python.
Certifique-se de criar um [ambiente virtual](../../virtual-environments.md), ativá-lo e então instalar o `pyjwt`:
```console
$ pip install pyjwt
---> 100%
```
/// info | Informação
Se você pretente utilizar algoritmos de assinatura digital como o RSA ou o ECDSA, você deve instalar a dependência da biblioteca de criptografia `pyjwt[crypto]`.
Você pode ler mais sobre isso na [documentação de instalação do PyJWT](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Hashing de senhas { #password-hashing }
"Hashing" significa converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece um monte de caracteres sem sentido.
Sempre que você passar exatamente o mesmo conteúdo (exatamente a mesma senha), você obterá exatamente o mesmo resultado.
Mas não é possível converter os caracteres sem sentido de volta para a senha original.
### Por que usar hashing de senhas { #why-use-password-hashing }
Se o seu banco de dados for roubado, o invasor não terá as senhas em texto puro dos seus usuários, apenas os hashes.
Então, o invasor não poderá tentar usar essas senhas em outro sistema (como muitos usuários utilizam a mesma senha em vários lugares, isso seria perigoso).
## Instalar o `pwdlib` { #install-pwdlib }
pwdlib é um excelente pacote Python para lidar com hashes de senhas.
Ele suporta muitos algoritmos de hashing seguros e utilitários para trabalhar com eles.
O algoritmo recomendado é o "Argon2".
Certifique-se de criar um [ambiente virtual](../../virtual-environments.md), ativá-lo e então instalar o pwdlib com Argon2:
/// tip | Dica
Com o `pwdlib`, você poderia até configurá-lo para ser capaz de ler senhas criadas pelo **Django**, um plug-in de segurança do **Flask** ou muitos outros.
Assim, você poderia, por exemplo, compartilhar os mesmos dados de um aplicativo Django em um banco de dados com um aplicativo FastAPI. Ou migrar gradualmente uma aplicação Django usando o mesmo banco de dados.
E seus usuários poderiam fazer login tanto pela sua aplicação Django quanto pela sua aplicação **FastAPI**, ao mesmo tempo.
///
## Criar o hash e verificar as senhas { #hash-and-verify-the-passwords }
Importe as ferramentas que nós precisamos de `pwdlib`.
Crie uma instância de PasswordHash com as configurações recomendadas – ela será usada para criar o hash e verificar as senhas.
/// tip | Dica
pwdlib também oferece suporte ao algoritmo de hashing bcrypt, mas não inclui algoritmos legados – para trabalhar com hashes antigos, é recomendado usar a biblioteca passlib.
Por exemplo, você poderia usá-lo para ler e verificar senhas geradas por outro sistema (como Django), mas criar o hash de novas senhas com um algoritmo diferente, como o Argon2 ou o Bcrypt.
E ser compatível com todos eles ao mesmo tempo.
///
Crie uma função utilitária para criar o hash de uma senha fornecida pelo usuário.
E outra função utilitária para verificar se uma senha recebida corresponde ao hash armazenado.
E outra para autenticar e retornar um usuário.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Quando `authenticate_user` é chamado com um nome de usuário que não existe no banco de dados, ainda executamos `verify_password` contra um hash fictício.
Isso garante que o endpoint leve aproximadamente o mesmo tempo para responder, seja o nome de usuário válido ou não, prevenindo **timing attacks** que poderiam ser usados para enumerar nomes de usuário existentes.
/// note | Nota
Se você verificar o novo banco de dados (falso) `fake_users_db`, você verá como o hash da senha se parece agora: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Manipular tokens JWT { #handle-jwt-tokens }
Importe os módulos instalados.
Crie uma chave secreta aleatória que será usada para assinar os tokens JWT.
Para gerar uma chave secreta aleatória e segura, use o comando:
E copie a saída para a variável `SECRET_KEY` (não use a do exemplo).
Crie uma variável `ALGORITHM` com o algoritmo usado para assinar o token JWT e defina como `"HS256"`.
Crie uma variável para a expiração do token.
Defina um modelo Pydantic que será usado no endpoint de token para a resposta.
Crie uma função utilitária para gerar um novo token de acesso.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Atualize as dependências { #update-the-dependencies }
Atualize `get_current_user` para receber o mesmo token de antes, mas desta vez, usando tokens JWT.
Decodifique o token recebido, verifique-o e retorne o usuário atual.
Se o token for inválido, retorne um erro HTTP imediatamente.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Atualize a *operação de rota* `/token` { #update-the-token-path-operation }
Crie um `timedelta` com o tempo de expiração do token.
Crie um token de acesso JWT real e o retorne.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Detalhes técnicos sobre o "sujeito" `sub` do JWT { #technical-details-about-the-jwt-subject-sub }
A especificação JWT diz que existe uma chave `sub`, com o sujeito do token.
É opcional usá-la, mas é onde você colocaria a identificação do usuário, então nós estamos usando aqui.
O JWT pode ser usado para outras coisas além de identificar um usuário e permitir que ele execute operações diretamente na sua API.
Por exemplo, você poderia identificar um "carro" ou uma "postagem de blog".
Depois, você poderia adicionar permissões sobre essa entidade, como "dirigir" (para o carro) ou "editar" (para o blog).
E então, poderia dar esse token JWT para um usuário (ou bot), e ele poderia usá-lo para realizar essas ações (dirigir o carro ou editar o blog) sem sequer precisar ter uma conta, apenas com o token JWT que sua API gerou para isso.
Usando essas ideias, o JWT pode ser usado para cenários muito mais sofisticados.
Nesses casos, várias dessas entidades poderiam ter o mesmo ID, digamos `foo` (um usuário `foo`, um carro `foo` e uma postagem de blog `foo`).
Então, para evitar colisões de ID, ao criar o token JWT para o usuário, você poderia prefixar o valor da chave `sub`, por exemplo, com `username:`. Assim, neste exemplo, o valor de `sub` poderia ser: `username:johndoe`.
O importante a se lembrar é que a chave `sub` deve ter um identificador único em toda a aplicação e deve ser uma string.
## Verifique { #check-it }
Execute o servidor e vá para a documentação: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Você verá a interface de usuário assim:
Autorize a aplicação da mesma maneira que antes.
Usando as credenciais:
Username: `johndoe`
Password: `secret`
/// check | Verifique
Observe que em nenhuma parte do código está a senha em texto puro "`secret`", nós temos apenas o hash.
///
Chame o endpoint `/users/me/`, você receberá o retorno como:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Se você abrir as ferramentas de desenvolvedor, poderá ver que os dados enviados incluem apenas o token. A senha é enviada apenas na primeira requisição para autenticar o usuário e obter o token de acesso, mas não é enviada nas próximas requisições:
/// note | Nota
Perceba que o cabeçalho `Authorization`, com o valor que começa com `Bearer `.
///
## Uso avançado com `scopes` { #advanced-usage-with-scopes }
O OAuth2 tem a noção de "scopes" (escopos).
Você pode usá-los para adicionar um conjunto específico de permissões a um token JWT.
Então, você pode dar este token diretamente a um usuário ou a uma terceira parte para interagir com sua API com um conjunto de restrições.
Você pode aprender como usá-los e como eles são integrados ao **FastAPI** mais adiante no **Guia Avançado do Usuário**.
## Recapitulação { #recap }
Com o que você viu até agora, você pode configurar uma aplicação **FastAPI** segura usando padrões como OAuth2 e JWT.
Em quase qualquer framework, lidar com a segurança se torna rapidamente um assunto bastante complexo.
Muitos pacotes que simplificam bastante isso precisam fazer muitas concessões com o modelo de dados, o banco de dados e os recursos disponíveis. E alguns desses pacotes que simplificam demais na verdade têm falhas de segurança subjacentes.
---
O **FastAPI** não faz nenhuma concessão com nenhum banco de dados, modelo de dados ou ferramenta.
Ele oferece toda a flexibilidade para você escolher as opções que melhor se ajustam ao seu projeto.
E você pode usar diretamente muitos pacotes bem mantidos e amplamente utilizados, como `pwdlib` e `PyJWT`, porque o **FastAPI** não exige mecanismos complexos para integrar pacotes externos.
Mas ele fornece as ferramentas para simplificar o processo o máximo possível, sem comprometer a flexibilidade, robustez ou segurança.
E você pode usar e implementar protocolos padrão seguros, como o OAuth2, de uma maneira relativamente simples.
Você pode aprender mais no **Guia Avançado do Usuário** sobre como usar os "scopes" do OAuth2 para um sistema de permissões mais refinado, seguindo esses mesmos padrões. O OAuth2 com scopes é o mecanismo usado por muitos provedores grandes de autenticação, como o Facebook, Google, GitHub, Microsoft, X (Twitter), etc. para autorizar aplicativos de terceiros a interagir com suas APIs em nome de seus usuários.
================================================
FILE: docs/pt/docs/tutorial/security/simple-oauth2.md
================================================
# Simples OAuth2 com senha e Bearer { #simple-oauth2-with-password-and-bearer }
Agora vamos construir a partir do capítulo anterior e adicionar as partes que faltam para ter um fluxo de segurança completo.
## Obtenha o `username` e a `password` { #get-the-username-and-password }
É utilizado o utils de segurança da **FastAPI** para obter o `username` e a `password`.
OAuth2 especifica que ao usar o "password flow" (fluxo de senha), que estamos usando, o cliente/usuário deve enviar os campos `username` e `password` como dados do formulário.
E a especificação diz que os campos devem ser nomeados assim. Portanto, `user-name` ou `email` não funcionariam.
Mas não se preocupe, você pode mostrá-lo como quiser aos usuários finais no frontend.
E seus modelos de banco de dados podem usar qualquer outro nome que você desejar.
Mas para a *operação de rota* de login, precisamos usar esses nomes para serem compatíveis com a especificação (e poder, por exemplo, usar o sistema integrado de documentação da API).
A especificação também afirma que o `username` e a `password` devem ser enviados como dados de formulário (portanto, não há JSON aqui).
### `scope` { #scope }
A especificação também diz que o cliente pode enviar outro campo de formulário "`scope`".
O nome do campo do formulário é `scope` (no singular), mas na verdade é uma longa string com "escopos" separados por espaços.
Cada “scope” é apenas uma string (sem espaços).
Normalmente são usados para declarar permissões de segurança específicas, por exemplo:
* `users:read` ou `users:write` são exemplos comuns.
* `instagram_basic` é usado pelo Facebook e Instagram.
* `https://www.googleapis.com/auth/drive` é usado pelo Google.
/// info | Informação
No OAuth2, um "scope" é apenas uma string que declara uma permissão específica necessária.
Não importa se tem outros caracteres como `:` ou se é uma URL.
Esses detalhes são específicos da implementação.
Para OAuth2 são apenas strings.
///
## Código para conseguir o `username` e a `password` { #code-to-get-the-username-and-password }
Agora vamos usar os utilitários fornecidos pelo **FastAPI** para lidar com isso.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Primeiro, importe `OAuth2PasswordRequestForm` e use-o como uma dependência com `Depends` na *operação de rota* para `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` é uma dependência de classe que declara um corpo de formulário com:
* O `username`.
* A `password`.
* Um campo `scope` opcional como uma string grande, composta de strings separadas por espaços.
* Um `grant_type` opcional.
/// tip | Dica
A especificação OAuth2 na verdade *requer* um campo `grant_type` com um valor fixo de `password`, mas `OAuth2PasswordRequestForm` não o impõe.
Se você precisar aplicá-lo, use `OAuth2PasswordRequestFormStrict` em vez de `OAuth2PasswordRequestForm`.
///
* Um `client_id` opcional (não precisamos dele em nosso exemplo).
* Um `client_secret` opcional (não precisamos dele em nosso exemplo).
/// info | Informação
O `OAuth2PasswordRequestForm` não é uma classe especial para **FastAPI** como é `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` faz com que **FastAPI** saiba que é um esquema de segurança. Portanto, é adicionado dessa forma ao OpenAPI.
Mas `OAuth2PasswordRequestForm` é apenas uma dependência de classe que você mesmo poderia ter escrito ou poderia ter declarado os parâmetros do `Form` (formulário) diretamente.
Mas como é um caso de uso comum, ele é fornecido diretamente pelo **FastAPI**, apenas para facilitar.
///
### Use os dados do formulário { #use-the-form-data }
/// tip | Dica
A instância da classe de dependência `OAuth2PasswordRequestForm` não terá um atributo `scope` com a string longa separada por espaços, em vez disso, terá um atributo `scopes` com a lista real de strings para cada escopo enviado.
Não estamos usando `scopes` neste exemplo, mas a funcionalidade está disponível se você precisar.
///
Agora, obtenha os dados do usuário do banco de dados (falso), usando o `username` do campo do formulário.
Se não existir tal usuário, retornaremos um erro dizendo "Incorrect username or password".
Para o erro, usamos a exceção `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Confira a senha { #check-the-password }
Neste ponto temos os dados do usuário do nosso banco de dados, mas não verificamos a senha.
Vamos colocar esses dados primeiro no modelo `UserInDB` do Pydantic.
Você nunca deve salvar senhas em texto simples, portanto, usaremos o sistema de hashing de senhas (falsas).
Se as senhas não corresponderem, retornaremos o mesmo erro.
#### Hashing de senha { #password-hashing }
"Hashing" significa: converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece algo sem sentido.
Sempre que você passa exatamente o mesmo conteúdo (exatamente a mesma senha), você obtém exatamente a mesma sequência aleatória de caracteres.
Mas você não pode converter a sequência aleatória de caracteres de volta para a senha.
##### Porque usar hashing de senha { #why-use-password-hashing }
Se o seu banco de dados for roubado, o ladrão não terá as senhas em texto simples dos seus usuários, apenas os hashes.
Assim, o ladrão não poderá tentar usar essas mesmas senhas em outro sistema (como muitos usuários usam a mesma senha em todos os lugares, isso seria perigoso).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Sobre `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` significa:
*Passe as chaves e valores de `user_dict` diretamente como argumentos de valor-chave, equivalente a:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Informação
Para uma explicação mais completa de `**user_dict`, verifique [a documentação para **Extra Models**](../extra-models.md#about-user-in-dict).
///
## Retorne o token { #return-the-token }
A resposta do endpoint `token` deve ser um objeto JSON.
Deve ter um `token_type`. No nosso caso, como estamos usando tokens "Bearer", o tipo de token deve ser "`bearer`".
E deve ter um `access_token`, com uma string contendo nosso token de acesso.
Para este exemplo simples, seremos completamente inseguros e retornaremos o mesmo `username` do token.
/// tip | Dica
No próximo capítulo, você verá uma implementação realmente segura, com hash de senha e tokens JWT.
Mas, por enquanto, vamos nos concentrar nos detalhes específicos de que precisamos.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Dica
Pela especificação, você deve retornar um JSON com um `access_token` e um `token_type`, o mesmo que neste exemplo.
Isso é algo que você mesmo deve fazer em seu código e certifique-se de usar essas chaves JSON.
É quase a única coisa que você deve se lembrar de fazer corretamente, para estar em conformidade com as especificações.
De resto, **FastAPI** cuida disso para você.
///
## Atualize as dependências { #update-the-dependencies }
Agora vamos atualizar nossas dependências.
Queremos obter o `current_user` *somente* se este usuário estiver ativo.
Portanto, criamos uma dependência adicional `get_current_active_user` que por sua vez usa `get_current_user` como dependência.
Ambas as dependências retornarão apenas um erro HTTP se o usuário não existir ou se estiver inativo.
Portanto, em nosso endpoint, só obteremos um usuário se o usuário existir, tiver sido autenticado corretamente e estiver ativo:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Informação
O cabeçalho adicional `WWW-Authenticate` com valor `Bearer` que estamos retornando aqui também faz parte da especificação.
Qualquer código de status HTTP (erro) 401 "UNAUTHORIZED" também deve retornar um cabeçalho `WWW-Authenticate`.
No caso de tokens ao portador (nosso caso), o valor desse cabeçalho deve ser `Bearer`.
Na verdade, você pode pular esse cabeçalho extra e ainda funcionaria.
Mas é fornecido aqui para estar em conformidade com as especificações.
Além disso, pode haver ferramentas que esperam e usam isso (agora ou no futuro) e que podem ser úteis para você ou seus usuários, agora ou no futuro.
Esse é o benefício dos padrões...
///
## Veja em ação { #see-it-in-action }
Abra o docs interativo: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Autentique-se { #authenticate }
Clique no botão "Authorize".
Use as credenciais:
User: `johndoe`
Password: `secret`
Após autenticar no sistema, você verá assim:
### Obtenha seus próprios dados de usuário { #get-your-own-user-data }
Agora use a operação `GET` com o caminho `/users/me`.
Você obterá os dados do seu usuário, como:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Se você clicar no ícone de cadeado, sair e tentar a mesma operação novamente, receberá um erro HTTP 401 de:
```JSON
{
"detail": "Not authenticated"
}
```
### Usuário inativo { #inactive-user }
Agora tente com um usuário inativo, autentique-se com:
User: `alice`
Password: `secret2`
E tente usar a operação `GET` com o caminho `/users/me`.
Você receberá um erro "Usuário inativo", como:
```JSON
{
"detail": "Inactive user"
}
```
## Recapitulando { #recap }
Agora você tem as ferramentas para implementar um sistema de segurança completo baseado em `username` e `password` para sua API.
Usando essas ferramentas, você pode tornar o sistema de segurança compatível com qualquer banco de dados e com qualquer usuário ou modelo de dados.
O único detalhe que falta é que ainda não é realmente "seguro".
No próximo capítulo você verá como usar uma biblioteca de hashing de senha segura e tokens JWT.
================================================
FILE: docs/pt/docs/tutorial/server-sent-events.md
================================================
# Eventos Enviados pelo Servidor (SSE) { #server-sent-events-sse }
Você pode transmitir dados para o cliente usando Server-Sent Events (SSE).
Isso é semelhante a [Stream de JSON Lines](stream-json-lines.md), mas usa o formato `text/event-stream`, que é suportado nativamente pelos navegadores com a [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info | Informação
Adicionado no FastAPI 0.135.0.
///
## O que são Server-Sent Events? { #what-are-server-sent-events }
SSE é um padrão para transmitir dados do servidor para o cliente via HTTP.
Cada evento é um pequeno bloco de texto com “campos” como `data`, `event`, `id` e `retry`, separados por linhas em branco.
Fica assim:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE é comumente usado para streaming de chat de IA, notificações em tempo real, logs e observabilidade, e outros casos em que o servidor envia atualizações para o cliente.
/// tip | Dica
Se você quiser transmitir dados binários, por exemplo vídeo ou áudio, veja o guia avançado: [Stream de Dados](../advanced/stream-data.md).
///
## Transmitir SSE com FastAPI { #stream-sse-with-fastapi }
Para transmitir SSE com FastAPI, use `yield` na sua função de operação de rota e defina `response_class=EventSourceResponse`.
Importe `EventSourceResponse` de `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Cada item produzido é codificado como JSON e enviado no campo `data:` de um evento SSE.
Se você declarar o tipo de retorno como `AsyncIterable[Item]`, o FastAPI o usará para validar, documentar e serializar os dados com o Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Dica
Como o Pydantic fará a serialização no lado em **Rust**, você terá um desempenho muito maior do que se não declarar um tipo de retorno.
///
### *Funções de operação de rota* não assíncronas { #non-async-path-operation-functions }
Você também pode usar funções `def` normais (sem `async`) e usar `yield` da mesma forma.
O FastAPI garantirá a execução correta para não bloquear o event loop.
Como, neste caso, a função não é assíncrona, o tipo de retorno adequado seria `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Sem tipo de retorno { #no-return-type }
Você também pode omitir o tipo de retorno. O FastAPI usará o [`jsonable_encoder`](./encoder.md) para converter os dados e enviá-los.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Se você precisar definir campos de SSE como `event`, `id`, `retry` ou `comment`, você pode produzir objetos `ServerSentEvent` em vez de dados simples.
Importe `ServerSentEvent` de `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
O campo `data` é sempre codificado como JSON. Você pode passar qualquer valor que possa ser serializado como JSON, incluindo modelos do Pydantic.
## Dados brutos { #raw-data }
Se você precisar enviar dados sem codificação JSON, use `raw_data` em vez de `data`.
Isto é útil para enviar texto pré-formatado, linhas de log ou valores "sentinela" especiais como `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Nota
`data` e `raw_data` são mutuamente exclusivos. Você só pode definir um deles em cada `ServerSentEvent`.
///
## Retomando com `Last-Event-ID` { #resuming-with-last-event-id }
Quando um navegador se reconecta após uma queda na conexão, ele envia o último `id` recebido no cabeçalho `Last-Event-ID`.
Você pode lê-lo como um parâmetro de cabeçalho e usá-lo para retomar o stream de onde o cliente parou:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE com POST { #sse-with-post }
SSE funciona com qualquer método HTTP, não apenas `GET`.
Isso é útil para protocolos como o [MCP](https://modelcontextprotocol.io) que fazem stream de SSE via `POST`:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Detalhes Técnicos { #technical-details }
O FastAPI implementa algumas boas práticas de SSE prontas para uso.
- Enviar um comentário de keep alive `ping` a cada 15 segundos quando não houver mensagens, para evitar que alguns proxies fechem a conexão, como sugerido na [especificação HTML: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
- Definir o cabeçalho `Cache-Control: no-cache` para evitar o cache do stream.
- Definir o cabeçalho especial `X-Accel-Buffering: no` para evitar buffering em alguns proxies como o Nginx.
Você não precisa fazer nada, isso funciona automaticamente. 🤓
================================================
FILE: docs/pt/docs/tutorial/sql-databases.md
================================================
# Bancos de Dados SQL (Relacionais) { #sql-relational-databases }
**FastAPI** não exige que você use um banco de dados SQL (relacional). Mas você pode usar **qualquer banco de dados** que quiser.
Aqui veremos um exemplo usando [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** é construído sobre [SQLAlchemy](https://www.sqlalchemy.org/) e Pydantic. Ele foi criado pelo mesmo autor do **FastAPI** para ser o par perfeito para aplicações **FastAPI** que precisam usar **bancos de dados SQL**.
/// tip | Dica
Você pode usar qualquer outra biblioteca de banco de dados SQL ou NoSQL que quiser (em alguns casos chamadas de "ORMs"), o FastAPI não obriga você a usar nada. 😎
///
Como o SQLModel é baseado no SQLAlchemy, você pode facilmente usar **qualquer banco de dados suportado** pelo SQLAlchemy (o que também os torna suportados pelo SQLModel), como:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, etc.
Neste exemplo, usaremos **SQLite**, porque ele usa um único arquivo e o Python tem suporte integrado. Assim, você pode copiar este exemplo e executá-lo como está.
Mais tarde, para sua aplicação em produção, você pode querer usar um servidor de banco de dados como o **PostgreSQL**.
/// tip | Dica
Existe um gerador de projetos oficial com **FastAPI** e **PostgreSQL** incluindo um frontend e mais ferramentas: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Este é um tutorial muito simples e curto, se você quiser aprender sobre bancos de dados em geral, sobre SQL ou recursos mais avançados, acesse a [documentação do SQLModel](https://sqlmodel.tiangolo.com/).
## Instalar o `SQLModel` { #install-sqlmodel }
Primeiro, certifique-se de criar seu [ambiente virtual](../virtual-environments.md), ativá-lo e, em seguida, instalar o `sqlmodel`:
```console
$ pip install sqlmodel
---> 100%
```
## Crear o App com um Único Modelo { #create-the-app-with-a-single-model }
Vamos criar a primeira versão mais simples do app com um único modelo **SQLModel**.
Depois, vamos melhorá-lo aumentando a segurança e versatilidade com **múltiplos modelos** abaixo. 🤓
### Criar Modelos { #create-models }
Importe o `SQLModel` e crie um modelo de banco de dados:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
A classe `Hero` é muito semelhante a um modelo Pydantic (na verdade, por baixo dos panos, ela *é um modelo Pydantic*).
Existem algumas diferenças:
* `table=True` informa ao SQLModel que este é um *modelo de tabela*, ele deve representar uma **tabela** no banco de dados SQL, não é apenas um *modelo de dados* (como seria qualquer outra classe Pydantic comum).
* `Field(primary_key=True)` informa ao SQLModel que o `id` é a **chave primária** no banco de dados SQL (você pode aprender mais sobre chaves primárias SQL na documentação do SQLModel).
**Nota:** Usamos `int | None` para o campo de chave primária para que, no código Python, possamos *criar um objeto sem um `id`* (`id=None`), assumindo que o banco de dados irá *gerá-lo ao salvar*. O SQLModel entende que o banco de dados fornecerá o `id` e *define a coluna como um `INTEGER` não nulo* no esquema do banco de dados. Veja a [documentação do SQLModel sobre chaves primárias](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) para detalhes.
* `Field(index=True)` informa ao SQLModel que ele deve criar um **índice SQL** para essa coluna, o que permitirá buscas mais rápidas no banco de dados ao ler dados filtrados por essa coluna.
O SQLModel saberá que algo declarado como `str` será uma coluna SQL do tipo `TEXT` (ou `VARCHAR`, dependendo do banco de dados).
### Criar um Engine { #create-an-engine }
Um `engine` SQLModel (por baixo dos panos, ele é na verdade um `engine` do SQLAlchemy) é o que **mantém as conexões** com o banco de dados.
Você teria **um único objeto `engine`** para todo o seu código se conectar ao mesmo banco de dados.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Usar `check_same_thread=False` permite que o FastAPI use o mesmo banco de dados SQLite em diferentes threads. Isso é necessário, pois **uma única requisição** pode usar **mais de uma thread** (por exemplo, em dependências).
Não se preocupe, com a forma como o código está estruturado, garantiremos que usamos **uma única *sessão* SQLModel por requisição** mais tarde, isso é realmente o que o `check_same_thread` está tentando conseguir.
### Criar as Tabelas { #create-the-tables }
Em seguida, adicionamos uma função que usa `SQLModel.metadata.create_all(engine)` para **criar as tabelas** para todos os *modelos de tabela*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Criar uma Dependência de Sessão { #create-a-session-dependency }
Uma **`Session`** é o que armazena os **objetos na memória** e acompanha as alterações necessárias nos dados, para então **usar o `engine`** para se comunicar com o banco de dados.
Vamos criar uma **dependência** do FastAPI com `yield` que fornecerá uma nova `Session` para cada requisição. Isso é o que garante que usamos uma única sessão por requisição. 🤓
Então, criamos uma dependência `Annotated` chamada `SessionDep` para simplificar o restante do código que usará essa dependência.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Criar Tabelas de Banco de Dados na Inicialização { #create-database-tables-on-startup }
Vamos criar as tabelas do banco de dados quando o aplicativo for iniciado.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Aqui, criamos as tabelas em um evento de inicialização do aplicativo.
Para produção, você provavelmente usaria um script de migração que é executado antes de iniciar seu app. 🤓
/// tip | Dica
O SQLModel terá utilitários de migração envolvendo o Alembic, mas por enquanto, você pode usar o [Alembic](https://alembic.sqlalchemy.org/en/latest/) diretamente.
///
### Criar um Hero { #create-a-hero }
Como cada modelo SQLModel também é um modelo Pydantic, você pode usá-lo nas mesmas **anotações de tipo** que usaria para modelos Pydantic.
Por exemplo, se você declarar um parâmetro do tipo `Hero`, ele será lido do **corpo JSON**.
Da mesma forma, você pode declará-lo como o **tipo de retorno** da função, e então o formato dos dados aparecerá na interface de documentação automática da API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Aqui, usamos a dependência `SessionDep` (uma `Session`) para adicionar o novo `Hero` à instância `Session`, fazer commit das alterações no banco de dados, atualizar os dados no `hero` e então retorná-lo.
### Ler Heroes { #read-heroes }
Podemos **ler** `Hero`s do banco de dados usando um `select()`. Podemos incluir um `limit` e `offset` para paginar os resultados.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Ler um Único Hero { #read-one-hero }
Podemos **ler** um único `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Deletar um Hero { #delete-a-hero }
Também podemos **deletar** um `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Executar o App { #run-the-app }
Você pode executar o app:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Então, vá para a interface `/docs`, você verá que o **FastAPI** está usando esses **modelos** para **documentar** a API, e ele também os usará para **serializar** e **validar** os dados.
## Atualizar o App com Múltiplos Modelos { #update-the-app-with-multiple-models }
Agora vamos **refatorar** este app um pouco para aumentar a **segurança** e **versatilidade**.
Se você verificar o app anterior, na interface você pode ser que, até agora, ele permite que o cliente decida o `id` do `Hero` a ser criado. 😱
Não deveríamos deixar isso acontecer, eles poderiam sobrescrever um `id` que já atribuimos na base de dados. Decidir o `id` deve ser feito pelo **backend** ou pelo **banco de dados**, **não pelo cliente**.
Além disso, criamos um `secret_name` para o hero, mas até agora estamos retornando ele em todos os lugares, isso não é muito **secreto**... 😅
Vamos corrigir essas coisas adicionando alguns **modelos extras**. Aqui é onde o SQLModel vai brilhar. ✨
### Criar Múltiplos Modelos { #create-multiple-models }
No **SQLModel**, qualquer classe de modelo que tenha `table=True` é um **modelo de tabela**.
E qualquer classe de modelo que não tenha `table=True` é um **modelo de dados**, esses são na verdade apenas modelos Pydantic (com alguns recursos extras pequenos). 🤓
Com o SQLModel, podemos usar a **herança** para **evitar duplicação** de todos os campos em todos os casos.
#### `HeroBase` - a classe base { #herobase-the-base-class }
Vamos começar com um modelo `HeroBase` que tem todos os **campos compartilhados** por todos os modelos:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - o *modelo de tabela* { #hero-the-table-model }
Em seguida, vamos criar `Hero`, o verdadeiro *modelo de tabela*, com os **campos extras** que nem sempre estão nos outros modelos:
* `id`
* `secret_name`
Como `Hero` herda de `HeroBase`, ele **também** tem os **campos** declarados em `HeroBase`, então todos os campos para `Hero` são:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - o *modelo de dados* público { #heropublic-the-public-data-model }
Em seguida, criamos um modelo `HeroPublic`, que será **retornado** para os clientes da API.
Ele tem os mesmos campos que `HeroBase`, então não incluirá `secret_name`.
Finalmente, a identidade dos nossos heróis está protegida! 🥷
Ele também declara novamente `id: int`. Ao fazer isso, estamos fazendo um **contrato** com os clientes da API, para que eles possam sempre esperar que o `id` estará lá e será um `int` (nunca será `None`).
/// tip | Dica
Fazer com que o modelo de retorno garanta que um valor esteja sempre disponível e sempre seja um `int` (não `None`) é muito útil para os clientes da API, eles podem escrever código muito mais simples com essa certeza.
Além disso, **clientes gerados automaticamente** terão interfaces mais simples, para que os desenvolvedores que se comunicam com sua API possam ter uma experiência muito melhor trabalhando com sua API. 😎
///
Todos os campos em `HeroPublic` são os mesmos que em `HeroBase`, com `id` declarado como `int` (não `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - o *modelo de dados* para criar um hero { #herocreate-the-data-model-to-create-a-hero }
Agora criamos um modelo `HeroCreate`, este é o que **validará** os dados dos clientes.
Ele tem os mesmos campos que `HeroBase`, e também tem `secret_name`.
Agora, quando os clientes **criarem um novo hero**, eles enviarão o `secret_name`, ele será armazenado no banco de dados, mas esses nomes secretos não serão retornados na API para os clientes.
/// tip | Dica
É assim que você trataria **senhas**. Receba-as, mas não as retorne na API.
Você também faria um **hash** com os valores das senhas antes de armazená-los, **nunca os armazene em texto simples**.
///
Os campos de `HeroCreate` são:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - o *modelo de dados* para atualizar um hero { #heroupdate-the-data-model-to-update-a-hero }
Não tínhamos uma maneira de **atualizar um hero** na versão anterior do app, mas agora com **múltiplos modelos**, podemos fazer isso. 🎉
O *modelo de dados* `HeroUpdate` é um pouco especial, ele tem **todos os mesmos campos** que seriam necessários para criar um novo hero, mas todos os campos são **opcionais** (todos têm um valor padrão). Dessa forma, quando você atualizar um hero, poderá enviar apenas os campos que deseja atualizar.
Como todos os **campos realmente mudam** (o tipo agora inclui `None` e eles agora têm um valor padrão de `None`), precisamos **declarar novamente** todos eles.
Não precisamos herdar de `HeroBase`, pois estamos redeclarando todos os campos. Vou deixá-lo herdando apenas por consistência, mas isso não é necessário. É mais uma questão de gosto pessoal. 🤷
Os campos de `HeroUpdate` são:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Criar com `HeroCreate` e retornar um `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Agora que temos **múltiplos modelos**, podemos atualizar as partes do app que os utilizam.
Recebemos na requisição um *modelo de dados* `HeroCreate`, e a partir dele, criamos um *modelo de tabela* `Hero`.
Esse novo *modelo de tabela* `Hero` terá os campos enviados pelo cliente, e também terá um `id` gerado pelo banco de dados.
Em seguida, retornamos o mesmo *modelo de tabela* `Hero` como está na função. Mas como declaramos o `response_model` com o *modelo de dados* `HeroPublic`, o **FastAPI** usará `HeroPublic` para validar e serializar os dados.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Dica
Agora usamos `response_model=HeroPublic` em vez da **anotação de tipo de retorno** `-> HeroPublic` porque o valor que estamos retornando na verdade *não* é um `HeroPublic`.
Se tivéssemos declarado `-> HeroPublic`, seu editor e o linter reclamariam (com razão) que você está retornando um `Hero` em vez de um `HeroPublic`.
Ao declará-lo no `response_model`, estamos dizendo ao **FastAPI** para fazer o seu trabalho, sem interferir nas anotações de tipo e na ajuda do seu editor e de outras ferramentas.
///
### Ler Heroes com `HeroPublic` { #read-heroes-with-heropublic }
Podemos fazer o mesmo que antes para **ler** `Hero`s, novamente, usamos `response_model=list[HeroPublic]` para garantir que os dados sejam validados e serializados corretamente.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Ler Um Hero com `HeroPublic` { #read-one-hero-with-heropublic }
Podemos **ler** um único herói:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Atualizar um Hero com `HeroUpdate` { #update-a-hero-with-heroupdate }
Podemos **atualizar um hero**. Para isso, usamos uma operação HTTP `PATCH`.
E no código, obtemos um `dict` com todos os dados enviados pelo cliente, **apenas os dados enviados pelo cliente**, excluindo quaisquer valores que estariam lá apenas por serem os valores padrão. Para fazer isso, usamos `exclude_unset=True`. Este é o truque principal. 🪄
Em seguida, usamos `hero_db.sqlmodel_update(hero_data)` para atualizar o `hero_db` com os dados de `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Deletar um Hero Novamente { #delete-a-hero-again }
**Deletar** um hero permanece praticamente o mesmo.
Não vamos satisfazer o desejo de refatorar tudo neste aqui. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Executar o App Novamente { #run-the-app-again }
Você pode executar o app novamente:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Se você for para a interface `/docs` da API, verá que agora ela está atualizada e não esperará receber o `id` do cliente ao criar um hero, etc.
## Recapitulando { #recap }
Você pode usar [**SQLModel**](https://sqlmodel.tiangolo.com/) para interagir com um banco de dados SQL e simplificar o código com *modelos de dados* e *modelos de tabela*.
Você pode aprender muito mais na documentação do **SQLModel**, há um mini [tutorial sobre como usar SQLModel com **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/) mais longo. 🚀
================================================
FILE: docs/pt/docs/tutorial/static-files.md
================================================
# Arquivos Estáticos { #static-files }
Você pode servir arquivos estáticos automaticamente a partir de um diretório usando `StaticFiles`.
## Use `StaticFiles` { #use-staticfiles }
* Importe `StaticFiles`.
* "Monte" uma instância de `StaticFiles()` em um path específico.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Detalhes Técnicos
Você também pode usar `from starlette.staticfiles import StaticFiles`.
O **FastAPI** fornece o mesmo que `starlette.staticfiles` como `fastapi.staticfiles` apenas como uma conveniência para você, o desenvolvedor. Mas na verdade vem diretamente da Starlette.
///
### O que é "Montagem" { #what-is-mounting }
"Montagem" significa adicionar uma aplicação completamente "independente" em um path específico, que então cuida de lidar com todos os sub-paths.
Isso é diferente de usar um `APIRouter`, pois uma aplicação montada é completamente independente. A OpenAPI e a documentação da sua aplicação principal não incluirão nada da aplicação montada, etc.
Você pode ler mais sobre isso no [Guia Avançado do Usuário](../advanced/index.md).
## Detalhes { #details }
O primeiro `"/static"` refere-se ao sub-path no qual este "subaplicativo" será "montado". Assim, qualquer path que comece com `"/static"` será tratado por ele.
O `directory="static"` refere-se ao nome do diretório que contém seus arquivos estáticos.
O `name="static"` dá a ele um nome que pode ser usado internamente pelo **FastAPI**.
Todos esses parâmetros podem ser diferentes de "`static`", ajuste-os de acordo com as necessidades e detalhes específicos da sua própria aplicação.
## Mais informações { #more-info }
Para mais detalhes e opções, consulte [a documentação da Starlette sobre Arquivos Estáticos](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/pt/docs/tutorial/stream-json-lines.md
================================================
# Stream de JSON Lines { #stream-json-lines }
Você pode ter uma sequência de dados que deseja enviar em um "**Stream**"; é possível fazer isso com **JSON Lines**.
/// info | Informação
Adicionado no FastAPI 0.134.0.
///
## O que é um Stream? { #what-is-a-stream }
"**Streaming**" de dados significa que sua aplicação começará a enviar itens ao cliente sem esperar que toda a sequência esteja pronta.
Assim, ela envia o primeiro item, o cliente o recebe e começa a processá-lo, enquanto você ainda pode estar produzindo o próximo item.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Pode até ser um Stream infinito, em que você continua enviando dados.
## JSON Lines { #json-lines }
Nesses casos, é comum enviar "**JSON Lines**", um formato em que você envia um objeto JSON por linha.
Uma response teria um tipo de conteúdo `application/jsonl` (em vez de `application/json`) e o corpo seria algo como:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
É muito semelhante a um array JSON (equivalente a uma list do Python), mas em vez de estar envolto em `[]` e ter `,` entre os itens, há **um objeto JSON por linha**, separados por um caractere de nova linha.
/// info | Informação
O ponto importante é que sua aplicação poderá produzir cada linha em sequência, enquanto o cliente consome as anteriores.
///
/// note | Detalhes Técnicos
Como cada objeto JSON será separado por uma nova linha, eles não podem conter caracteres de nova linha literais em seu conteúdo, mas podem conter novas linhas com escape (`\n`), o que faz parte do padrão JSON.
Mas, normalmente, você não precisará se preocupar com isso, é feito automaticamente, continue lendo. 🤓
///
## Casos de uso { #use-cases }
Você pode usar isso para transmitir dados de um serviço de **IA LLM**, de **logs** ou **telemetria**, ou de outros tipos de dados que possam ser estruturados em itens **JSON**.
/// tip | Dica
Se você quiser transmitir dados binários, por exemplo vídeo ou áudio, confira o guia avançado: [Stream Data](../advanced/stream-data.md).
///
## Stream de JSON Lines com FastAPI { #stream-json-lines-with-fastapi }
Para transmitir JSON Lines com FastAPI, em vez de usar `return` na sua *função de operação de rota*, use `yield` para produzir cada item em sequência.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Se cada item JSON que você quer enviar de volta for do tipo `Item` (um modelo Pydantic) e a função for assíncrona, você pode declarar o tipo de retorno como `AsyncIterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Se você declarar o tipo de retorno, o FastAPI o usará para **validar** os dados, **documentá-los** no OpenAPI, **filtrá-los** e **serializá-los** usando o Pydantic.
/// tip | Dica
Como o Pydantic fará a serialização no lado em **Rust**, você terá uma **performance** muito maior do que se não declarar um tipo de retorno.
///
### Funções de operação de rota não assíncronas { #non-async-path-operation-functions }
Você também pode usar funções `def` normais (sem `async`) e usar `yield` da mesma forma.
O FastAPI garantirá que sejam executadas corretamente para não bloquear o event loop.
Como, neste caso, a função não é assíncrona, o tipo de retorno adequado seria `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Sem tipo de retorno { #no-return-type }
Você também pode omitir o tipo de retorno. O FastAPI então usará o [`jsonable_encoder`](./encoder.md) para converter os dados em algo que possa ser serializado para JSON e depois enviá-los como JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Eventos enviados pelo servidor (SSE) { #server-sent-events-sse }
O FastAPI também tem suporte de primeira classe a Server-Sent Events (SSE), que são bastante semelhantes, mas com alguns detalhes extras. Você pode aprender sobre eles no próximo capítulo: [Eventos enviados pelo servidor (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/pt/docs/tutorial/testing.md
================================================
# Testando { #testing }
Graças ao [Starlette](https://www.starlette.dev/testclient/), testar aplicações **FastAPI** é fácil e agradável.
Ele é baseado no [HTTPX](https://www.python-httpx.org), que por sua vez é projetado com base em Requests, por isso é muito familiar e intuitivo.
Com ele, você pode usar o [pytest](https://docs.pytest.org/) diretamente com **FastAPI**.
## Usando `TestClient` { #using-testclient }
/// info | Informação
Para usar o `TestClient`, primeiro instale [`httpx`](https://www.python-httpx.org).
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e instalá-lo, por exemplo:
```console
$ pip install httpx
```
///
Importe `TestClient`.
Crie um `TestClient` passando sua aplicação **FastAPI** para ele.
Crie funções com um nome que comece com `test_` (essa é a convenção padrão do `pytest`).
Use o objeto `TestClient` da mesma forma que você faz com `httpx`.
Escreva instruções `assert` simples com as expressões Python padrão que você precisa verificar (novamente, `pytest` padrão).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Dica
Observe que as funções de teste são `def` normais, não `async def`.
E as chamadas para o cliente também são chamadas normais, não usando `await`.
Isso permite que você use `pytest` diretamente sem complicações.
///
/// note | Detalhes Técnicos
Você também pode usar `from starlette.testclient import TestClient`.
**FastAPI** fornece o mesmo `starlette.testclient` que `fastapi.testclient` apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente da Starlette.
///
/// tip | Dica
Se você quiser chamar funções `async` em seus testes além de enviar solicitações à sua aplicação FastAPI (por exemplo, funções de banco de dados assíncronas), dê uma olhada em [Testes assíncronos](../advanced/async-tests.md) no tutorial avançado.
///
## Separando testes { #separating-tests }
Em uma aplicação real, você provavelmente teria seus testes em um arquivo diferente.
E sua aplicação **FastAPI** também pode ser composta de vários arquivos/módulos, etc.
### Arquivo da aplicação **FastAPI** { #fastapi-app-file }
Digamos que você tenha uma estrutura de arquivo conforme descrito em [Aplicações maiores](bigger-applications.md):
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
No arquivo `main.py` você tem sua aplicação **FastAPI**:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Arquivo de teste { #testing-file }
Então você poderia ter um arquivo `test_main.py` com seus testes. Ele poderia estar no mesmo pacote Python (o mesmo diretório com um arquivo `__init__.py`):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Como esse arquivo está no mesmo pacote, você pode usar importações relativas para importar o objeto `app` do módulo `main` (`main.py`):
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...e ter o código para os testes como antes.
## Testando: exemplo estendido { #testing-extended-example }
Agora vamos estender este exemplo e adicionar mais detalhes para ver como testar diferentes partes.
### Arquivo de aplicação **FastAPI** estendido { #extended-fastapi-app-file }
Vamos continuar com a mesma estrutura de arquivo de antes:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Digamos que agora o arquivo `main.py` com sua aplicação **FastAPI** tenha algumas outras **operações de rotas**.
Ele tem uma operação `GET` que pode retornar um erro.
Ele tem uma operação `POST` que pode retornar vários erros.
Ambas as *operações de rotas* requerem um cabeçalho `X-Token`.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Arquivo de teste estendido { #extended-testing-file }
Você pode então atualizar `test_main.py` com os testes estendidos:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Sempre que você precisar que o cliente passe informações na requisição e não souber como, você pode pesquisar (no Google) como fazer isso no `httpx`, ou até mesmo como fazer isso com `requests`, já que o design do HTTPX é baseado no design do Requests.
Depois é só fazer o mesmo nos seus testes.
Por exemplo:
* Para passar um parâmetro *path* ou *query*, adicione-o à própria URL.
* Para passar um corpo JSON, passe um objeto Python (por exemplo, um `dict`) para o parâmetro `json`.
* Se você precisar enviar *Dados de Formulário* em vez de JSON, use o parâmetro `data`.
* Para passar *headers*, use um `dict` no parâmetro `headers`.
* Para *cookies*, um `dict` no parâmetro `cookies`.
Para mais informações sobre como passar dados para o backend (usando `httpx` ou `TestClient`), consulte a [documentação do HTTPX](https://www.python-httpx.org).
/// info | Informação
Observe que o `TestClient` recebe dados que podem ser convertidos para JSON, não para modelos Pydantic.
Se você tiver um modelo Pydantic em seu teste e quiser enviar seus dados para o aplicativo durante o teste, poderá usar o `jsonable_encoder` descrito em [Codificador compatível com JSON](encoder.md).
///
## Execute-o { #run-it }
Depois disso, você só precisa instalar o `pytest`.
Certifique-se de criar um [ambiente virtual](../virtual-environments.md), ativá-lo e instalá-lo, por exemplo:
```console
$ pip install pytest
---> 100%
```
Ele detectará os arquivos e os testes automaticamente, os executará e informará os resultados para você.
Execute os testes com:
================================================
FILE: docs/pt/docs/virtual-environments.md
================================================
# Ambientes Virtuais { #virtual-environments }
Ao trabalhar em projetos Python, você provavelmente deveria usar um **ambiente virtual** (ou um mecanismo similar) para isolar os pacotes que você instala para cada projeto.
/// info | Informação
Se você já sabe sobre ambientes virtuais, como criá-los e usá-los, talvez seja melhor pular esta seção. 🤓
///
/// tip | Dica
Um **ambiente virtual** é diferente de uma **variável de ambiente**.
Uma **variável de ambiente** é uma variável no sistema que pode ser usada por programas.
Um **ambiente virtual** é um diretório com alguns arquivos.
///
/// info | Informação
Esta página lhe ensinará como usar **ambientes virtuais** e como eles funcionam.
Se você estiver pronto para adotar uma **ferramenta que gerencia tudo** para você (incluindo a instalação do Python), experimente [uv](https://github.com/astral-sh/uv).
///
## Criar um Projeto { #create-a-project }
Primeiro, crie um diretório para seu projeto.
O que normalmente faço é criar um diretório chamado `code` dentro do meu diretório home/user.
E dentro disso eu crio um diretório por projeto.
```console
// Vá para o diretório inicial
$ cd
// Crie um diretório para todos os seus projetos de código
$ mkdir code
// Entre nesse diretório de código
$ cd code
// Crie um diretório para este projeto
$ mkdir awesome-project
// Entre no diretório do projeto
$ cd awesome-project
```
## Crie um ambiente virtual { #create-a-virtual-environment }
Ao começar a trabalhar em um projeto Python **pela primeira vez**, crie um ambiente virtual **dentro do seu projeto**.
/// tip | Dica
Você só precisa fazer isso **uma vez por projeto**, não toda vez que trabalhar.
///
//// tab | `venv`
Para criar um ambiente virtual, você pode usar o módulo `venv` que vem com o Python.
```console
$ python -m venv .venv
```
/// details | O que esse comando significa
* `python`: usa o programa chamado `python`
* `-m`: chama um módulo como um script, nós diremos a ele qual módulo vem em seguida
* `venv`: usa o módulo chamado `venv` que normalmente vem instalado com o Python
* `.venv`: cria o ambiente virtual no novo diretório `.venv`
///
////
//// tab | `uv`
Se você tiver [`uv`](https://github.com/astral-sh/uv) instalado, poderá usá-lo para criar um ambiente virtual.
```console
$ uv venv
```
/// tip | Dica
Por padrão, `uv` criará um ambiente virtual em um diretório chamado `.venv`.
Mas você pode personalizá-lo passando um argumento adicional com o nome do diretório.
///
////
Esse comando cria um novo ambiente virtual em um diretório chamado `.venv`.
/// details | `.venv` ou outro nome
Você pode criar o ambiente virtual em um diretório diferente, mas há uma convenção para chamá-lo de `.venv`.
///
## Ative o ambiente virtual { #activate-the-virtual-environment }
Ative o novo ambiente virtual para que qualquer comando Python que você executar ou pacote que você instalar o utilize.
/// tip | Dica
Faça isso **toda vez** que iniciar uma **nova sessão de terminal** para trabalhar no projeto.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ou se você usa o Bash para Windows (por exemplo, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | Dica
Toda vez que você instalar um **novo pacote** naquele ambiente, **ative** o ambiente novamente.
Isso garante que, se você usar um **programa de terminal (CLI)** instalado por esse pacote, você usará aquele do seu ambiente virtual e não qualquer outro que possa ser instalado globalmente, provavelmente com uma versão diferente do que você precisa.
///
## Verifique se o ambiente virtual está ativo { #check-the-virtual-environment-is-active }
Verifique se o ambiente virtual está ativo (o comando anterior funcionou).
/// tip | Dica
Isso é **opcional**, mas é uma boa maneira de **verificar** se tudo está funcionando conforme o esperado e se você está usando o ambiente virtual pretendido.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Se ele mostrar o binário `python` em `.venv/bin/python`, dentro do seu projeto (neste caso `awesome-project`), então funcionou. 🎉
////
//// tab | Windows PowerShell
Se ele mostrar o binário `python` em `.venv\Scripts\python`, dentro do seu projeto (neste caso `awesome-project`), então funcionou. 🎉
////
## Atualizar `pip` { #upgrade-pip }
/// tip | Dica
Se você usar [`uv`](https://github.com/astral-sh/uv), você o usará para instalar coisas em vez do `pip`, então não precisará atualizar o `pip`. 😎
///
Se você estiver usando `pip` para instalar pacotes (ele vem por padrão com o Python), você deveria **atualizá-lo** para a versão mais recente.
Muitos erros exóticos durante a instalação de um pacote são resolvidos apenas atualizando o `pip` primeiro.
/// tip | Dica
Normalmente, você faria isso **uma vez**, logo após criar o ambiente virtual.
///
Certifique-se de que o ambiente virtual esteja ativo (com o comando acima) e execute:
/// tip | Dica
Às vezes, você pode receber um erro **`No module named pip`** ao tentar atualizar o pip.
Se isso acontecer, instale e atualize o pip usando o comando abaixo:
Esse comando instalará o pip caso ele ainda não esteja instalado e também garante que a versão instalada do pip seja pelo menos tão recente quanto a disponível em `ensurepip`.
///
## Adicionar `.gitignore` { #add-gitignore }
Se você estiver usando **Git** (você deveria), adicione um arquivo `.gitignore` para excluir tudo em seu `.venv` do Git.
/// tip | Dica
Se você usou [`uv`](https://github.com/astral-sh/uv) para criar o ambiente virtual, ele já fez isso para você, você pode pular esta etapa. 😎
///
/// tip | Dica
Faça isso **uma vez**, logo após criar o ambiente virtual.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | O que esse comando significa
* `echo "*"`: irá "imprimir" o texto `*` no terminal (a próxima parte muda isso um pouco)
* `>`: qualquer coisa impressa no terminal pelo comando à esquerda de `>` não deve ser impressa, mas sim escrita no arquivo que vai à direita de `>`
* `.gitignore`: o nome do arquivo onde o texto deve ser escrito
E `*` para Git significa "tudo". Então, ele ignorará tudo no diretório `.venv`.
Esse comando criará um arquivo `.gitignore` com o conteúdo:
```gitignore
*
```
///
## Instalar Pacotes { #install-packages }
Após ativar o ambiente, você pode instalar pacotes nele.
/// tip | Dica
Faça isso **uma vez** ao instalar ou atualizar os pacotes que seu projeto precisa.
Se precisar atualizar uma versão ou adicionar um novo pacote, você **fará isso novamente**.
///
### Instalar pacotes diretamente { #install-packages-directly }
Se estiver com pressa e não quiser usar um arquivo para declarar os requisitos de pacote do seu projeto, você pode instalá-los diretamente.
/// tip | Dica
É uma (muito) boa ideia colocar os pacotes e versões que seu programa precisa em um arquivo (por exemplo `requirements.txt` ou `pyproject.toml`).
///
//// tab | `pip`
////
### Instalar a partir de `requirements.txt` { #install-from-requirements-txt }
Se você tiver um `requirements.txt`, agora poderá usá-lo para instalar seus pacotes.
//// tab | `pip`
////
/// details | `requirements.txt`
Um `requirements.txt` com alguns pacotes poderia se parecer com:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Execute seu programa { #run-your-program }
Depois de ativar o ambiente virtual, você pode executar seu programa, e ele usará o Python dentro do seu ambiente virtual com os pacotes que você instalou lá.
```console
$ python main.py
Hello World
```
## Configure seu editor { #configure-your-editor }
Você provavelmente usaria um editor. Certifique-se de configurá-lo para usar o mesmo ambiente virtual que você criou (ele provavelmente o detectará automaticamente) para que você possa obter preenchimento automático e erros em linha.
Por exemplo:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Dica
Normalmente, você só precisa fazer isso **uma vez**, ao criar o ambiente virtual.
///
## Desativar o ambiente virtual { #deactivate-the-virtual-environment }
Quando terminar de trabalhar no seu projeto, você pode **desativar** o ambiente virtual.
```console
$ deactivate
```
Dessa forma, quando você executar `python`, ele não tentará executá-lo naquele ambiente virtual com os pacotes instalados nele.
## Pronto para trabalhar { #ready-to-work }
Agora você está pronto para começar a trabalhar no seu projeto.
/// tip | Dica
Você quer entender o que é tudo isso acima?
Continue lendo. 👇🤓
///
## Por que ambientes virtuais { #why-virtual-environments }
Para trabalhar com o FastAPI, você precisa instalar o [Python](https://www.python.org/).
Depois disso, você precisará **instalar** o FastAPI e quaisquer outros **pacotes** que queira usar.
Para instalar pacotes, você normalmente usaria o comando `pip` que vem com o Python (ou alternativas semelhantes).
No entanto, se você usar `pip` diretamente, os pacotes serão instalados no seu **ambiente Python global** (a instalação global do Python).
### O Problema { #the-problem }
Então, qual é o problema em instalar pacotes no ambiente global do Python?
Em algum momento, você provavelmente acabará escrevendo muitos programas diferentes que dependem de **pacotes diferentes**. E alguns desses projetos em que você trabalha dependerão de **versões diferentes** do mesmo pacote. 😱
Por exemplo, você pode criar um projeto chamado `philosophers-stone`, este programa depende de outro pacote chamado **`harry`, usando a versão `1`**. Então, você precisa instalar `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Então, em algum momento depois, você cria outro projeto chamado `prisoner-of-azkaban`, e esse projeto também depende de `harry`, mas esse projeto precisa do **`harry` versão `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Mas agora o problema é que, se você instalar os pacotes globalmente (no ambiente global) em vez de em um **ambiente virtual** local, você terá que escolher qual versão do `harry` instalar.
Se você quiser executar `philosophers-stone`, precisará primeiro instalar `harry` versão `1`, por exemplo com:
```console
$ pip install "harry==1"
```
E então você acabaria com `harry` versão `1` instalado em seu ambiente Python global.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Mas se você quiser executar `prisoner-of-azkaban`, você precisará desinstalar `harry` versão `1` e instalar `harry` versão `3` (ou apenas instalar a versão `3` desinstalaria automaticamente a versão `1`).
```console
$ pip install "harry==3"
```
E então você acabaria com `harry` versão `3` instalado em seu ambiente Python global.
E se você tentar executar `philosophers-stone` novamente, há uma chance de que **não funcione** porque ele precisa de `harry` versão `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Dica
É muito comum em pacotes Python tentar ao máximo **evitar alterações drásticas** em **novas versões**, mas é melhor prevenir do que remediar e instalar versões mais recentes intencionalmente e, quando possível, executar os testes para verificar se tudo está funcionando corretamente.
///
Agora, imagine isso com **muitos** outros **pacotes** dos quais todos os seus **projetos dependem**. Isso é muito difícil de gerenciar. E você provavelmente acabaria executando alguns projetos com algumas **versões incompatíveis** dos pacotes, e não saberia por que algo não está funcionando.
Além disso, dependendo do seu sistema operacional (por exemplo, Linux, Windows, macOS), ele pode ter vindo com o Python já instalado. E, nesse caso, provavelmente tinha alguns pacotes pré-instalados com algumas versões específicas **necessárias para o seu sistema**. Se você instalar pacotes no ambiente global do Python, poderá acabar **quebrando** alguns dos programas que vieram com seu sistema operacional.
## Onde os pacotes são instalados { #where-are-packages-installed }
Quando você instala o Python, ele cria alguns diretórios com alguns arquivos no seu computador.
Alguns desses diretórios são os responsáveis por ter todos os pacotes que você instala.
Quando você executa:
```console
// Não execute isso agora, é apenas um exemplo 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Isso fará o download de um arquivo compactado com o código FastAPI, normalmente do [PyPI](https://pypi.org/project/fastapi/).
Ele também fará o **download** de arquivos para outros pacotes dos quais o FastAPI depende.
Em seguida, ele **extrairá** todos esses arquivos e os colocará em um diretório no seu computador.
Por padrão, ele colocará os arquivos baixados e extraídos no diretório que vem com a instalação do Python, que é o **ambiente global**.
## O que são ambientes virtuais { #what-are-virtual-environments }
A solução para os problemas de ter todos os pacotes no ambiente global é usar um **ambiente virtual para cada projeto** em que você trabalha.
Um ambiente virtual é um **diretório**, muito semelhante ao global, onde você pode instalar os pacotes para um projeto.
Dessa forma, cada projeto terá seu próprio ambiente virtual (diretório `.venv`) com seus próprios pacotes.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## O que significa ativar um ambiente virtual { #what-does-activating-a-virtual-environment-mean }
Quando você ativa um ambiente virtual, por exemplo com:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ou se você usa o Bash para Windows (por exemplo, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Esse comando criará ou modificará algumas [variáveis de ambiente](environment-variables.md) que estarão disponíveis para os próximos comandos.
Uma dessas variáveis é a variável `PATH`.
/// tip | Dica
Você pode aprender mais sobre a variável de ambiente `PATH` na seção [Variáveis de ambiente](environment-variables.md#path-environment-variable).
///
A ativação de um ambiente virtual adiciona seu caminho `.venv/bin` (no Linux e macOS) ou `.venv\Scripts` (no Windows) à variável de ambiente `PATH`.
Digamos que antes de ativar o ambiente, a variável `PATH` estava assim:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Isso significa que o sistema procuraria programas em:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Isso significa que o sistema procuraria programas em:
* `C:\Windows\System32`
////
Após ativar o ambiente virtual, a variável `PATH` ficaria mais ou menos assim:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Isso significa que o sistema agora começará a procurar primeiro por programas em:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
antes de procurar nos outros diretórios.
Então, quando você digita `python` no terminal, o sistema encontrará o programa Python em
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
e usa esse.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Isso significa que o sistema agora começará a procurar primeiro por programas em:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
antes de procurar nos outros diretórios.
Então, quando você digita `python` no terminal, o sistema encontrará o programa Python em
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
e usa esse.
////
Um detalhe importante é que ele colocará o caminho do ambiente virtual no **início** da variável `PATH`. O sistema o encontrará **antes** de encontrar qualquer outro Python disponível. Dessa forma, quando você executar `python`, ele usará o Python **do ambiente virtual** em vez de qualquer outro `python` (por exemplo, um `python` de um ambiente global).
Ativar um ambiente virtual também muda algumas outras coisas, mas esta é uma das mais importantes.
## Verificando um ambiente virtual { #checking-a-virtual-environment }
Ao verificar se um ambiente virtual está ativo, por exemplo com:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Isso significa que o programa `python` que será usado é aquele **no ambiente virtual**.
você usa `which` no Linux e macOS e `Get-Command` no Windows PowerShell.
A maneira como esse comando funciona é que ele vai e verifica na variável de ambiente `PATH`, passando por **cada caminho em ordem**, procurando pelo programa chamado `python`. Uma vez que ele o encontre, ele **mostrará o caminho** para esse programa.
A parte mais importante é que quando você chama `python`, esse é exatamente o "`python`" que será executado.
Assim, você pode confirmar se está no ambiente virtual correto.
/// tip | Dica
É fácil ativar um ambiente virtual, obter um Python e então **ir para outro projeto**.
E o segundo projeto **não funcionaria** porque você está usando o **Python incorreto**, de um ambiente virtual para outro projeto.
É útil poder verificar qual `python` está sendo usado. 🤓
///
## Por que desativar um ambiente virtual { #why-deactivate-a-virtual-environment }
Por exemplo, você pode estar trabalhando em um projeto `philosophers-stone`, **ativar esse ambiente virtual**, instalar pacotes e trabalhar com esse ambiente.
E então você quer trabalhar em **outro projeto** `prisoner-of-azkaban`.
Você vai para aquele projeto:
```console
$ cd ~/code/prisoner-of-azkaban
```
Se você não desativar o ambiente virtual para `philosophers-stone`, quando você executar `python` no terminal, ele tentará usar o Python de `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Erro ao importar o Sirius, ele não está instalado 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Mas se você desativar o ambiente virtual e ativar o novo para `prisoner-of-askaban`, quando você executar `python`, ele usará o Python do ambiente virtual em `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// Você não precisa estar no diretório antigo para desativar, você pode fazer isso de onde estiver, mesmo depois de ir para o outro projeto 😎
$ deactivate
// Ative o ambiente virtual em prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Agora, quando você executar o python, ele encontrará o pacote sirius instalado neste ambiente virtual ✨
$ python main.py
I solemnly swear 🐺
```
## Alternativas { #alternatives }
Este é um guia simples para você começar e lhe ensinar como tudo funciona **por baixo**.
Existem muitas **alternativas** para gerenciar ambientes virtuais, dependências de pacotes (requisitos) e projetos.
Quando estiver pronto e quiser usar uma ferramenta para **gerenciar todo o projeto**, dependências de pacotes, ambientes virtuais, etc., sugiro que você experimente o [uv](https://github.com/astral-sh/uv).
`uv` pode fazer muitas coisas, ele pode:
* **Instalar o Python** para você, incluindo versões diferentes
* Gerenciar o **ambiente virtual** para seus projetos
* Instalar **pacotes**
* Gerenciar **dependências e versões** de pacotes para seu projeto
* Certificar-se de que você tenha um conjunto **exato** de pacotes e versões para instalar, incluindo suas dependências, para que você possa ter certeza de que pode executar seu projeto em produção exatamente da mesma forma que em seu computador durante o desenvolvimento, isso é chamado de **bloqueio**
* E muitas outras coisas
## Conclusão { #conclusion }
Se você leu e entendeu tudo isso, agora **você sabe muito mais** sobre ambientes virtuais do que muitos desenvolvedores por aí. 🤓
Saber esses detalhes provavelmente será útil no futuro, quando você estiver depurando algo que parece complexo, mas você saberá **como tudo funciona**. 😎
================================================
FILE: docs/pt/llm-prompt.md
================================================
### Target language
Translate to Portuguese (Português).
Language code: pt.
For instructions or titles in imperative, keep them in imperative, for example "Import FastAPI" to "Importe o FastAPI".
Keep existing translations as they are if the term is already translated.
When translating documentation into Portuguese, use neutral and widely understandable language. Although Portuguese originated in Portugal and has its largest number of speakers in Brazil, it is also an official language in several countries and regions, such as Equatorial Guinea, Mozambique, Angola, Cape Verde, and São Tomé and Príncipe. Avoid words or expressions that are specific to a single country or region.
Only keep parentheses if they exist in the source text. Do not add parentheses to terms that do not have them.
### Avoiding Repetition in Translation
When translating sentences, avoid unnecessary repetition of words or phrases that are implied in context.
- Merge repeated words naturally while keeping the meaning.
- Do **not** introduce extra words to replace repeated phrases unnecessarily.
- Keep translations fluent and concise, but maintain the original meaning.
**Example:**
Source:
Let's see how that works and how to change it if you need to do that.
Avoid translating literally as:
Vamos ver como isso funciona e como alterar isso se você precisar fazer isso.
Better translation:
Vamos ver como isso funciona e como alterar se você precisar.
---
For the next terms, use the following translations:
* /// check: /// check | Verifique
* /// danger: /// danger | Cuidado
* /// info: /// info | Informação
* /// note | Technical Details: /// note | Detalhes Técnicos
* /// info | Very Technical Details: /// note | Detalhes Técnicos Avançados
* /// note: /// note | Nota
* /// tip: /// tip | Dica
* /// warning: /// warning | Atenção
* you should: você deveria
* async context manager: gerenciador de contexto assíncrono
* autocomplete: autocompletar
* autocompletion: preenchimento automático
* auto-completion: preenchimento automático
* bug: bug
* context manager: gerenciador de contexto
* cross domain: cross domain (do not translate to "domínio cruzado")
* cross origin: cross origin (do not translate to "origem cruzada")
* Cross-Origin Resource Sharing: Cross-Origin Resource Sharing (do not translate to "Compartilhamento de Recursos de Origem Cruzada")
* Deep Learning: Deep Learning (do not translate to "Aprendizado Profundo")
* dependable: dependable
* dependencies: dependências
* deprecated: descontinuado
* docs: documentação
* FastAPI app: aplicação FastAPI
* framework: framework (do not translate)
* feature: funcionalidade
* guides: tutoriais
* I/O (as in "input and output"): I/O (do not translate to "E/S")
* JSON Schema: JSON Schema
* library: biblioteca
* lifespan: lifespan (do not translate to "vida útil")
* list (as in Python list): list
* Machine Learning: Aprendizado de Máquina
* media type: media type (do not translate to "tipo de mídia")
* non-Annotated: non-Annotated (do not translate non-Annotated when it comes after a Python version.e.g., “Python 3.10+ non-Annotated”)
* operation IDs: IDs de operação
* path (as in URL path): path
* path operation: operação de rota
* path operation function: função de operação de rota
* prefix: prefixo
* request (as in HTTP request): request (do not change if it's already translated to requisição)
* router (as in FastAPI's router): router (do not change if it's already translated to "roteador" or "roteadores")
* response (as in HTTP response): response (do not change if it's already translated to resposta)
* shutdown (of the app): encerramento
* shutdown event (of the app): evento de encerramento
* startup (as in the event of the app): inicialização
* startup event (as in the event of the app): evento de inicialização
* Stream: Stream
* string: string
* type hints: anotações de tipo
* wildcards: curingas
================================================
FILE: docs/pt/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/ru/docs/_llm-test.md
================================================
# Тестовый файл LLM { #llm-test-file }
Этот документ проверяет, понимает ли LLM, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`.
Тесты, добавленные здесь, увидят все создатели языковых специфичных промптов.
Использование:
* Подготовьте языковой специфичный промпт — `docs/{language code}/llm-prompt.md`.
* Выполните новый перевод этого документа на нужный целевой язык (см., например, команду `translate-page` в `translate.py`). Это создаст перевод в `docs/{language code}/docs/_llm-test.md`.
* Проверьте, всё ли в порядке в переводе.
* При необходимости улучшите ваш языковой специфичный промпт, общий промпт или английский документ.
* Затем вручную исправьте оставшиеся проблемы в переводе, чтобы он был хорошим.
* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт настолько хороши, насколько это возможно (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что [LLM — недетерминированные алгоритмы](https://doublespeak.chat/#/handbook#deterministic-output)).
Тесты:
## Фрагменты кода { #code-snippets }
//// tab | Тест
Это фрагмент кода: `foo`. А это ещё один фрагмент кода: `bar`. И ещё один: `baz quux`.
////
//// tab | Информация
Содержимое фрагментов кода должно оставаться как есть.
См. раздел `### Content of code snippets` в общем промпте в `scripts/translate.py`.
////
## Кавычки { #quotes }
//// tab | Тест
Вчера мой друг написал: "Если вы написали incorrectly правильно, значит вы написали это неправильно". На что я ответил: "Верно, но 'incorrectly' — это неправильно, а не '"incorrectly"'".
/// note | Примечание
LLM, вероятно, переведёт это неправильно. Интересно лишь то, сохранит ли она фиксированный перевод при повторном переводе.
///
////
//// tab | Информация
Автор промпта может выбрать, хочет ли он преобразовывать нейтральные кавычки в типографские. Допускается оставить их как есть.
См., например, раздел `### Quotes` в `docs/de/llm-prompt.md`.
////
## Кавычки во фрагментах кода { #quotes-in-code-snippets }
//// tab | Тест
`pip install "foo[bar]"`
Примеры строковых литералов во фрагментах кода: `"this"`, `'that'`.
Сложный пример строковых литералов во фрагментах кода: `f"I like {'oranges' if orange else "apples"}"`
Хардкор: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Информация
... Однако кавычки внутри фрагментов кода должны оставаться как есть.
////
## Блоки кода { #code-blocks }
//// tab | Тест
Пример кода Bash...
```bash
# Вывести приветствие вселенной
echo "Hello universe"
```
...и пример вывода в консоли...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...и ещё один пример вывода в консоли...
```console
// Создать директорию "Code"
$ mkdir code
// Перейти в эту директорию
$ cd code
```
...и пример кода на Python...
```Python
wont_work() # Это не сработает 😱
works(foo="bar") # Это работает 🎉
```
...и на этом всё.
////
//// tab | Информация
Код в блоках кода не должен изменяться, за исключением комментариев.
См. раздел `### Content of code blocks` в общем промпте в `scripts/translate.py`.
////
## Вкладки и цветные блоки { #tabs-and-colored-boxes }
//// tab | Тест
/// info | Информация
Некоторый текст
///
/// note | Примечание
Некоторый текст
///
/// note | Технические подробности
Некоторый текст
///
/// check | Проверка
Некоторый текст
///
/// tip | Совет
Некоторый текст
///
/// warning | Предупреждение
Некоторый текст
///
/// danger | Опасность
Некоторый текст
///
////
//// tab | Информация
Для вкладок и блоков `Info`/`Note`/`Warning`/и т.п. нужно добавить перевод их заголовка после вертикальной черты (`|`).
См. разделы `### Special blocks` и `### Tab blocks` в общем промпте в `scripts/translate.py`.
////
## Веб- и внутренние ссылки { #web-and-internal-links }
//// tab | Тест
Текст ссылок должен переводиться, адрес ссылки не должен изменяться:
* [Ссылка на заголовок выше](#code-snippets)
* [Внутренняя ссылка](index.md#installation)
* [Внешняя ссылка](https://sqlmodel.tiangolo.com/)
* [Ссылка на стиль](https://fastapi.tiangolo.com/css/styles.css)
* [Ссылка на скрипт](https://fastapi.tiangolo.com/js/logic.js)
* [Ссылка на изображение](https://fastapi.tiangolo.com/img/foo.jpg)
Текст ссылок должен переводиться, адрес ссылки должен указывать на перевод:
* [Ссылка на FastAPI](https://fastapi.tiangolo.com/ru/)
////
//// tab | Информация
Ссылки должны переводиться, но их адреса не должны изменяться. Исключение — абсолютные ссылки на страницы документации FastAPI. В этом случае ссылка должна вести на перевод.
См. раздел `### Links` в общем промпте в `scripts/translate.py`.
////
## HTML-элементы "abbr" { #html-abbr-elements }
//// tab | Тест
Вот некоторые элементы, обёрнутые в HTML-элементы "abbr" (часть выдумана):
### abbr даёт полную расшифровку { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Информация
Атрибуты "title" элементов "abbr" переводятся по определённым правилам.
Переводы могут добавлять свои собственные элементы "abbr", которые LLM не должна удалять. Например, чтобы объяснить английские слова.
См. раздел `### HTML abbr elements` в общем промпте в `scripts/translate.py`.
////
## HTML-элементы "dfn" { #html-dfn-elements }
* кластер
* Глубокое обучение
## Заголовки { #headings }
//// tab | Тест
### Разработка веб‑приложения — руководство { #develop-a-webapp-a-tutorial }
Привет.
### Аннотации типов и -аннотации { #type-hints-and-annotations }
Снова привет.
### Супер- и подклассы { #super-and-subclasses }
Снова привет.
////
//// tab | Информация
Единственное жёсткое правило для заголовков — LLM должна оставить часть хеша в фигурных скобках без изменений, чтобы ссылки не ломались.
См. раздел `### Headings` в общем промпте в `scripts/translate.py`.
Для некоторых языковых инструкций см., например, раздел `### Headings` в `docs/de/llm-prompt.md`.
////
## Термины, используемые в документации { #terms-used-in-the-docs }
//// tab | Тест
* вы
* ваш
* например
* и т.д.
* `foo` как `int`
* `bar` как `str`
* `baz` как `list`
* Учебник — Руководство пользователя
* Расширенное руководство пользователя
* Документация по SQLModel
* Документация API
* Автоматическая документация
* Наука о данных
* Глубокое обучение
* Машинное обучение
* Внедрение зависимостей
* Аутентификация HTTP Basic
* HTTP Digest
* формат ISO
* стандарт JSON Schema
* JSON-схема
* определение схемы
* password flow
* Мобильный
* устаревший
* спроектированный
* некорректный
* на лету
* стандарт
* по умолчанию
* чувствительный к регистру
* нечувствительный к регистру
* отдавать приложение
* отдавать страницу
* приложение
* приложение
* HTTP-запрос
* HTTP-ответ
* ответ с ошибкой
* операция пути
* декоратор операции пути
* функция-обработчик пути
* тело
* тело запроса
* тело ответа
* JSON-тело
* тело формы
* тело файла
* тело функции
* параметр
* body-параметр
* path-параметр
* query-параметр
* cookie-параметр
* параметр заголовка
* параметр формы
* параметр функции
* событие
* событие запуска
* запуск сервера
* событие остановки
* событие lifespan
* обработчик
* обработчик события
* обработчик исключений
* обрабатывать
* модель
* Pydantic-модель
* модель данных
* модель базы данных
* модель формы
* объект модели
* класс
* базовый класс
* родительский класс
* подкласс
* дочерний класс
* родственный класс
* метод класса
* заголовок
* HTTP-заголовки
* заголовок авторизации
* заголовок `Authorization`
* заголовок `Forwarded`
* система внедрения зависимостей
* зависимость
* зависимый объект
* зависимый
* ограниченный вводом/выводом
* ограниченный процессором
* конкурентность
* параллелизм
* многопроцессность
* переменная окружения
* переменная окружения
* `PATH`
* переменная `PATH`
* аутентификация
* провайдер аутентификации
* авторизация
* форма авторизации
* провайдер авторизации
* пользователь аутентифицируется
* система аутентифицирует пользователя
* CLI
* интерфейс командной строки
* сервер
* клиент
* облачный провайдер
* облачный сервис
* разработка
* этапы разработки
* dict
* словарь
* перечисление
* enum
* член перечисления
* кодировщик
* декодировщик
* кодировать
* декодировать
* исключение
* вызвать
* выражение
* оператор
* фронтенд
* бэкенд
* обсуждение на GitHub
* Issue на GitHub (тикет/обращение)
* производительность
* оптимизация производительности
* тип возвращаемого значения
* возвращаемое значение
* безопасность
* схема безопасности
* задача
* фоновая задача
* функция задачи
* шаблон
* шаблонизатор
* аннотация типов
* аннотация типов
* воркер сервера
* воркер Uvicorn
* воркер Gunicorn
* воркер-процесс
* класс воркера
* рабочая нагрузка
* деплой
* развернуть
* SDK
* набор средств разработки ПО
* `APIRouter`
* `requirements.txt`
* токен Bearer
* несовместимое изменение
* баг
* кнопка
* вызываемый объект
* код
* коммит
* менеджер контекста
* корутина
* сессия базы данных
* диск
* домен
* движок
* фиктивный X
* метод HTTP GET
* элемент
* библиотека
* lifespan
* блокировка
* middleware (Промежуточный слой)
* мобильное приложение
* модуль
* монтирование
* сеть
* origin (источник)
* переопределение
* полезная нагрузка
* процессор
* свойство
* прокси
* пулл-реквест (запрос на изменение)
* запрос
* ОЗУ
* удалённая машина
* статус-код
* строка
* тег
* веб‑фреймворк
* подстановочный знак
* вернуть
* валидировать
////
//// tab | Информация
Это неполный и ненормативный список (в основном) технических терминов, встречающихся в документации. Он может помочь автору промпта понять, по каким терминам LLM нужна подсказка. Например, когда она продолжает возвращать действительно хороший перевод к неоптимальному. Или когда у неё возникают проблемы со склонением/спряжением термина на вашем языке.
См., например, раздел `### List of English terms and their preferred German translations` в `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/ru/docs/about/index.md
================================================
# О проекте { #about }
О FastAPI, его дизайне, источниках вдохновения и многом другом. 🤓
================================================
FILE: docs/ru/docs/advanced/additional-responses.md
================================================
# Дополнительные ответы в OpenAPI { #additional-responses-in-openapi }
/// warning | Предупреждение
Это довольно продвинутая тема.
Если вы только начинаете работать с **FastAPI**, возможно, вам это пока не нужно.
///
Вы можете объявлять дополнительные ответы с дополнительными статус-кодами, типами содержимого, описаниями и т.д.
Эти дополнительные ответы будут включены в схему OpenAPI, и поэтому появятся в документации API.
Но для таких дополнительных ответов убедитесь, что вы возвращаете `Response`, например `JSONResponse`, напрямую, со своим статус-кодом и содержимым.
## Дополнительный ответ с `model` { #additional-response-with-model }
Вы можете передать вашим декораторам операции пути параметр `responses`.
Он принимает `dict`: ключи — это статус-коды для каждого ответа (например, `200`), а значения — другие `dict` с информацией для каждого из них.
Каждый из этих `dict` для ответа может иметь ключ `model`, содержащий Pydantic-модель, аналогично `response_model`.
**FastAPI** возьмёт эту модель, сгенерирует для неё JSON‑схему и включит её в нужное место в OpenAPI.
Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Примечание
Имейте в виду, что необходимо возвращать `JSONResponse` напрямую.
///
/// info | Информация
Ключ `model` не является частью OpenAPI.
**FastAPI** возьмёт Pydantic-модель оттуда, сгенерирует JSON‑схему и поместит её в нужное место.
Нужное место:
* В ключе `content`, значением которого является другой JSON‑объект (`dict`), содержащий:
* Ключ с типом содержимого, например `application/json`, значением которого является другой JSON‑объект, содержащий:
* Ключ `schema`, значением которого является JSON‑схема из модели — вот нужное место.
* **FastAPI** добавляет здесь ссылку на глобальные JSON‑схемы в другом месте вашего OpenAPI вместо того, чтобы включать схему напрямую. Так другие приложения и клиенты смогут использовать эти JSON‑схемы напрямую, предоставлять лучшие инструменты генерации кода и т.д.
///
Сгенерированные в OpenAPI ответы для этой операции пути будут такими:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Схемы даны как ссылки на другое место внутри схемы OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Дополнительные типы содержимого для основного ответа { #additional-media-types-for-the-main-response }
Вы можете использовать этот же параметр `responses`, чтобы добавить разные типы содержимого для того же основного ответа.
Например, вы можете добавить дополнительный тип содержимого `image/png`, объявив, что ваша операция пути может возвращать JSON‑объект (с типом содержимого `application/json`) или PNG‑изображение:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Примечание
Учтите, что изображение нужно возвращать напрямую, используя `FileResponse`.
///
/// info | Информация
Если вы явно не укажете другой тип содержимого в параметре `responses`, FastAPI будет считать, что ответ имеет тот же тип содержимого, что и основной класс ответа (по умолчанию `application/json`).
Но если вы указали пользовательский класс ответа с `None` в качестве его типа содержимого, FastAPI использует `application/json` для любого дополнительного ответа, у которого есть связанная модель.
///
## Комбинирование информации { #combining-information }
Вы также можете комбинировать информацию об ответах из нескольких мест, включая параметры `response_model`, `status_code` и `responses`.
Вы можете объявить `response_model`, используя статус-код по умолчанию `200` (или свой, если нужно), а затем объявить дополнительную информацию для этого же ответа в `responses`, напрямую в схеме OpenAPI.
**FastAPI** сохранит дополнительную информацию из `responses` и объединит её с JSON‑схемой из вашей модели.
Например, вы можете объявить ответ со статус-кодом `404`, который использует Pydantic-модель и имеет пользовательское `description`.
А также ответ со статус-кодом `200`, который использует ваш `response_model`, но включает пользовательский `example`:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Всё это будет объединено и включено в ваш OpenAPI и отображено в документации API:
## Комбинирование предопределённых и пользовательских ответов { #combine-predefined-responses-and-custom-ones }
Возможно, вы хотите иметь некоторые предопределённые ответы, применимые ко многим операциям пути, но при этом комбинировать их с пользовательскими ответами, необходимыми для каждой конкретной операции пути.
В таких случаях вы можете использовать приём Python «распаковки» `dict` с помощью `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Здесь `new_dict` будет содержать все пары ключ-значение из `old_dict` плюс новую пару ключ-значение:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Вы можете использовать этот приём, чтобы переиспользовать некоторые предопределённые ответы в ваших операциях пути и комбинировать их с дополнительными пользовательскими.
Например:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Дополнительная информация об ответах OpenAPI { #more-information-about-openapi-responses }
Чтобы увидеть, что именно можно включать в ответы, посмотрите эти разделы спецификации OpenAPI:
* [Объект Responses OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), он включает `Response Object`.
* [Объект Response OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), вы можете включить всё из этого объекта напрямую в каждый ответ внутри вашего параметра `responses`. Включая `description`, `headers`, `content` (внутри него вы объявляете разные типы содержимого и JSON‑схемы) и `links`.
================================================
FILE: docs/ru/docs/advanced/additional-status-codes.md
================================================
# Дополнительные статус-коды { #additional-status-codes }
По умолчанию **FastAPI** будет возвращать ответы, используя `JSONResponse`, помещая содержимое, которое вы возвращаете из вашей *операции пути*, внутрь этого `JSONResponse`.
Он будет использовать статус-код по умолчанию или тот, который вы укажете в вашей *операции пути*.
## Дополнительные статус-коды { #additional-status-codes_1 }
Если вы хотите возвращать дополнительные статус-коды помимо основного, вы можете сделать это, возвращая `Response` напрямую, например `JSONResponse`, и устанавливая дополнительный статус-код напрямую.
Например, предположим, что вы хотите иметь *операцию пути*, которая позволяет обновлять элементы и возвращает HTTP статус-код 200 «OK» при успешном выполнении.
Но вы также хотите, чтобы она принимала новые элементы. И если элементы ранее не существовали, она создаёт их и возвращает HTTP статус-код 201 «Created».
Чтобы добиться этого, импортируйте `JSONResponse` и верните туда свой контент напрямую, установив нужный вам `status_code`:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Внимание
Когда вы возвращаете `Response` напрямую, как в примере выше, он будет возвращён как есть.
Он не будет сериализован с помощью модели и т.п.
Убедитесь, что в нём именно те данные, которые вы хотите, и что значения являются валидным JSON (если вы используете `JSONResponse`).
///
/// note | Технические детали
Вы также можете использовать `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет тот же `starlette.responses` через `fastapi.responses` просто для вашего удобства как разработчика. Но большинство доступных Response-классов приходят напрямую из Starlette. То же самое со `status`.
///
## OpenAPI и документация API { #openapi-and-api-docs }
Если вы возвращаете дополнительные статус-коды и ответы напрямую, они не будут включены в схему OpenAPI (документацию API), потому что у FastAPI нет способа заранее знать, что вы собираетесь вернуть.
Но вы можете задокументировать это в своём коде, используя: [Дополнительные ответы](additional-responses.md).
================================================
FILE: docs/ru/docs/advanced/advanced-dependencies.md
================================================
# Продвинутые зависимости { #advanced-dependencies }
## Параметризованные зависимости { #parameterized-dependencies }
Все зависимости, которые мы видели, — это конкретная функция или класс.
Но бывают случаи, когда нужно задавать параметры зависимости, не объявляя много разных функций или классов.
Представим, что нам нужна зависимость, которая проверяет, содержит ли query-параметр `q` некоторое фиксированное содержимое.
Но при этом мы хотим иметь возможность параметризовать это фиксированное содержимое.
## «Вызываемый» экземпляр { #a-callable-instance }
В Python есть способ сделать экземпляр класса «вызываемым» объектом.
Не сам класс (он уже является вызываемым), а экземпляр этого класса.
Для этого объявляем метод `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
## Параметризуем экземпляр { #parameterize-the-instance }
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
## Создаём экземпляр { #create-an-instance }
Мы можем создать экземпляр этого класса так:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
## Используем экземпляр как зависимость { #use-the-instance-as-a-dependency }
Затем мы можем использовать этот `checker` в `Depends(checker)` вместо `Depends(FixedContentQueryChecker)`, потому что зависимостью является экземпляр `checker`, а не сам класс.
И при разрешении зависимости **FastAPI** вызовет `checker` примерно так:
```Python
checker(q="somequery")
```
…и передаст возвращённое значение как значение зависимости в параметр `fixed_content_included` нашей *функции-обработчику пути*:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Совет
Все это может показаться притянутым за уши. И пока может быть не совсем понятно, чем это полезно.
Эти примеры намеренно простые, но они показывают, как всё устроено.
В главах про безопасность есть вспомогательные функции, реализованные тем же способом.
Если вы поняли всё выше, вы уже знаете, как «под капотом» работают эти утилиты для безопасности.
///
## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Предупреждение
Скорее всего, вам не понадобятся эти технические детали.
Они полезны главным образом, если у вас было приложение FastAPI версии ниже 0.121.0 и вы столкнулись с проблемами зависимостей с `yield`.
///
Зависимости с `yield` со временем изменялись, чтобы учитывать разные случаи применения и исправлять проблемы. Ниже — краткое резюме изменений.
### Зависимости с `yield` и `scope` { #dependencies-with-yield-and-scope }
В версии 0.121.0 FastAPI добавил поддержку `Depends(scope="function")` для зависимостей с `yield`.
При использовании `Depends(scope="function")` код после `yield` выполняется сразу после завершения *функции-обработчика пути*, до отправки ответа клиенту.
А при использовании `Depends(scope="request")` (значение по умолчанию) код после `yield` выполняется после отправки ответа.
Подробнее читайте в документации: [Зависимости с `yield` — раннее завершение и `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Зависимости с `yield` и `StreamingResponse`, технические детали { #dependencies-with-yield-and-streamingresponse-technical-details }
До FastAPI 0.118.0, если вы использовали зависимость с `yield`, код после `yield` выполнялся после возврата из *функции-обработчика пути*, но прямо перед отправкой ответа.
Идея состояла в том, чтобы не удерживать ресурсы дольше необходимого, пока ответ «путешествует» по сети.
Это изменение также означало, что если вы возвращали `StreamingResponse`, код после `yield` в зависимости уже успевал выполниться.
Например, если у вас была сессия базы данных в зависимости с `yield`, `StreamingResponse` не смог бы использовать эту сессию во время стриминга данных, потому что сессия уже была закрыта в коде после `yield`.
В версии 0.118.0 это поведение было возвращено к тому, что код после `yield` выполняется после отправки ответа.
/// info | Информация
Как вы увидите ниже, это очень похоже на поведение до версии 0.106.0, но с несколькими улучшениями и исправлениями краевых случаев.
///
#### Сценарии с ранним выполнением кода после `yield` { #use-cases-with-early-exit-code }
Есть некоторые сценарии со специфическими условиями, которым могло бы помочь старое поведение — выполнение кода после `yield` перед отправкой ответа.
Например, представьте, что вы используете сессию базы данных в зависимости с `yield` только для проверки пользователя, а в самой *функции-обработчике пути* эта сессия больше не используется, и при этом ответ отправляется долго, например, это `StreamingResponse`, который медленно отправляет данные и по какой-то причине не использует базу данных.
В таком случае сессия базы данных будет удерживаться до завершения отправки ответа, хотя если вы её не используете, удерживать её не требуется.
Это могло бы выглядеть так:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Код после `yield`, автоматическое закрытие `Session` в:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
…будет выполнен после того, как ответ закончит отправку медленных данных:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Но поскольку `generate_stream()` не использует сессию базы данных, нет реальной необходимости держать сессию открытой во время отправки ответа.
Если у вас именно такой сценарий с SQLModel (или SQLAlchemy), вы можете явно закрыть сессию, когда она больше не нужна:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Так сессия освободит подключение к базе данных, и другие запросы смогут его использовать.
Если у вас есть другой сценарий, где нужно раннее завершение зависимости с `yield`, пожалуйста, создайте [вопрос в GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions) с описанием конкретного кейса и почему вам было бы полезно иметь раннее закрытие для зависимостей с `yield`.
Если появятся веские причины для раннего закрытия в зависимостях с `yield`, я рассмотрю добавление нового способа опционально включать раннее закрытие.
### Зависимости с `yield` и `except`, технические детали { #dependencies-with-yield-and-except-technical-details }
До FastAPI 0.110.0, если вы использовали зависимость с `yield`, затем перехватывали исключение с `except` в этой зависимости и не пробрасывали исключение снова, исключение автоматически пробрасывалось дальше к обработчикам исключений или к обработчику внутренней ошибки сервера.
В версии 0.110.0 это было изменено, чтобы исправить неконтролируемое потребление памяти из‑за проброшенных исключений без обработчика (внутренние ошибки сервера) и привести поведение в соответствие с обычным поведением Python-кода.
### Фоновые задачи и зависимости с `yield`, технические детали { #background-tasks-and-dependencies-with-yield-technical-details }
До FastAPI 0.106.0 вызывать исключения после `yield` было невозможно: код после `yield` в зависимостях выполнялся уже после отправки ответа, поэтому [Обработчики исключений](../tutorial/handling-errors.md#install-custom-exception-handlers) к тому моменту уже отработали.
Так было сделано в основном для того, чтобы можно было использовать те же объекты, «отданные» зависимостями через `yield`, внутри фоновых задач, потому что код после `yield` выполнялся после завершения фоновых задач.
В FastAPI 0.106.0 это изменили, чтобы не удерживать ресурсы, пока ответ передаётся по сети.
/// tip | Совет
Кроме того, фоновая задача обычно — это самостоятельный фрагмент логики, который следует обрабатывать отдельно, со своими ресурсами (например, со своим подключением к базе данных).
Так код, скорее всего, будет чище.
///
Если вы полагались на прежнее поведение, теперь ресурсы для фоновых задач следует создавать внутри самой фоновой задачи и использовать внутри неё только данные, которые не зависят от ресурсов зависимостей с `yield`.
Например, вместо использования той же сессии базы данных, создайте новую сессию в фоновой задаче и получите объекты из базы данных с помощью этой новой сессии. И затем, вместо передачи объекта из базы данных параметром в функцию фоновой задачи, передавайте идентификатор этого объекта и заново получайте объект внутри функции фоновой задачи.
================================================
FILE: docs/ru/docs/advanced/advanced-python-types.md
================================================
# Продвинутые типы Python { #advanced-python-types }
Ниже несколько дополнительных идей, которые могут быть полезны при работе с типами Python.
## Использование `Union` или `Optional` { #using-union-or-optional }
Если по какой-то причине ваш код не может использовать `|`, например, если это не аннотация типов, а что-то вроде `response_model=`, вместо вертикальной черты (`|`) можно использовать `Union` из `typing`.
Например, вы можете объявить, что значение может быть `str` или `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
В `typing` также есть сокращение, чтобы объявить, что значение может быть `None`, — `Optional`.
Вот совет с моей очень субъективной точки зрения:
- 🚨 Избегайте использования `Optional[SomeType]`
- Вместо этого ✨ используйте **`Union[SomeType, None]`** ✨.
Оба варианта эквивалентны и под капотом это одно и то же, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «optional» может наводить на мысль, что значение необязательное, тогда как на самом деле это означает «значение может быть `None`», даже если оно не является необязательным и по-прежнему требуется.
По-моему, `Union[SomeType, None]` более явно передаёт смысл.
Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваша команда думаете о коде.
В качестве примера возьмём такую функцию:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
Параметр `name` объявлен как `Optional[str]`, но он не является необязательным: вы не можете вызвать функцию без этого параметра:
```Python
say_hi() # О нет, это вызывает ошибку! 😱
```
Параметр `name` по-прежнему обязателен (не «optional»), так как у него нет значения по умолчанию. При этом `name` принимает `None` в качестве значения:
```Python
say_hi(name=None) # Это работает, None допустим 🎉
```
Хорошая новость: в большинстве случаев вы сможете просто использовать `|` для объявления объединений типов:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Так что обычно вам не о чем переживать из‑за названий вроде `Optional` и `Union`. 😎
================================================
FILE: docs/ru/docs/advanced/async-tests.md
================================================
# Асинхронное тестирование { #async-tests }
Вы уже видели как тестировать **FastAPI** приложение, используя имеющийся класс `TestClient`. К этому моменту вы видели только как писать тесты в синхронном стиле без использования `async` функций.
Возможность использования асинхронных функций в ваших тестах может быть полезнa, когда, например, вы асинхронно обращаетесь к вашей базе данных. Представьте, что вы хотите отправить запросы в ваше FastAPI приложение, а затем при помощи асинхронной библиотеки для работы с базой данных удостовериться, что ваш бекэнд корректно записал данные в базу данных.
Давайте рассмотрим, как мы можем это реализовать.
## pytest.mark.anyio { #pytest-mark-anyio }
Если мы хотим вызывать асинхронные функции в наших тестах, то наши тестовые функции должны быть асинхронными. AnyIO предоставляет для этого отличный плагин, который позволяет нам указывать, какие тестовые функции должны вызываться асинхронно.
## HTTPX { #httpx }
Даже если **FastAPI** приложение использует обычные функции `def` вместо `async def`, это все равно `async` приложение 'под капотом'.
Чтобы работать с асинхронным FastAPI приложением в ваших обычных тестовых функциях `def`, используя стандартный pytest, `TestClient` внутри себя делает некоторую магию. Но эта магия перестает работать, когда мы используем его внутри асинхронных функций. Запуская наши тесты асинхронно, мы больше не можем использовать `TestClient` внутри наших тестовых функций.
`TestClient` основан на [HTTPX](https://www.python-httpx.org), и, к счастью, мы можем использовать его (`HTTPX`) напрямую для тестирования API.
## Пример { #example }
В качестве простого примера, давайте рассмотрим файловую структуру, схожую с описанной в [Большие приложения](../tutorial/bigger-applications.md) и [Тестирование](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Файл `main.py`:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
Файл `test_main.py` содержит тесты для `main.py`, теперь он может выглядеть так:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Запуск тестов { #run-it }
Вы можете запустить свои тесты как обычно:
```console
$ pytest
---> 100%
```
## Подробнее { #in-detail }
Маркер `@pytest.mark.anyio` говорит pytest, что тестовая функция должна быть вызвана асинхронно:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Подсказка
Обратите внимание, что тестовая функция теперь `async def` вместо простого `def`, как это было при использовании `TestClient`.
///
Затем мы можем создать `AsyncClient` со ссылкой на приложение и посылать асинхронные запросы, используя `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Это эквивалентно следующему:
```Python
response = client.get('/')
```
...которое мы использовали для отправки наших запросов с `TestClient`.
/// tip | Подсказка
Обратите внимание, что мы используем async/await с `AsyncClient` - запрос асинхронный.
///
/// warning | Внимание
Если ваше приложение полагается на lifespan события, то `AsyncClient` не запустит эти события. Чтобы обеспечить их срабатывание используйте `LifespanManager` из [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Вызов других асинхронных функций { #other-asynchronous-function-calls }
Теперь тестовая функция стала асинхронной, поэтому внутри нее вы можете вызывать также и другие `async` функции, не связанные с отправлением запросов в ваше FastAPI приложение. Как если бы вы вызывали их в любом другом месте вашего кода.
/// tip | Подсказка
Если вы столкнулись с `RuntimeError: Task attached to a different loop` при вызове асинхронных функций в ваших тестах (например, при использовании [MongoDB's MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), то не забывайте создавать экземпляры объектов, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в `@app.on_event("startup")` callback.
///
================================================
FILE: docs/ru/docs/advanced/behind-a-proxy.md
================================================
# За прокси‑сервером { #behind-a-proxy }
Во многих случаях перед приложением FastAPI используется прокси‑сервер, например Traefik или Nginx.
Такие прокси могут обрабатывать HTTPS‑сертификаты и многое другое.
## Пересылаемые заголовки прокси { #proxy-forwarded-headers }
Прокси перед вашим приложением обычно на лету добавляет некоторые HTTP‑заголовки перед отправкой запроса на ваш сервер, чтобы сообщить ему, что запрос был переслан прокси, а также передать исходный (публичный) URL (включая домен), информацию об использовании HTTPS и т.д.
Программа сервера (например, Uvicorn, запущенный через FastAPI CLI) умеет интерпретировать эти заголовки и передавать соответствующую информацию вашему приложению.
Но из соображений безопасности, пока сервер не уверен, что находится за доверенным прокси, он не будет интерпретировать эти заголовки.
/// note | Технические детали
Заголовки прокси:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Включить пересылаемые заголовки прокси { #enable-proxy-forwarded-headers }
Вы можете запустить FastAPI CLI с опцией командной строки `--forwarded-allow-ips` и передать IP‑адреса, которым следует доверять при чтении этих пересылаемых заголовков.
Если указать `--forwarded-allow-ips="*"`, приложение будет доверять всем входящим IP.
Если ваш сервер находится за доверенным прокси и только прокси обращается к нему, этого достаточно, чтобы он принимал IP этого прокси.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Редиректы с HTTPS { #redirects-with-https }
Например, вы объявили операцию пути `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Если клиент обратится к `/items`, по умолчанию произойдёт редирект на `/items/`.
Но до установки опции `--forwarded-allow-ips` редирект может вести на `http://localhost:8000/items/`.
Однако приложение может быть доступно по `https://mysuperapp.com`, и редирект должен вести на `https://mysuperapp.com/items/`.
Указав `--proxy-headers`, FastAPI сможет редиректить на корректный адрес. 😎
```
https://mysuperapp.com/items/
```
/// tip | Совет
Если хотите узнать больше об HTTPS, смотрите руководство [О HTTPS](../deployment/https.md).
///
### Как работают пересылаемые заголовки прокси { #how-proxy-forwarded-headers-work }
Ниже показано, как прокси добавляет пересылаемые заголовки между клиентом и сервером приложения:
```mermaid
sequenceDiagram
participant Client as Клиент
participant Proxy as Прокси/Балансировщик нагрузки
participant Server as FastAPI-сервер
Client->>Proxy: HTTPS-запрос Host: mysuperapp.com Path: /items
Note over Proxy: Прокси-сервер добавляет пересылаемые заголовки
Proxy->>Server: HTTP-запрос X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server интерпретирует HTTP-заголовки (если --forwarded-allow-ips установлен)
Server->>Proxy: HTTP-ответ с верными HTTPS URLs
Proxy->>Client: HTTPS-ответ
```
Прокси перехватывает исходный клиентский запрос и добавляет специальные пересылаемые заголовки (`X-Forwarded-*`) перед передачей запроса на сервер приложения.
Эти заголовки сохраняют информацию об исходном запросе, которая иначе была бы потеряна:
* X-Forwarded-For: исходный IP‑адрес клиента
* X-Forwarded-Proto: исходный протокол (`https`)
* X-Forwarded-Host: исходный хост (`mysuperapp.com`)
Когда FastAPI CLI сконфигурирован с `--forwarded-allow-ips`, он доверяет этим заголовкам и использует их, например, чтобы формировать корректные URL в редиректах.
## Прокси с функцией удаления префикса пути { #proxy-with-a-stripped-path-prefix }
Прокси может добавлять к вашему приложению префикс пути (размещать приложение по пути с дополнительным префиксом).
В таких случаях вы можете использовать `root_path` для настройки приложения.
Механизм `root_path` определён спецификацией ASGI (на которой построен FastAPI, через Starlette).
`root_path` используется для обработки таких специфических случаев.
Он также используется внутри при монтировании вложенных приложений.
Прокси с функцией удаления префикса пути в этом случае означает, что вы объявляете путь `/app` в коде, а затем добавляете сверху слой (прокси), который размещает ваше приложение FastAPI под путём вида `/api/v1`.
Тогда исходный путь `/app` фактически будет обслуживаться по адресу `/api/v1/app`.
Хотя весь ваш код написан с расчётом, что путь один — `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Прокси будет «обрезать» префикс пути на лету перед передачей запроса на сервер приложения (скорее всего Uvicorn, запущенный через FastAPI CLI), поддерживая у вашего приложения иллюзию, что его обслуживают по `/app`, чтобы вам не пришлось менять весь код и добавлять префикс `/api/v1`.
До этого момента всё будет работать как обычно.
Но когда вы откроете встроенный интерфейс документации (фронтенд), он будет ожидать получить схему OpenAPI по адресу `/openapi.json`, а не `/api/v1/openapi.json`.
Поэтому фронтенд (который работает в браузере) попытается обратиться к `/openapi.json` и не сможет получить схему OpenAPI.
Так как для нашего приложения используется прокси с префиксом пути `/api/v1`, фронтенду нужно забирать схему OpenAPI по `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Совет
IP `0.0.0.0` обычно означает, что программа слушает на всех IP‑адресах, доступных на этой машине/сервере.
///
Интерфейсу документации также нужна схема OpenAPI, в которой будет указано, что этот API `server` находится по пути `/api/v1` (за прокси). Например:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Здесь ещё что-то
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Здесь ещё что-то
}
}
```
В этом примере «Proxy» может быть, например, Traefik. А сервером будет что‑то вроде FastAPI CLI с Uvicorn, на котором запущено ваше приложение FastAPI.
### Указание `root_path` { #providing-the-root-path }
Для этого используйте опцию командной строки `--root-path`, например так:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Если вы используете Hypercorn, у него тоже есть опция `--root-path`.
/// note | Технические детали
Спецификация ASGI определяет `root_path` для такого случая.
А опция командной строки `--root-path` передаёт этот `root_path`.
///
### Проверка текущего `root_path` { #checking-the-current-root-path }
Вы можете получить текущий `root_path`, используемый вашим приложением для каждого запроса, — он входит в словарь `scope` (часть спецификации ASGI).
Здесь мы добавляем его в сообщение лишь для демонстрации.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Затем, если вы запустите Uvicorn так:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Ответ будет примерно таким:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Установка `root_path` в приложении FastAPI { #setting-the-root-path-in-the-fastapi-app }
Если нет возможности передать опцию командной строки `--root-path` (или аналог), вы можете указать параметр `root_path` при создании приложения FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Передача `root_path` в `FastAPI` эквивалентна опции командной строки `--root-path` для Uvicorn или Hypercorn.
### О `root_path` { #about-root-path }
Учтите, что сервер (Uvicorn) не использует `root_path` ни для чего, кроме как передать его в приложение.
Если вы откроете в браузере [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), вы увидите обычный ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
То есть он не ожидает, что к нему обратятся по адресу `http://127.0.0.1:8000/api/v1/app`.
Uvicorn ожидает, что прокси обратится к нему по `http://127.0.0.1:8000/app`, а уже задача прокси — добавить сверху префикс `/api/v1`.
## О прокси с функцией удаления префикса пути { #about-proxies-with-a-stripped-path-prefix }
Помните, что прокси с функцией удаления префикса пути — лишь один из вариантов настройки.
Во многих случаях по умолчанию прокси будет без функции удаления префикса пути.
В таком случае (без функции удаления префикса пути) прокси слушает, например, по адресу `https://myawesomeapp.com`, и если браузер идёт на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (например, Uvicorn) слушает на `http://127.0.0.1:8000`, то прокси (без урезанного префикса) обратится к Uvicorn по тому же пути: `http://127.0.0.1:8000/api/v1/app`.
## Локальное тестирование с Traefik { #testing-locally-with-traefik }
Вы можете легко поэкспериментировать локально с функцией удаления префикса пути, используя [Traefik](https://docs.traefik.io/).
[Скачайте Traefik](https://github.com/containous/traefik/releases) — это один бинарный файл; распакуйте архив и запустите его прямо из терминала.
Затем создайте файл `traefik.toml` со следующим содержимым:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Это говорит Traefik слушать порт 9999 и использовать другой файл `routes.toml`.
/// tip | Совет
Мы используем порт 9999 вместо стандартного HTTP‑порта 80, чтобы не нужно было запускать с правами администратора (`sudo`).
///
Теперь создайте второй файл `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Этот файл настраивает Traefik на использование префикса пути `/api/v1`.
Далее Traefik будет проксировать запросы на ваш Uvicorn, работающий на `http://127.0.0.1:8000`.
Теперь запустите Traefik:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Проверьте ответы { #check-the-responses }
Теперь, если вы перейдёте на URL с портом Uvicorn: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), вы увидите обычный ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Совет
Обратите внимание, что хотя вы обращаетесь по `http://127.0.0.1:8000/app`, в ответе указан `root_path` равный `/api/v1`, взятый из опции `--root-path`.
///
А теперь откройте URL с портом Traefik и префиксом пути: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Мы получим тот же ответ:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
но уже по URL с префиксом, который добавляет прокси: `/api/v1`.
Разумеется, задумывается, что все будут обращаться к приложению через прокси, поэтому вариант с префиксом пути `/api/v1` является «правильным».
А вариант без префикса (`http://127.0.0.1:8000/app`), выдаваемый напрямую Uvicorn, предназначен исключительно для того, чтобы прокси (Traefik) мог к нему обращаться.
Это демонстрирует, как прокси (Traefik) использует префикс пути и как сервер (Uvicorn) использует `root_path`, переданный через опцию `--root-path`.
### Проверьте интерфейс документации { #check-the-docs-ui }
А вот самое интересное. ✨
«Официальный» способ доступа к приложению — через прокси с заданным префиксом пути. Поэтому, как и ожидается, если открыть интерфейс документации, отдаваемый напрямую Uvicorn, без префикса пути в URL, он не будет работать, так как предполагается доступ через прокси.
Проверьте по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
А вот если открыть интерфейс документации по «официальному» URL через прокси на порту `9999`, по `/api/v1/docs`, всё работает корректно! 🎉
Проверьте по адресу [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Именно как и хотелось. ✔️
Это потому, что FastAPI использует `root_path`, чтобы создать в OpenAPI сервер по умолчанию с URL из `root_path`.
## Дополнительные серверы { #additional-servers }
/// warning | Предупреждение
Это более продвинутый сценарий. Можно пропустить.
///
По умолчанию FastAPI создаёт в схеме OpenAPI `server` с URL из `root_path`.
Но вы также можете указать дополнительные `servers`, например, если хотите, чтобы один и тот же интерфейс документации работал и со стейджингом, и с продакшн.
Если вы передадите свой список `servers` и при этом задан `root_path` (потому что ваш API работает за прокси), FastAPI вставит «server» с этим `root_path` в начало списка.
Например:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Будет сгенерирована схема OpenAPI примерно такая:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Здесь ещё что-то
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Здесь ещё что-то
}
}
```
/// tip | Совет
Обратите внимание на автоматически добавленный сервер с `url` равным `/api/v1`, взятым из `root_path`.
///
В интерфейсе документации по адресу [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) это будет выглядеть так:
/// tip | Совет
Интерфейс документации будет взаимодействовать с сервером, который вы выберете.
///
/// note | Технические детали
Свойство `servers` в спецификации OpenAPI является необязательным.
Если вы не укажете параметр `servers`, а `root_path` равен `/`, то свойство `servers` в сгенерированной схеме OpenAPI по умолчанию будет опущено. Это эквивалентно серверу со значением `url` равным `/`.
///
### Отключить автоматическое добавление сервера из `root_path` { #disable-automatic-server-from-root-path }
Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
и тогда этот сервер не будет добавлен в схему OpenAPI.
## Монтирование вложенного приложения { #mounting-a-sub-application }
Если вам нужно смонтировать вложенное приложение (как описано в [Вложенные приложения — монтирование](sub-applications.md)), и при этом вы используете прокси с `root_path`, делайте это обычным образом — всё будет работать, как ожидается.
FastAPI умно использует `root_path` внутри, так что всё просто работает. ✨
================================================
FILE: docs/ru/docs/advanced/custom-response.md
================================================
# Кастомные ответы — HTML, поток, файл и другие { #custom-response-html-stream-file-others }
По умолчанию **FastAPI** возвращает ответы в формате JSON.
Вы можете переопределить это, вернув `Response` напрямую, как показано в разделе [Вернуть Response напрямую](response-directly.md).
Но если вы возвращаете `Response` напрямую (или любой его подкласс, например `JSONResponse`), данные не будут автоматически преобразованы (даже если вы объявили `response_model`), и документация не будет автоматически сгенерирована (например, со специфичным «типом содержимого» в HTTP-заголовке `Content-Type` как частью сгенерированного OpenAPI).
Но вы также можете объявить `Response`, который хотите использовать (например, любой подкласс `Response`), в декораторе операции пути, указав параметр `response_class`.
Содержимое, которое вы возвращаете из своей функции-обработчика пути, будет помещено внутрь этого `Response`.
/// note | Примечание
Если вы используете класс ответа без типа содержимого, FastAPI будет ожидать, что у вашего ответа нет содержимого, поэтому он не будет документировать формат ответа в сгенерированной документации OpenAPI.
///
## JSON-ответы { #json-responses }
По умолчанию FastAPI возвращает ответы в формате JSON.
Если вы объявите [Модель ответа](../tutorial/response-model.md), FastAPI использует её для сериализации данных в JSON с помощью Pydantic.
Если вы не объявите модель ответа, FastAPI использует `jsonable_encoder`, описанный в разделе [JSON-совместимый энкодер](../tutorial/encoder.md), и поместит результат в `JSONResponse`.
Если вы объявите `response_class` с JSON типом содержимого (`application/json`), как в случае с `JSONResponse`, данные, которые вы возвращаете, будут автоматически преобразованы (и отфильтрованы) любой Pydantic-моделью ответа (`response_model`), объявленной вами в декораторе операции пути. Но данные не будут сериализованы в JSON-байты через Pydantic; вместо этого они будут преобразованы с помощью `jsonable_encoder`, а затем переданы в класс `JSONResponse`, который сериализует их в байты, используя стандартную JSON-библиотеку Python.
### Производительность JSON { #json-performance }
Коротко: если вам нужна максимальная производительность, используйте [Модель ответа](../tutorial/response-model.md) и не объявляйте `response_class` в декораторе операции пути.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML-ответ { #html-response }
Чтобы вернуть ответ с HTML напрямую из **FastAPI**, используйте `HTMLResponse`.
- Импортируйте `HTMLResponse`.
- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Информация
Параметр `response_class` также используется для указания «типа содержимого» ответа.
В этом случае HTTP-заголовок `Content-Type` будет установлен в `text/html`.
И это будет задокументировано как таковое в OpenAPI.
///
### Вернуть `Response` { #return-a-response }
Как показано в разделе [Вернуть Response напрямую](response-directly.md), вы также можете переопределить ответ прямо в своей операции пути, просто вернув его.
Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Предупреждение
`Response`, возвращённый напрямую вашей функцией-обработчиком пути, не будет задокументирован в OpenAPI (например, `Content-Type` не будет задокументирован) и не будет виден в автоматически сгенерированной интерактивной документации.
///
/// info | Информация
Разумеется, фактический заголовок `Content-Type`, статус-код и т.д. возьмутся из объекта `Response`, который вы вернули.
///
### Задокументировать в OpenAPI и переопределить `Response` { #document-in-openapi-and-override-response }
Если вы хотите переопределить ответ внутри функции, но при этом задокументировать «тип содержимого» в OpenAPI, вы можете использовать параметр `response_class` И вернуть объект `Response`.
Тогда `response_class` будет использоваться только для документирования *операции пути* в OpenAPI, а ваш `Response` будет использован как есть.
#### Вернуть `HTMLResponse` напрямую { #return-an-htmlresponse-directly }
Например, это может быть что-то вроде:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
Возвращая результат вызова `generate_html_response()`, вы уже возвращаете `Response`, который переопределит поведение **FastAPI** по умолчанию.
Но поскольку вы также передали `HTMLResponse` в `response_class`, **FastAPI** будет знать, как задокументировать это в OpenAPI и интерактивной документации как HTML с `text/html`:
## Доступные ответы { #available-responses }
Ниже перечислены некоторые доступные классы ответов.
Учтите, что вы можете использовать `Response`, чтобы вернуть что угодно ещё, или даже создать собственный подкласс.
/// note | Технические детали
Вы также могли бы использовать `from starlette.responses import HTMLResponse`.
**FastAPI** предоставляет те же `starlette.responses` как `fastapi.responses` для вашего удобства как разработчика. Но большинство доступных классов ответов приходят непосредственно из Starlette.
///
### `Response` { #response }
Базовый класс `Response`, от него наследуются все остальные ответы.
Его можно возвращать напрямую.
Он принимает следующие параметры:
- `content` — `str` или `bytes`.
- `status_code` — целое число, HTTP статус-код.
- `headers` — словарь строк.
- `media_type` — строка, задающая тип содержимого. Например, `"text/html"`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Принимает текст или байты и возвращает HTML-ответ, как описано выше.
### `PlainTextResponse` { #plaintextresponse }
Принимает текст или байты и возвращает ответ в виде простого текста.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Принимает данные и возвращает ответ, кодированный как `application/json`.
Это ответ по умолчанию, используемый в **FastAPI**, как было сказано выше.
/// note | Технические детали
Но если вы объявите модель ответа или тип возвращаемого значения, они будут использованы напрямую для сериализации данных в JSON, и ответ с корректным типом содержимого для JSON будет возвращён напрямую, без использования класса `JSONResponse`.
Это идеальный способ получить наилучшую производительность.
///
### `RedirectResponse` { #redirectresponse }
Возвращает HTTP-редирект. По умолчанию использует статус-код 307 (Temporary Redirect — временное перенаправление).
Вы можете вернуть `RedirectResponse` напрямую:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Или можно использовать его в параметре `response_class`:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
В этом случае будет использован статус-код по умолчанию для `RedirectResponse`, то есть `307`.
---
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Принимает асинхронный генератор или обычный генератор/итератор (функцию с `yield`) и отправляет тело ответа потоково.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Технические детали
Задача `async` может быть отменена только при достижении `await`. Если `await` отсутствует, генератор (функция с `yield`) не может быть корректно отменён и может продолжить работу даже после запроса на отмену.
Так как этому небольшому примеру не нужны операторы `await`, мы добавляем `await anyio.sleep(0)`, чтобы дать циклу событий возможность обработать отмену.
Это ещё более важно для больших или бесконечных потоков.
///
/// tip | Совет
Вместо того чтобы возвращать `StreamingResponse` напрямую, вероятно, лучше следовать стилю из раздела [Передача данных потоком](./stream-data.md) - так гораздо удобнее, и отмена обрабатывается «за кулисами».
Если вы передаёте JSON Lines потоком, следуйте руководству [Поток JSON Lines](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Асинхронно отправляет файл как ответ.
Для создания экземпляра принимает иной набор аргументов, чем другие типы ответов:
- `path` — путь к файлу, который будет отправлен.
- `headers` — любые дополнительные заголовки для включения, в виде словаря.
- `media_type` — строка, задающая тип содержимого. Если не задан, для определения типа содержимого будет использовано имя файла или путь.
- `filename` — если задан, будет включён в заголовок ответа `Content-Disposition`.
Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Вы также можете использовать параметр `response_class`:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
## Пользовательский класс ответа { #custom-response-class }
Вы можете создать собственный класс ответа, унаследовавшись от `Response`, и использовать его.
Например, предположим, что вы хотите использовать [`orjson`](https://github.com/ijl/orjson) с некоторыми настройками.
Скажем, вы хотите, чтобы возвращался отформатированный JSON с отступами, то есть хотите использовать опцию orjson `orjson.OPT_INDENT_2`.
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Теперь вместо того, чтобы возвращать:
```json
{"message": "Hello World"}
```
...этот ответ вернёт:
```json
{
"message": "Hello World"
}
```
Разумеется, вы наверняка найдёте гораздо более полезные способы воспользоваться этим, чем просто форматирование JSON. 😉
### `orjson` или Модель ответа { #orjson-or-response-model }
Если вы стремитесь увеличить производительность, вероятно, лучше использовать [Модель ответа](../tutorial/response-model.md), чем ответ на базе `orjson`.
С моделью ответа FastAPI использует Pydantic для сериализации данных в JSON, без промежуточных шагов, таких как преобразование через `jsonable_encoder`, которое происходило бы в любом другом случае.
А под капотом Pydantic использует те же базовые механизмы на Rust, что и `orjson`, для сериализации в JSON, так что с моделью ответа вы и так получите лучшую производительность.
## Класс ответа по умолчанию { #default-response-class }
При создании экземпляра класса **FastAPI** или `APIRouter` вы можете указать, какой класс ответа использовать по умолчанию.
Параметр, который это определяет, — `default_response_class`.
В примере ниже **FastAPI** будет использовать `HTMLResponse` по умолчанию во всех операциях пути, вместо JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Совет
Вы по-прежнему можете переопределять `response_class` в операциях пути, как и раньше.
///
## Дополнительная документация { #additional-documentation }
Вы также можете объявить тип содержимого и многие другие детали в OpenAPI с помощью `responses`: [Дополнительные ответы в OpenAPI](additional-responses.md).
================================================
FILE: docs/ru/docs/advanced/dataclasses.md
================================================
# Использование dataclasses { #using-dataclasses }
FastAPI построен поверх **Pydantic**, и я показывал вам, как использовать Pydantic-модели для объявления HTTP-запросов и HTTP-ответов.
Но FastAPI также поддерживает использование [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) тем же способом:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Это по-прежнему поддерживается благодаря **Pydantic**, так как в нём есть [встроенная поддержка `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Так что даже если в коде выше Pydantic не используется явно, FastAPI использует Pydantic, чтобы конвертировать стандартные dataclasses в собственный вариант dataclasses от Pydantic.
И, конечно, поддерживаются те же возможности:
- валидация данных
- сериализация данных
- документирование данных и т.д.
Это работает так же, как с Pydantic-моделями. И на самом деле под капотом это достигается тем же образом, с использованием Pydantic.
/// info | Информация
Помните, что dataclasses не умеют всего того, что умеют Pydantic-модели.
Поэтому вам всё ещё может потребоваться использовать Pydantic-модели.
Но если у вас уже есть набор dataclasses, это полезный приём — задействовать их для веб-API на FastAPI. 🤓
///
## Dataclasses в `response_model` { #dataclasses-in-response-model }
Вы также можете использовать `dataclasses` в параметре `response_model`:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Этот dataclass будет автоматически преобразован в Pydantic dataclass.
Таким образом, его схема появится в интерфейсе документации API:
## Dataclasses во вложенных структурах данных { #dataclasses-in-nested-data-structures }
Вы также можете комбинировать `dataclasses` с другими аннотациями типов, чтобы создавать вложенные структуры данных.
В некоторых случаях вам всё же может понадобиться использовать версию `dataclasses` из Pydantic. Например, если у вас возникают ошибки с автоматически генерируемой документацией API.
В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement):
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.
2. `pydantic.dataclasses` — полностью совместимая замена (drop-in replacement) для `dataclasses`.
3. Dataclass `Author` содержит список dataclass `Item`.
4. Dataclass `Author` используется в параметре `response_model`.
5. Вы можете использовать и другие стандартные аннотации типов вместе с dataclasses в качестве тела запроса.
В этом случае это список dataclass `Item`.
6. Здесь мы возвращаем словарь, содержащий `items`, который является списком dataclass.
FastAPI по-прежнему способен сериализовать данные в JSON.
7. Здесь `response_model` использует аннотацию типа — список dataclass `Author`.
Снова, вы можете комбинировать `dataclasses` со стандартными аннотациями типов.
8. Обратите внимание, что эта *функция-обработчик пути* использует обычный `def` вместо `async def`.
Как и всегда в FastAPI, вы можете сочетать `def` и `async def` по необходимости.
Если хотите освежить в памяти, когда что использовать, посмотрите раздел _"Нет времени?"_ в документации про [`async` и `await`](../async.md#in-a-hurry).
9. Эта *функция-обработчик пути* возвращает не dataclasses (хотя могла бы), а список словарей с внутренними данными.
FastAPI использует параметр `response_model` (в котором заданы dataclasses), чтобы преобразовать HTTP-ответ.
Вы можете комбинировать `dataclasses` с другими аннотациями типов множеством способов, чтобы формировать сложные структуры данных.
Смотрите подсказки в коде выше, чтобы увидеть более конкретные детали.
## Узнать больше { #learn-more }
Вы также можете комбинировать `dataclasses` с другими Pydantic-моделями, наследоваться от них, включать их в свои модели и т.д.
Чтобы узнать больше, посмотрите [документацию Pydantic о dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Версия { #version }
Доступно начиная с версии FastAPI `0.67.0`. 🔖
================================================
FILE: docs/ru/docs/advanced/events.md
================================================
# События lifespan { #lifespan-events }
Вы можете определить логику (код), которую нужно выполнить перед тем, как приложение начнет запускаться. Это означает, что этот код будет выполнен один раз, перед тем как приложение начнет получать HTTP-запросы.
Аналогично, вы можете определить логику (код), которую нужно выполнить, когда приложение завершает работу. В этом случае код будет выполнен один раз, после обработки, возможно, многих запросов.
Поскольку этот код выполняется до того, как приложение начинает принимать запросы, и сразу после того, как оно заканчивает их обрабатывать, он охватывает весь lifespan (жизненный цикл) приложения (слово «lifespan» станет важным через секунду 😉).
Это может быть очень полезно для настройки ресурсов, которые нужны для всего приложения, которые разделяются между запросами и/или которые нужно затем очистить. Например, пул подключений к базе данных или загрузка общей модели Машинного обучения.
## Вариант использования { #use-case }
Начнем с примера варианта использования, а затем посмотрим, как это решить.
Представим, что у вас есть несколько моделей Машинного обучения, которые вы хотите использовать для обработки запросов. 🤖
Эти же модели разделяются между запросами, то есть это не одна модель на запрос, не одна на пользователя и т.п.
Представим, что загрузка модели может занимать довольно много времени, потому что ей нужно прочитать много данных с диска. Поэтому вы не хотите делать это для каждого запроса.
Вы могли бы загрузить её на верхнем уровне модуля/файла, но это означало бы, что модель загружается даже если вы просто запускаете простой автоматический тест; тогда этот тест будет медленным, так как ему придется ждать загрузки модели перед запуском независимой части кода.
Именно это мы и решим: давайте загружать модель перед тем, как начнётся обработка запросов, но только непосредственно перед тем, как приложение начнет принимать запросы, а не во время загрузки кода.
## Lifespan { #lifespan }
Вы можете определить логику для startup и shutdown, используя параметр `lifespan` приложения `FastAPI` и «менеджер контекста» (через секунду покажу что это).
Начнем с примера, а затем разберём его подробнее.
Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
А затем сразу после `yield` мы выгружаем модель. Этот код будет выполнен после того, как приложение закончит обрабатывать запросы, непосредственно перед shutdown. Это может, например, освободить ресурсы, такие как память или GPU.
/// tip | Совет
`shutdown` произойдёт, когда вы останавливаете приложение.
Возможно, вам нужно запустить новую версию, или вы просто устали от него. 🤷
///
### Функция lifespan { #lifespan-function }
Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
Первая часть функции, до `yield`, будет выполнена до запуска приложения.
А часть после `yield` будет выполнена после завершения работы приложения.
### Асинхронный менеджер контекста { #async-context-manager }
Если присмотреться, функция декорирована `@asynccontextmanager`.
Это превращает функцию в «асинхронный менеджер контекста».
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
```Python
with open("file.txt") as file:
file.read()
```
В последних версиях Python есть также асинхронный менеджер контекста. Его используют с `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Когда вы создаёте менеджер контекста или асинхронный менеджер контекста, как выше, он перед входом в блок `with` выполнит код до `yield`, а после выхода из блока `with` выполнит код после `yield`.
В нашем примере выше мы не используем его напрямую, а передаём его в FastAPI, чтобы он использовал его сам.
Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Альтернативные события (устаревшие) { #alternative-events-deprecated }
/// warning | Предупреждение
Рекомендуемый способ обрабатывать startup и shutdown — использовать параметр `lifespan` приложения `FastAPI`, как описано выше. Если вы укажете параметр `lifespan`, обработчики событий `startup` и `shutdown` больше вызываться не будут. Либо всё через `lifespan`, либо всё через события — не одновременно.
Эту часть, скорее всего, можно пропустить.
///
Есть альтернативный способ определить логику, которую нужно выполнить во время startup и во время shutdown.
Вы можете определить обработчики событий (функции), которые нужно выполнить до старта приложения или при его завершении.
Эти функции можно объявить с `async def` или обычным `def`.
### Событие `startup` { #startup-event }
Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
Вы можете добавить более одного обработчика события.
И ваше приложение не начнет принимать запросы, пока все обработчики события `startup` не завершатся.
### Событие `shutdown` { #shutdown-event }
Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
/// info | Информация
В функции `open()` параметр `mode="a"` означает «добавление» (append), то есть строка будет добавлена в конец файла, без перезаписи предыдущего содержимого.
///
/// tip | Совет
Обратите внимание, что в этом случае мы используем стандартную Python-функцию `open()`, которая взаимодействует с файлом.
То есть это I/O (ввод/вывод), требующий «ожидания» записи на диск.
Но `open()` не использует `async` и `await`.
Поэтому мы объявляем обработчик события обычным `def` вместо `async def`.
///
### `startup` и `shutdown` вместе { #startup-and-shutdown-together }
С высокой вероятностью логика для вашего startup и shutdown связана: вы можете хотеть что-то запустить, а затем завершить, получить ресурс, а затем освободить его и т.д.
Делать это в отдельных функциях, которые не разделяют общую логику или переменные, сложнее, так как придётся хранить значения в глобальных переменных или использовать похожие приёмы.
Поэтому теперь рекомендуется использовать `lifespan`, как описано выше.
## Технические детали { #technical-details }
Немного технических подробностей для любопытных умников. 🤓
Под капотом, в ASGI-технической спецификации, это часть [Протокола Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), и он определяет события `startup` и `shutdown`.
/// info | Информация
Вы можете прочитать больше про обработчики `lifespan` в Starlette в [документации Starlette по Lifespan](https://www.starlette.dev/lifespan/).
Включая то, как работать с состоянием lifespan, которое можно использовать в других частях вашего кода.
///
## Подприложения { #sub-applications }
🚨 Имейте в виду, что эти события lifespan (startup и shutdown) будут выполнены только для основного приложения, а не для [Подприложения — Mounts](sub-applications.md).
================================================
FILE: docs/ru/docs/advanced/generate-clients.md
================================================
# Генерация SDK { #generating-sdks }
Поскольку **FastAPI** основан на спецификации **OpenAPI**, его API можно описать в стандартном формате, понятном множеству инструментов.
Это упрощает генерацию актуальной **документации**, клиентских библиотек (**SDKs**) на разных языках, а также **тестирования** или **воркфлоу автоматизации**, которые остаются синхронизированными с вашим кодом.
В этом руководстве вы узнаете, как сгенерировать **TypeScript SDK** для вашего бэкенда на FastAPI.
## Генераторы SDK с открытым исходным кодом { #open-source-sdk-generators }
Гибкий вариант — [OpenAPI Generator](https://openapi-generator.tech/), который поддерживает **многие языки программирования** и умеет генерировать SDK из вашей спецификации OpenAPI.
Для **TypeScript‑клиентов** [Hey API](https://heyapi.dev/) — специализированное решение, обеспечивающее оптимальный опыт для экосистемы TypeScript.
Больше генераторов SDK можно найти на [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Совет
FastAPI автоматически генерирует спецификации **OpenAPI 3.1**, поэтому любой используемый инструмент должен поддерживать эту версию.
///
## Генераторы SDK от спонсоров FastAPI { #sdk-generators-from-fastapi-sponsors }
В этом разделе представлены решения с **венчурной поддержкой** и **поддержкой компаний** от компаний, которые спонсируют FastAPI. Эти продукты предоставляют **дополнительные возможности** и **интеграции** сверх высококачественно генерируемых SDK.
Благодаря ✨ [**спонсорству FastAPI**](../help-fastapi.md#sponsor-the-author) ✨ эти компании помогают обеспечивать, чтобы фреймворк и его **экосистема** оставались здоровыми и **устойчивыми**.
Их спонсорство также демонстрирует серьёзную приверженность **сообществу** FastAPI (вам), показывая, что им важно не только предоставлять **отличный сервис**, но и поддерживать **надёжный и процветающий фреймворк** FastAPI. 🙇
Например, вы можете попробовать:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Некоторые из этих решений также могут быть open source или иметь бесплатные тарифы, так что вы сможете попробовать их без финансовых затрат. Другие коммерческие генераторы SDK доступны и их можно найти онлайн. 🤓
## Создать TypeScript SDK { #create-a-typescript-sdk }
Начнём с простого приложения FastAPI:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Обратите внимание, что *операции пути (обработчики пути)* определяют модели, которые они используют для полезной нагрузки запроса и полезной нагрузки ответа, с помощью моделей `Item` и `ResponseMessage`.
### Документация API { #api-docs }
Если перейти на `/docs`, вы увидите **схемы** данных, отправляемых в запросах и принимаемых в ответах:
Вы видите эти схемы, потому что они были объявлены с моделями в приложении.
Эта информация доступна в **схеме OpenAPI** приложения и затем отображается в документации API.
Та же информация из моделей, включённая в OpenAPI, может использоваться для **генерации клиентского кода**.
### Hey API { #hey-api }
Как только у нас есть приложение FastAPI с моделями, мы можем использовать Hey API для генерации TypeScript‑клиента. Самый быстрый способ сделать это — через npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Это сгенерирует TypeScript SDK в `./src/client`.
Вы можете узнать, как [установить `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started) и почитать о [сгенерированном результате](https://heyapi.dev/openapi-ts/output) на их сайте.
### Использование SDK { #using-the-sdk }
Теперь вы можете импортировать и использовать клиентский код. Это может выглядеть так, обратите внимание, что вы получаете автозавершение для методов:
Вы также получите автозавершение для отправляемой полезной нагрузки:
/// tip | Совет
Обратите внимание на автозавершение для `name` и `price`, это было определено в приложении FastAPI, в модели `Item`.
///
Вы получите ошибки прямо в редакторе для отправляемых данных:
Объект ответа также будет иметь автозавершение:
## Приложение FastAPI с тегами { #fastapi-app-with-tags }
Во многих случаях ваше приложение FastAPI будет больше, и вы, вероятно, будете использовать теги, чтобы разделять разные группы *операций пути*.
Например, у вас может быть раздел для **items** и другой раздел для **users**, и они могут быть разделены тегами:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Генерация TypeScript‑клиента с тегами { #generate-a-typescript-client-with-tags }
Если вы генерируете клиент для приложения FastAPI с использованием тегов, обычно клиентский код также будет разделён по тегам.
Таким образом вы сможете иметь всё правильно упорядоченным и сгруппированным в клиентском коде:
В этом случае у вас есть:
* `ItemsService`
* `UsersService`
### Имена методов клиента { #client-method-names }
Сейчас сгенерированные имена методов вроде `createItemItemsPost` выглядят не очень аккуратно:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...это потому, что генератор клиента использует внутренний **ID операции** OpenAPI для каждой *операции пути*.
OpenAPI требует, чтобы каждый ID операции был уникален среди всех *операций пути*, поэтому FastAPI использует **имя функции**, **путь** и **HTTP‑метод/операцию** для генерации этого ID операции, так как таким образом можно гарантировать уникальность ID операций.
Но далее я покажу, как это улучшить. 🤓
## Пользовательские ID операций и лучшие имена методов { #custom-operation-ids-and-better-method-names }
Вы можете **изменить** способ **генерации** этих ID операций, чтобы сделать их проще, а имена методов в клиентах — **более простыми**.
В этом случае вам нужно будет обеспечить, чтобы каждый ID операции был **уникальным** другим способом.
Например, вы можете гарантировать, что у каждой *операции пути* есть тег, и затем генерировать ID операции на основе **тега** и **имени** *операции пути* (имени функции).
### Пользовательская функция генерации уникального ID { #custom-generate-unique-id-function }
FastAPI использует **уникальный ID** для каждой *операции пути*, который применяется для **ID операции**, а также для имён любых необходимых пользовательских моделей запросов или ответов.
Вы можете кастомизировать эту функцию. Она принимает `APIRoute` и возвращает строку.
Например, здесь берётся первый тег (скорее всего у вас один тег) и имя *операции пути* (имя функции).
Затем вы можете передать эту пользовательскую функцию в **FastAPI** через параметр `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Генерация TypeScript‑клиента с пользовательскими ID операций { #generate-a-typescript-client-with-custom-operation-ids }
Теперь, если снова сгенерировать клиент, вы увидите, что имена методов улучшились:
Как видите, теперь имена методов содержат тег, а затем имя функции; больше они не включают информацию из URL‑пути и HTTP‑операции.
### Предобработка спецификации OpenAPI для генератора клиента { #preprocess-the-openapi-specification-for-the-client-generator }
В сгенерированном коде всё ещё есть **дублирующаяся информация**.
Мы уже знаем, что этот метод относится к **items**, потому что это слово есть в `ItemsService` (взято из тега), но при этом имя тега всё ещё добавлено префиксом к имени метода. 😕
Скорее всего мы захотим оставить это в OpenAPI в целом, так как это гарантирует, что ID операций будут **уникальны**.
Но для сгенерированного клиента мы можем **модифицировать** ID операций OpenAPI непосредственно перед генерацией клиентов, чтобы сделать имена методов более приятными и **чистыми**.
Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
После этого ID операций будут переименованы с чего‑то вроде `items-get_items` просто в `get_items`, и генератор клиента сможет создавать более простые имена методов.
### Генерация TypeScript‑клиента с предобработанным OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
Так как конечный результат теперь в файле `openapi.json`, нужно обновить входное расположение:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
После генерации нового клиента у вас будут **чистые имена методов**, со всем **автозавершением**, **ошибками прямо в редакторе** и т.д.:
## Преимущества { #benefits }
При использовании автоматически сгенерированных клиентов вы получите **автозавершение** для:
* Методов.
* Данных запроса — в теле запроса, query‑параметрах и т.д.
* Данных ответа.
У вас также будут **ошибки прямо в редакторе** для всего.
И каждый раз, когда вы обновляете код бэкенда и **перегенерируете** фронтенд, в нём появятся новые *операции пути* как методы, старые будут удалены, а любые другие изменения отразятся в сгенерированном коде. 🤓
Это также означает, что если что‑то изменилось, это будет **отражено** в клиентском коде автоматически. И если вы **соберёте** клиент, он завершится с ошибкой, если где‑то есть **несоответствие** в используемых данных.
Таким образом, вы **обнаружите многие ошибки** очень рано в цикле разработки, вместо того чтобы ждать, когда ошибки проявятся у конечных пользователей в продакшн, и затем пытаться отладить, в чём проблема. ✨
================================================
FILE: docs/ru/docs/advanced/index.md
================================================
# Расширенное руководство пользователя { #advanced-user-guide }
## Дополнительные возможности { #additional-features }
Основное [Учебник - Руководство пользователя](../tutorial/index.md) должно быть достаточно, чтобы познакомить вас со всеми основными функциями **FastAPI**.
В следующих разделах вы увидите другие варианты, конфигурации и дополнительные возможности.
/// tip | Совет
Следующие разделы **не обязательно являются "продвинутыми"**.
И вполне возможно, что для вашего случая использования решение находится в одном из них.
///
## Сначала прочитайте Учебник - Руководство пользователя { #read-the-tutorial-first }
Вы все еще можете использовать большинство функций **FastAPI** со знаниями из [Учебник - Руководство пользователя](../tutorial/index.md).
И следующие разделы предполагают, что вы уже прочитали его, и предполагают, что вы знаете эти основные идеи.
================================================
FILE: docs/ru/docs/advanced/json-base64-bytes.md
================================================
# JSON с байтами в Base64 { #json-with-bytes-as-base64 }
Если вашему приложению нужно принимать и отправлять JSON-данные, но при этом необходимо включать в них бинарные данные, вы можете кодировать их в base64.
## Base64 и файлы { #base64-vs-files }
Сначала рассмотрите возможность использовать [Файлы в запросе](../tutorial/request-files.md) для загрузки бинарных данных и [Пользовательский HTTP-ответ — FileResponse](./custom-response.md#fileresponse--fileresponse-) для отправки бинарных данных вместо кодирования их в JSON.
JSON может содержать только строки в кодировке UTF-8, поэтому он не может содержать «сырые» байты.
Base64 может кодировать бинарные данные в строки, но для этого используется больше символов, чем в исходных бинарных данных, поэтому обычно это менее эффективно, чем обычные файлы.
Используйте base64 только если вам действительно нужно включать бинарные данные в JSON и вы не можете использовать файлы для этого.
## Pydantic `bytes` { #pydantic-bytes }
Вы можете объявить Pydantic-модель с полями `bytes`, а затем использовать `val_json_bytes` в конфиге модели, чтобы указать использовать base64 для валидации входящих JSON-данных; как часть этой валидации строка base64 будет декодирована в байты.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Если вы откроете `/docs`, вы увидите, что поле `data` ожидает байты, закодированные в base64:
Вы можете отправить такой HTTP-запрос:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Совет
`aGVsbG8=` — это base64-кодирование строки `hello`.
///
Затем Pydantic декодирует строку base64 и передаст вам исходные байты в поле `data` модели.
Вы получите такой HTTP-ответ:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` для выходных данных { #pydantic-bytes-for-output-data }
Вы также можете использовать поля `bytes` с `ser_json_bytes` в конфиге модели для выходных данных, и Pydantic будет сериализовать байты в base64 при формировании JSON-ответа.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` для входных и выходных данных { #pydantic-bytes-for-input-and-output-data }
И, конечно, вы можете использовать одну и ту же модель, настроенную на использование base64, чтобы обрабатывать и входящие данные (валидация) с `val_json_bytes`, и исходящие данные (сериализация) с `ser_json_bytes` при приеме и отправке JSON-данных.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/ru/docs/advanced/middleware.md
================================================
# Расширенное использование middleware { #advanced-middleware }
В основном руководстве вы читали, как добавить [пользовательское middleware](../tutorial/middleware.md) в ваше приложение.
А затем — как работать с [CORS с помощью `CORSMiddleware`](../tutorial/cors.md).
В этом разделе посмотрим, как использовать другие middleware.
## Добавление ASGI middleware { #adding-asgi-middlewares }
Так как **FastAPI** основан на Starlette и реализует спецификацию ASGI, вы можете использовать любое ASGI middleware.
Middleware не обязательно должно быть сделано специально для FastAPI или Starlette — достаточно, чтобы оно соответствовало спецификации ASGI.
В общем случае ASGI middleware — это классы, которые ожидают получить ASGI‑приложение первым аргументом.
Поэтому в документации к сторонним ASGI middleware, скорее всего, вы увидите что‑то вроде:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Но FastAPI (точнее, Starlette) предоставляет более простой способ, который гарантирует корректную обработку внутренних ошибок сервера и корректную работу пользовательских обработчиков исключений.
Для этого используйте `app.add_middleware()` (как в примере с CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` принимает класс middleware в качестве первого аргумента и любые дополнительные аргументы, которые будут переданы этому middleware.
## Встроенные middleware { #integrated-middlewares }
**FastAPI** включает несколько middleware для распространённых сценариев. Ниже рассмотрим, как их использовать.
/// note | Технические детали
В следующих примерах вы также можете использовать `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** предоставляет несколько middleware в `fastapi.middleware` для удобства разработчика. Но большинство доступных middleware приходит напрямую из Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Гарантирует, что все входящие запросы должны использовать либо `https`, либо `wss`.
Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Поддерживаются следующие аргументы:
- `allowed_hosts` — список доменных имён, которые следует разрешить как имена хостов. Подстановки вида `*.example.com` поддерживаются для сопоставления поддоменов. Чтобы разрешить любой хост, используйте либо `allowed_hosts=["*"]`, либо не добавляйте это middleware.
- `www_redirect` — если установлено в True, запросы к не‑www версиям разрешённых хостов будут перенаправляться на их www‑аналоги. По умолчанию — `True`.
Если входящий запрос не проходит валидацию, будет отправлен ответ `400`.
## `GZipMiddleware` { #gzipmiddleware }
Обрабатывает GZip‑ответы для любых запросов, которые включают `"gzip"` в заголовке `Accept-Encoding`.
Это middleware обрабатывает как обычные, так и потоковые ответы.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Поддерживаются следующие аргументы:
- `minimum_size` — не сжимать GZip‑ом ответы, размер которых меньше этого минимального значения в байтах. По умолчанию — `500`.
- `compresslevel` — уровень GZip‑сжатия. Целое число от 1 до 9. По умолчанию — `9`. Более низкое значение — быстрее сжатие, но больший размер файла; более высокое значение — более медленное сжатие, но меньший размер файла.
## Другие middleware { #other-middlewares }
Существует много других ASGI middleware.
Например:
- [`ProxyHeadersMiddleware` от Uvicorn](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
- [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Чтобы увидеть другие доступные middleware, посмотрите [документацию по middleware в Starlette](https://www.starlette.dev/middleware/) и [список ASGI Awesome](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/ru/docs/advanced/openapi-callbacks.md
================================================
# Обратные вызовы в OpenAPI { #openapi-callbacks }
Вы можете создать API с *операцией пути* (обработчиком пути), которая будет инициировать HTTP-запрос к *внешнему API*, созданному кем-то другим (скорее всего тем же разработчиком, который будет использовать ваш API).
Процесс, происходящий, когда ваше приложение API обращается к *внешнему API*, называется «callback» (обратный вызов). Программное обеспечение, написанное внешним разработчиком, отправляет HTTP-запрос вашему API, а затем ваш API выполняет обратный вызов, отправляя HTTP-запрос во *внешний API* (который, вероятно, тоже создал тот же разработчик).
В этом случае вам может понадобиться задокументировать, как должно выглядеть это внешнее API: какую *операцию пути* оно должно иметь, какое тело запроса ожидать, какой ответ возвращать и т.д.
## Приложение с обратными вызовами { #an-app-with-callbacks }
Давайте рассмотрим это на примере.
Представьте, что вы разрабатываете приложение, позволяющее создавать счета.
Эти счета будут иметь `id`, `title` (необязательный), `customer` и `total`.
Пользователь вашего API (внешний разработчик) создаст счет в вашем API с помощью POST-запроса.
Затем ваш API (предположим) сделает следующее:
* Отправит счет клиенту внешнего разработчика.
* Получит оплату.
* Отправит уведомление обратно пользователю API (внешнему разработчику).
* Это будет сделано отправкой POST-запроса (из *вашего API*) в *внешний API*, предоставленный этим внешним разработчиком (это и есть «callback»).
## Обычное приложение **FastAPI** { #the-normal-fastapi-app }
Сначала посмотрим, как будет выглядеть обычное приложение API до добавления обратного вызова.
В нём будет *операция пути*, которая получит тело запроса `Invoice`, и query-параметр `callback_url`, содержащий URL для обратного вызова.
Эта часть вполне обычна, большая часть кода вам уже знакома:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Совет
Query-параметр `callback_url` использует тип Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).
///
Единственное новое — это `callbacks=invoices_callback_router.routes` в качестве аргумента *декоратора операции пути*. Далее разберёмся, что это такое.
## Документирование обратного вызова { #documenting-the-callback }
Реальный код обратного вызова будет сильно зависеть от вашего приложения API.
И, вероятно, он будет заметно отличаться от одного приложения к другому.
Это могут быть буквально одна-две строки кода, например:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Но, возможно, самая важная часть обратного вызова — это убедиться, что пользователь вашего API (внешний разработчик) правильно реализует *внешний API* в соответствии с данными, которые *ваш API* будет отправлять в теле запроса обратного вызова и т.п.
Поэтому далее мы добавим код, документирующий, как должен выглядеть этот *внешний API*, чтобы получать обратный вызов от *вашего API*.
Эта документация отобразится в Swagger UI по адресу `/docs` в вашем API и позволит внешним разработчикам понять, как построить *внешний API*.
В этом примере сам обратный вызов не реализуется (это может быть всего одна строка кода), реализуется только часть с документацией.
/// tip | Совет
Сам обратный вызов — это всего лишь HTTP-запрос.
Реализуя обратный вызов, вы можете использовать, например, [HTTPX](https://www.python-httpx.org) или [Requests](https://requests.readthedocs.io/).
///
## Напишите код документации обратного вызова { #write-the-callback-documentation-code }
Этот код не будет выполняться в вашем приложении, он нужен только для *документирования* того, как должен выглядеть *внешний API*.
Но вы уже знаете, как легко получить автоматическую документацию для API с **FastAPI**.
Мы используем те же знания, чтобы задокументировать, как должен выглядеть *внешний API*... создав *операции пути*, которые внешний API должен реализовать (те, которые ваш API будет вызывать).
/// tip | Совет
Когда вы пишете код для документирования обратного вызова, полезно представить, что вы — тот самый *внешний разработчик*. И что вы сейчас реализуете *внешний API*, а не *свой API*.
Временное принятие этой точки зрения (внешнего разработчика) поможет интуитивно понять, куда поместить параметры, какую Pydantic-модель использовать для тела запроса, для ответа и т.д. во *внешнем API*.
///
### Создайте `APIRouter` для обратного вызова { #create-a-callback-apirouter }
Сначала создайте новый `APIRouter`, который будет содержать один или несколько обратных вызовов.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Создайте *операцию пути* для обратного вызова { #create-the-callback-path-operation }
Чтобы создать *операцию пути* для обратного вызова, используйте тот же `APIRouter`, который вы создали выше.
Она должна выглядеть как обычная *операция пути* FastAPI:
* Вероятно, в ней должно быть объявление тела запроса, например `body: InvoiceEvent`.
* А также может быть объявление модели ответа, например `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Есть 2 основных отличия от обычной *операции пути*:
* Ей не нужен реальный код, потому что ваше приложение никогда не будет вызывать эту функцию. Она используется только для документирования *внешнего API*. Поэтому в функции может быть просто `pass`.
* *Путь* может содержать [выражение OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (подробнее ниже), где можно использовать переменные с параметрами и части исходного HTTP-запроса, отправленного *вашему API*.
### Выражение пути для обратного вызова { #the-callback-path-expression }
*Путь* обратного вызова может содержать [выражение OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression), которое может включать части исходного запроса, отправленного *вашему API*.
В нашем случае это `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Итак, если пользователь вашего API (внешний разработчик) отправляет HTTP-запрос вашему API по адресу:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
с телом JSON:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
то *ваш API* обработает счёт и, в какой-то момент позже, отправит запрос обратного вызова на `callback_url` (*внешний API*):
```
https://www.external.org/events/invoices/2expen51ve
```
с телом JSON примерно такого вида:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
и будет ожидать от *внешнего API* ответ с телом JSON вида:
```JSON
{
"ok": true
}
```
/// tip | Совет
Обратите внимание, что используемый URL обратного вызова содержит URL, полученный как query-параметр в `callback_url` (`https://www.external.org/events`), а также `id` счёта из тела JSON (`2expen51ve`).
///
### Подключите маршрутизатор обратного вызова { #add-the-callback-router }
К этому моменту у вас есть необходимые *операции пути* обратного вызова (те, которые *внешний разработчик* должен реализовать во *внешнем API*) в созданном выше маршрутизаторе обратных вызовов.
Теперь используйте параметр `callbacks` в *декораторе операции пути вашего API*, чтобы передать атрибут `.routes` (это, по сути, просто `list` маршрутов/*операций пути*) из этого маршрутизатора обратных вызовов:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Совет
Обратите внимание, что вы передаёте не сам маршрутизатор (`invoices_callback_router`) в `callback=`, а его атрибут `.routes`, то есть `invoices_callback_router.routes`.
///
### Проверьте документацию { #check-the-docs }
Теперь вы можете запустить приложение и перейти по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите документацию, включающую раздел «Callbacks» для вашей *операции пути*, который показывает, как должен выглядеть *внешний API*:
================================================
FILE: docs/ru/docs/advanced/openapi-webhooks.md
================================================
# Вебхуки OpenAPI { #openapi-webhooks }
Бывают случаи, когда вы хотите сообщить пользователям вашего API, что ваше приложение может вызвать их приложение (отправив HTTP-запрос) с некоторыми данными, обычно чтобы уведомить о каком-то событии.
Это означает, что вместо обычного процесса, когда пользователи отправляют запросы вашему API, ваш API (или ваше приложение) может отправлять запросы в их систему (в их API, их приложение).
Обычно это называется вебхуком.
## Шаги вебхуков { #webhooks-steps }
Обычно процесс таков: вы определяете в своем коде, какое сообщение вы будете отправлять, то есть тело запроса.
Вы также определяете, в какие моменты (при каких событиях) ваше приложение будет отправлять эти запросы (события).
А ваши пользователи каким-то образом (например, в веб‑панели) указывают URL-адрес, на который ваше приложение должно отправлять эти запросы.
Вся логика регистрации URL-адресов для вебхуков и код, который реально отправляет эти запросы, целиком на вашей стороне. Вы пишете это так, как вам нужно, в своем собственном коде.
## Документирование вебхуков с помощью **FastAPI** и OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
С **FastAPI**, используя OpenAPI, вы можете определить имена этих вебхуков, типы HTTP-операций, которые ваше приложение может отправлять (например, `POST`, `PUT` и т.д.), а также тела запросов, которые ваше приложение будет отправлять.
Это значительно упростит вашим пользователям реализацию их API для приема ваших вебхук-запросов; возможно, они даже смогут автоматически сгенерировать часть кода своего API.
/// info | Информация
Вебхуки доступны в OpenAPI 3.1.0 и выше, поддерживаются в FastAPI `0.99.0` и новее.
///
## Приложение с вебхуками { #an-app-with-webhooks }
При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
/// info | Информация
Объект `app.webhooks` на самом деле — это обычный `APIRouter`, тот же тип, который вы используете при структурировании приложения по нескольким файлам.
///
Обратите внимание: в случае с вебхуками вы на самом деле не объявляете путь (например, `/items/`), передаваемый туда текст — это лишь идентификатор вебхука (имя события). Например, в `@app.webhooks.post("new-subscription")` имя вебхука — `new-subscription`.
Это связано с тем, что предполагается: фактический URL‑путь, по которому они хотят получать запрос вебхука, ваши пользователи укажут каким-то другим образом (например, в веб‑панели).
### Посмотрите документацию { #check-the-docs }
Теперь вы можете запустить приложение и перейти по ссылке [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите, что в документации есть обычные операции пути, а также появились вебхуки:
================================================
FILE: docs/ru/docs/advanced/path-operation-advanced-configuration.md
================================================
# Расширенная конфигурация операций пути { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | Предупреждение
Если вы не «эксперт» по OpenAPI, скорее всего, это вам не нужно.
///
Вы можете задать OpenAPI `operationId`, который будет использоваться в вашей *операции пути*, с помощью параметра `operation_id`.
Нужно убедиться, что он уникален для каждой операции.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Использование имени *функции-обработчика пути* как operationId { #using-the-path-operation-function-name-as-the-operationid }
Если вы хотите использовать имена функций ваших API в качестве `operationId`, вы можете пройти по всем из них и переопределить `operation_id` каждой *операции пути* с помощью их `APIRoute.name`.
Делать это следует после добавления всех *операций пути*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Совет
Если вы вызываете `app.openapi()` вручную, обновите `operationId` до этого.
///
/// warning | Предупреждение
Если вы делаете это, убедитесь, что каждая из ваших *функций-обработчиков пути* имеет уникальное имя.
Даже если они находятся в разных модулях (файлах Python).
///
## Исключить из OpenAPI { #exclude-from-openapi }
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматических систем документации), используйте параметр `include_in_schema` и установите его в `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Расширенное описание из docstring { #advanced-description-from-docstring }
Вы можете ограничить количество строк из docstring *функции-обработчика пути*, используемых для OpenAPI.
Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать текст, используемый для OpenAPI, в этой точке.
Это не отобразится в документации, но другие инструменты (например, Sphinx) смогут использовать остальное.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Дополнительные ответы { #additional-responses }
Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*.
Это определяет метаданные об основном HTTP-ответе *операции пути*.
Также можно объявлять дополнительные ответы с их моделями, статус-кодами и т.д.
В документации есть целая глава об этом — [Дополнительные ответы в OpenAPI](additional-responses.md).
## Дополнительные данные OpenAPI { #openapi-extra }
Когда вы объявляете *операцию пути* в своём приложении, **FastAPI** автоматически генерирует соответствующие метаданные об этой *операции пути* для включения в схему OpenAPI.
/// note | Технические детали
В спецификации OpenAPI это называется [Объект операции](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
Он содержит всю информацию об *операции пути* и используется для генерации автоматической документации.
Там есть `tags`, `parameters`, `requestBody`, `responses` и т.д.
Эта специфичная для *операции пути* схема OpenAPI обычно генерируется автоматически **FastAPI**, но вы также можете её расширить.
/// tip | Совет
Это низкоуровневая возможность расширения.
Если вам нужно лишь объявить дополнительные ответы, удобнее сделать это через [Дополнительные ответы в OpenAPI](additional-responses.md).
///
Вы можете расширить схему OpenAPI для *операции пути* с помощью параметра `openapi_extra`.
### Расширения OpenAPI { #openapi-extensions }
`openapi_extra` может пригодиться, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Если вы откроете автоматическую документацию API, ваше расширение появится внизу страницы конкретной *операции пути*.
И если вы посмотрите на итоговый OpenAPI (по адресу `/openapi.json` вашего API), вы также увидите своё расширение в составе описания соответствующей *операции пути*:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Пользовательская схема OpenAPI для *операции пути* { #custom-openapi-path-operation-schema }
Словарь в `openapi_extra` будет глубоко объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*.
Таким образом, вы можете добавить дополнительные данные к автоматически сгенерированной схеме.
Например, вы можете решить читать и валидировать HTTP-запрос своим кодом, не используя автоматические возможности FastAPI и Pydantic, но при этом захотите описать HTTP-запрос в схеме OpenAPI.
Это можно сделать с помощью `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не распарсено как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
### Пользовательский тип содержимого в OpenAPI { #custom-openapi-content-type }
Используя тот же приём, вы можете воспользоваться Pydantic-моделью, чтобы определить JSON Schema, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*.
И вы можете сделать это, даже если тип данных в HTTP-запросе — не JSON.
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого HTTP-запроса как YAML, а не JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML.
Затем мы работаем с HTTP-запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку HTTP-запроса как JSON.
А затем в нашем коде мы напрямую парсим это содержимое YAML и снова используем ту же Pydantic-модель, чтобы валидировать YAML-содержимое:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Совет
Здесь мы переиспользуем ту же Pydantic-модель.
Но аналогично мы могли бы валидировать данные и каким-то другим способом.
///
================================================
FILE: docs/ru/docs/advanced/response-change-status-code.md
================================================
# Response - Изменение статус-кода { #response-change-status-code }
Вы, вероятно, уже читали о том, что можно установить [статус-код ответа по умолчанию](../tutorial/response-status-code.md).
Но в некоторых случаях нужно вернуть другой статус-код, отличный от значения по умолчанию.
## Пример использования { #use-case }
Например, представьте, что вы хотите по умолчанию возвращать HTTP статус-код «OK» `200`.
Но если данные не существовали, вы хотите создать их и вернуть HTTP статус-код «CREATED» `201`.
При этом вы всё ещё хотите иметь возможность фильтровать и преобразовывать возвращаемые данные с помощью `response_model`.
Для таких случаев вы можете использовать параметр `Response`.
## Использование параметра `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей *функции обработки пути* (как и для cookies и HTTP-заголовков).
И затем вы можете установить `status_code` в этом *временном* объекте ответа.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.).
И если вы объявили `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
**FastAPI** будет использовать этот *временный* ответ для извлечения статус-кода (а также cookies и HTTP-заголовков) и поместит их в финальный ответ, который содержит возвращаемое вами значение, отфильтрованное любым `response_model`.
Вы также можете объявить параметр `Response` в зависимостях и установить в них статус-код. Но помните, что последнее установленное значение будет иметь приоритет.
================================================
FILE: docs/ru/docs/advanced/response-cookies.md
================================================
# Cookies в ответе { #response-cookies }
## Использование параметра `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути.
Затем установить cookies в этом временном объекте ответа.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
Если вы указали `response_model`, он всё равно будет использоваться для фильтрации и преобразования возвращаемого объекта.
**FastAPI** извлечет cookies (а также HTTP-заголовки и статус-код) из временного ответа и включит их в окончательный ответ, содержащий ваше возвращаемое значение, отфильтрованное через `response_model`.
Вы также можете объявить параметр типа `Response` в зависимостях и устанавливать cookies (и HTTP-заголовки) там.
## Возвращение `Response` напрямую { #return-a-response-directly }
Вы также можете установить Cookies, если возвращаете `Response` напрямую в вашем коде.
Для этого создайте объект `Response`, как описано в разделе [Возвращение ответа напрямую](response-directly.md).
Затем установите cookies и верните этот объект:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip | Совет
Имейте в виду, что если вы возвращаете ответ напрямую, вместо использования параметра `Response`, FastAPI вернёт его напрямую.
Убедитесь, что ваши данные имеют корректный тип. Например, они должны быть совместимы с JSON, если вы возвращаете `JSONResponse`.
Также убедитесь, что вы не отправляете данные, которые должны были быть отфильтрованы через `response_model`.
///
### Дополнительная информация { #more-info }
/// note | Технические детали
Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет `fastapi.responses`, которые являются теми же объектами, что и `starlette.responses`, просто для удобства. Однако большинство доступных типов ответов поступает непосредственно из **Starlette**.
И так как `Response` часто используется для установки HTTP-заголовков и cookies, **FastAPI** также предоставляет его как `fastapi.Response`.
///
Чтобы увидеть все доступные параметры и настройки, ознакомьтесь с [документацией Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/ru/docs/advanced/response-directly.md
================================================
# Возврат ответа напрямую { #return-a-response-directly }
Когда вы создаёте **FastAPI** *операцию пути*, вы можете возвращать из неё любые данные: `dict`, `list`, Pydantic-модель, модель базы данных и т.д.
Если вы объявите [Модель ответа](../tutorial/response-model.md), FastAPI будет использовать её для сериализации данных в JSON с помощью Pydantic.
Если вы не объявите модель ответа, FastAPI использует `jsonable_encoder`, как описано в [JSON кодировщик](../tutorial/encoder.md), и поместит результат в `JSONResponse`.
Также вы можете создать `JSONResponse` напрямую и вернуть его.
/// tip | Подсказка
Обычно вы получите значительно лучшую производительность, если будете использовать [Модель ответа](../tutorial/response-model.md), а не возвращать `JSONResponse` напрямую, так как в этом случае сериализация данных с помощью Pydantic происходит на стороне Rust.
///
## Возврат `Response` { #return-a-response }
Вы можете возвращать `Response` или любой его подкласс.
/// info | Информация
`JSONResponse` сам по себе является подклассом `Response`.
///
И когда вы возвращаете `Response`, **FastAPI** передаст его напрямую.
Это не приведет к преобразованию данных с помощью Pydantic-моделей, содержимое не будет преобразовано в какой-либо тип и т.д.
Это даёт вам большую гибкость. Вы можете возвращать любые типы данных, переопределять любые объявления или валидацию данных и т.д.
Это также накладывает на вас ответственность. Вам нужно удостовериться, что возвращаемые данные корректны, в правильном формате, что их можно сериализовать и т.д.
## Использование `jsonable_encoder` в `Response` { #using-the-jsonable-encoder-in-a-response }
Поскольку **FastAPI** не изменяет объект `Response`, который вы возвращаете, вы должны убедиться, что его содержимое готово к отправке.
Например, вы не можете поместить Pydantic-модель в `JSONResponse`, не преобразовав её сначала в `dict` с помощью преобразования всех типов данных (таких как `datetime`, `UUID` и т.д.) в совместимые с JSON типы.
В таких случаях вы можете использовать `jsonable_encoder` для преобразования данных перед передачей их в ответ:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Технические детали
Вы также можете использовать `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет `starlette.responses` через `fastapi.responses` просто для вашего удобства, как разработчика. Но большинство доступных Response-классов поступают напрямую из Starlette.
///
## Возврат пользовательского `Response` { #returning-a-custom-response }
Пример выше показывает все необходимые части, но он пока не очень полезен, так как вы могли бы просто вернуть `item` напрямую, и **FastAPI** поместил бы его в `JSONResponse`, преобразовав в `dict` и т.д. Всё это происходит по умолчанию.
Теперь давайте посмотрим, как можно использовать это для возврата пользовательского ответа.
Допустим, вы хотите вернуть ответ в формате [XML](https://en.wikipedia.org/wiki/XML).
Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Как работает модель ответа { #how-a-response-model-works }
Когда вы объявляете [Модель ответа - возвращаемый тип](../tutorial/response-model.md) в операции пути, **FastAPI** будет использовать её для сериализации данных в JSON с помощью Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Поскольку это происходит на стороне Rust, производительность будет значительно выше, чем если бы это выполнялось обычным Python и классом `JSONResponse`.
При использовании `response_model` или возвращаемого типа FastAPI не будет использовать `jsonable_encoder` для преобразования данных (что было бы медленнее) и не будет использовать класс `JSONResponse`.
Вместо этого он берёт JSON-байты, сгенерированные Pydantic с использованием модели ответа (или возвращаемого типа), и возвращает `Response` с правильным типом содержимого для JSON (`application/json`) напрямую.
## Примечания { #notes }
Когда вы возвращаете объект `Response` напрямую, его данные не валидируются, не преобразуются (не сериализуются) и не документируются автоматически.
Но вы всё равно можете задокументировать это, как описано в [Дополнительные ответы в OpenAPI](additional-responses.md).
В следующих разделах вы увидите, как использовать/объявлять такие кастомные `Response`, при этом сохраняя автоматическое преобразование данных, документацию и т.д.
================================================
FILE: docs/ru/docs/advanced/response-headers.md
================================================
# HTTP-заголовки ответа { #response-headers }
## Использовать параметр `Response` { #use-a-response-parameter }
Вы можете объявить параметр типа `Response` в вашей функции-обработчике пути (как можно сделать и для cookie).
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
И, если вы объявили `response_model`, он всё равно будет использован для фильтрации и преобразования возвращённого объекта.
**FastAPI** использует этот *временный* ответ, чтобы извлечь HTTP-заголовки (а также cookie и статус-код) и поместит их в финальный HTTP-ответ, который содержит возвращённое вами значение, отфильтрованное согласно `response_model`.
Вы также можете объявлять параметр `Response` в зависимостях и устанавливать в них заголовки (и cookie).
## Вернуть `Response` напрямую { #return-a-response-directly }
Вы также можете добавить HTTP-заголовки, когда возвращаете `Response` напрямую.
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md), и передайте заголовки как дополнительный параметр:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Технические детали
Вы также можете использовать `from starlette.responses import Response` или `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет те же самые `starlette.responses` как `fastapi.responses` — для вашего удобства как разработчика. Но большинство доступных классов ответов поступают напрямую из Starlette.
И поскольку `Response` часто используется для установки заголовков и cookie, **FastAPI** также предоставляет его как `fastapi.Response`.
///
## Пользовательские HTTP-заголовки { #custom-headers }
Помните, что собственные проприетарные заголовки можно добавлять, [используя префикс `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Но если у вас есть пользовательские заголовки, которые вы хотите показывать клиенту в браузере, вам нужно добавить их в настройки CORS (подробнее см. в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), используя параметр `expose_headers`, описанный в [документации Starlette по CORS](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/ru/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
Для самых простых случаев можно использовать HTTP Basic Auth.
При HTTP Basic Auth приложение ожидает HTTP-заголовок, который содержит имя пользователя и пароль.
Если его нет, возвращается ошибка HTTP 401 «Unauthorized».
Также возвращается заголовок `WWW-Authenticate` со значением `Basic` и необязательным параметром `realm`.
Это говорит браузеру показать встроенное окно запроса имени пользователя и пароля.
Затем, когда вы вводите эти данные, браузер автоматически отправляет их в заголовке.
## Простой HTTP Basic Auth { #simple-http-basic-auth }
* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
* Создайте «схему» `security` с помощью `HTTPBasic`.
* Используйте эту `security` как зависимость в вашей *операции пути*.
* Она возвращает объект типа `HTTPBasicCredentials`:
* Он содержит отправленные `username` и `password`.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Когда вы впервые откроете URL (или нажмёте кнопку «Execute» в документации), браузер попросит ввести имя пользователя и пароль:
## Проверка имени пользователя { #check-the-username }
Вот более полный пример.
Используйте зависимость, чтобы проверить, корректны ли имя пользователя и пароль.
Для этого используйте стандартный модуль Python [`secrets`](https://docs.python.org/3/library/secrets.html) для проверки имени пользователя и пароля.
`secrets.compare_digest()` должен получать `bytes` или `str`, который содержит только символы ASCII (английские символы). Это значит, что он не будет работать с символами вроде `á`, как в `Sebastián`.
Чтобы это обработать, сначала преобразуем `username` и `password` в `bytes`, закодировав их в UTF-8.
Затем можно использовать `secrets.compare_digest()`, чтобы убедиться, что `credentials.username` равен `"stanleyjobson"`, а `credentials.password` — `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Это было бы похоже на:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Вернуть ошибку
...
```
Но используя `secrets.compare_digest()`, вы защитите код от атак типа «тайминговая атака» (атака по времени).
### Тайминговые атаки { #timing-attacks }
Что такое «тайминговая атака»?
Представим, что злоумышленники пытаются угадать имя пользователя и пароль.
И они отправляют запрос с именем пользователя `johndoe` и паролем `love123`.
Тогда Python-код в вашем приложении будет эквивалентен чему-то вроде:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Но в момент, когда Python сравнит первую `j` в `johndoe` с первой `s` в `stanleyjobson`, он вернёт `False`, потому что уже ясно, что строки не совпадают, решив, что «нет смысла тратить ресурсы на сравнение остальных букв». И ваше приложение ответит «Неверное имя пользователя или пароль».
Затем злоумышленники попробуют имя пользователя `stanleyjobsox` и пароль `love123`.
И ваш код сделает что-то вроде:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Pythonу придётся сравнить весь общий префикс `stanleyjobso` в `stanleyjobsox` и `stanleyjobson`, прежде чем понять, что строки отличаются. Поэтому на ответ «Неверное имя пользователя или пароль» уйдёт на несколько микросекунд больше.
#### Время ответа помогает злоумышленникам { #the-time-to-answer-helps-the-attackers }
Замечая, что сервер прислал «Неверное имя пользователя или пароль» на несколько микросекунд позже, злоумышленники поймут, что какая-то часть была угадана — начальные буквы верны.
Тогда они могут попробовать снова, зная, что правильнее что-то ближе к `stanleyjobsox`, чем к `johndoe`.
#### «Профессиональная» атака { #a-professional-attack }
Конечно, злоумышленники не будут делать всё это вручную — они напишут программу, возможно, с тысячами или миллионами попыток в секунду. И будут подбирать по одной дополнительной верной букве за раз.
Так за минуты или часы они смогут угадать правильные имя пользователя и пароль — с «помощью» нашего приложения — используя лишь время, затраченное на ответ.
#### Исправление с помощью `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Но в нашем коде мы используем `secrets.compare_digest()`.
Вкратце: сравнение `stanleyjobsox` с `stanleyjobson` займёт столько же времени, сколько и сравнение `johndoe` с `stanleyjobson`. То же относится и к паролю.
Таким образом, используя `secrets.compare_digest()` в коде приложения, вы защитите его от всего этого класса атак на безопасность.
### Возврат ошибки { #return-the-error }
После того как обнаружено, что учётные данные некорректны, верните `HTTPException` со статус-кодом ответа 401 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/ru/docs/advanced/security/index.md
================================================
# Расширенная безопасность { #advanced-security }
## Дополнительные возможности { #additional-features }
Есть дополнительные возможности для работы с безопасностью помимо тех, что описаны в [Учебник — Руководство пользователя: Безопасность](../../tutorial/security/index.md).
/// tip | Совет
Следующие разделы не обязательно являются «продвинутыми».
И возможно, что решение для вашего варианта использования находится в одном из них.
///
## Сначала прочитайте руководство { #read-the-tutorial-first }
В следующих разделах предполагается, что вы уже прочитали основной [Учебник — Руководство пользователя: Безопасность](../../tutorial/security/index.md).
Все они основаны на тех же концепциях, но предоставляют дополнительные возможности.
================================================
FILE: docs/ru/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2 scopes { #oauth2-scopes }
Вы можете использовать OAuth2 scopes напрямую с **FastAPI** — они интегрированы и работают бесшовно.
Это позволит вам иметь более детальную систему разрешений по стандарту OAuth2, интегрированную в ваше OpenAPI‑приложение (и документацию API).
OAuth2 со scopes — это механизм, который используют многие крупные провайдеры аутентификации: Facebook, Google, GitHub, Microsoft, X (Twitter) и т.д. Они применяют его, чтобы предоставлять конкретные разрешения пользователям и приложениям.
Каждый раз, когда вы «входите через» Facebook, Google, GitHub, Microsoft, X (Twitter), это приложение использует OAuth2 со scopes.
В этом разделе вы увидите, как управлять аутентификацией и авторизацией с теми же OAuth2 scopes в вашем приложении на **FastAPI**.
/// warning | Предупреждение
Это более-менее продвинутый раздел. Если вы только начинаете, можете пропустить его.
Вам не обязательно нужны OAuth2 scopes — аутентификацию и авторизацию можно реализовать любым нужным вам способом.
Но OAuth2 со scopes можно красиво интегрировать в ваш API (через OpenAPI) и документацию API.
Так или иначе, вы все равно будете применять эти scopes или какие-то другие требования безопасности/авторизации, как вам нужно, в вашем коде.
Во многих случаях OAuth2 со scopes может быть избыточным.
Но если вы знаете, что это нужно, или вам просто интересно — продолжайте чтение.
///
## OAuth2 scopes и OpenAPI { #oauth2-scopes-and-openapi }
Спецификация OAuth2 определяет «scopes» как список строк, разделённых пробелами.
Содержимое каждой такой строки может иметь любой формат, но не должно содержать пробелов.
Эти scopes представляют «разрешения».
В OpenAPI (например, в документации API) можно определить «схемы безопасности» (security schemes).
Когда одна из таких схем безопасности использует OAuth2, вы также можете объявлять и использовать scopes.
Каждый «scope» — это просто строка (без пробелов).
Обычно они используются для объявления конкретных разрешений безопасности, например:
- `users:read` или `users:write` — распространённые примеры.
- `instagram_basic` используется Facebook / Instagram.
- `https://www.googleapis.com/auth/drive` используется Google.
/// info | Информация
В OAuth2 «scope» — это просто строка, объявляющая требуемое конкретное разрешение.
Неважно, есть ли там другие символы, такие как `:`, или это URL.
Эти детали зависят от реализации.
Для OAuth2 это просто строки.
///
## Взгляд издалека { #global-view }
Сначала быстро посмотрим, что изменилось по сравнению с примерами из основного раздела **Учебник - Руководство пользователя** — [OAuth2 с паролем (и хешированием), Bearer с JWT-токенами](../../tutorial/security/oauth2-jwt.md). Теперь — с использованием OAuth2 scopes:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Теперь рассмотрим эти изменения шаг за шагом.
## OAuth2 схема безопасности { #oauth2-security-scheme }
Первое изменение — мы объявляем схему безопасности OAuth2 с двумя доступными scopes: `me` и `items`.
Параметр `scopes` получает `dict`, где каждый scope — это ключ, а описание — значение:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Так как теперь мы объявляем эти scopes, они появятся в документации API при входе/авторизации.
И вы сможете выбрать, какие scopes вы хотите выдать доступ: `me` и `items`.
Это тот же механизм, когда вы даёте разрешения при входе через Facebook, Google, GitHub и т.д.:
## JWT-токены со scopes { #jwt-token-with-scopes }
Теперь измените операцию пути, выдающую токен, чтобы возвращать запрошенные scopes.
Мы всё ещё используем тот же `OAuth2PasswordRequestForm`. Он включает свойство `scopes` с `list` из `str` — каждый scope, полученный в запросе.
И мы возвращаем scopes как часть JWT‑токена.
/// danger | Опасность
Для простоты здесь мы просто добавляем полученные scopes прямо в токен.
Но в вашем приложении, в целях безопасности, следует убедиться, что вы добавляете только те scopes, которые пользователь действительно может иметь, или те, которые вы заранее определили.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Объявление scopes в *обработчиках путей* и зависимостях { #declare-scopes-in-path-operations-and-dependencies }
Теперь объявим, что операция пути для `/users/me/items/` требует scope `items`.
Для этого импортируем и используем `Security` из `fastapi`.
Вы можете использовать `Security` для объявления зависимостей (как `Depends`), но `Security` также принимает параметр `scopes` со списком scopes (строк).
В этом случае мы передаём функцию‑зависимость `get_current_active_user` в `Security` (точно так же, как сделали бы с `Depends`).
Но мы также передаём `list` scopes — в данном случае только один scope: `items` (их могло быть больше).
И функция‑зависимость `get_current_active_user` тоже может объявлять подзависимости не только через `Depends`, но и через `Security`, объявляя свою подзависимость (`get_current_user`) и дополнительные требования по scopes.
В данном случае требуется scope `me` (их также могло быть больше одного).
/// note | Примечание
Вам не обязательно добавлять разные scopes в разных местах.
Мы делаем это здесь, чтобы показать, как **FastAPI** обрабатывает scopes, объявленные на разных уровнях.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Технические детали
`Security` на самом деле является подклассом `Depends` и имеет всего один дополнительный параметр, который мы рассмотрим позже.
Но используя `Security` вместо `Depends`, **FastAPI** будет знать, что можно объявлять security scopes, использовать их внутри и документировать API в OpenAPI.
Однако когда вы импортируете `Query`, `Path`, `Depends`, `Security` и другие из `fastapi`, это на самом деле функции, возвращающие специальные классы.
///
## Использование `SecurityScopes` { #use-securityscopes }
Теперь обновим зависимость `get_current_user`.
Именно её используют зависимости выше.
Здесь мы используем ту же схему OAuth2, созданную ранее, объявляя её как зависимость: `oauth2_scheme`.
Поскольку у этой функции‑зависимости нет собственных требований по scopes, мы можем использовать `Depends` с `oauth2_scheme` — нам не нужно использовать `Security`, если не требуется указывать security scopes.
Мы также объявляем специальный параметр типа `SecurityScopes`, импортированный из `fastapi.security`.
Класс `SecurityScopes` похож на `Request` (через `Request` мы получали сам объект запроса).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Использование `scopes` { #use-the-scopes }
Параметр `security_scopes` будет типа `SecurityScopes`.
У него есть свойство `scopes` со списком, содержащим все scopes, требуемые им самим и всеми зависимостями, использующими его как подзависимость. То есть всеми «зависящими»… это может звучать запутанно, ниже есть дополнительное объяснение.
Объект `security_scopes` (класс `SecurityScopes`) также предоставляет атрибут `scope_str` — это одна строка с этими scopes, разделёнными пробелами (мы будем её использовать).
Мы создаём `HTTPException`, который можем переиспользовать (`raise`) в нескольких местах.
В этом исключении мы включаем требуемые scopes (если есть) в виде строки, разделённой пробелами (используя `scope_str`). Эту строку со scopes мы помещаем в HTTP‑заголовок `WWW-Authenticate` (это часть спецификации).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Проверка `username` и формата данных { #verify-the-username-and-data-shape }
Мы проверяем, что получили `username`, и извлекаем scopes.
Затем валидируем эти данные с помощью Pydantic‑модели (перехватывая исключение `ValidationError`), и если возникает ошибка при чтении JWT‑токена или при валидации данных с Pydantic, мы вызываем `HTTPException`, созданное ранее.
Для этого мы обновляем Pydantic‑модель `TokenData`, добавляя новое свойство `scopes`.
Валидируя данные с помощью Pydantic, мы можем удостовериться, что у нас, например, именно `list` из `str` со scopes и `str` с `username`.
А не, скажем, `dict` или что‑то ещё — ведь это могло бы где‑то позже сломать приложение и создать риск для безопасности.
Мы также проверяем, что существует пользователь с таким именем, и если нет — вызываем то же исключение, созданное ранее.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Проверка `scopes` { #verify-the-scopes }
Теперь проверяем, что все требуемые scopes — этой зависимостью и всеми зависящими (включая операции пути) — присутствуют среди scopes, предоставленных в полученном токене, иначе вызываем `HTTPException`.
Для этого используем `security_scopes.scopes`, содержащий `list` со всеми этими scopes как `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Дерево зависимостей и scopes { #dependency-tree-and-scopes }
Ещё раз рассмотрим дерево зависимостей и scopes.
Так как у зависимости `get_current_active_user` есть подзависимость `get_current_user`, scope `"me"`, объявленный в `get_current_active_user`, будет включён в список требуемых scopes в `security_scopes.scopes`, передаваемый в `get_current_user`.
Сама операция пути тоже объявляет scope — `"items"`, поэтому он также будет в списке `security_scopes.scopes`, передаваемом в `get_current_user`.
Иерархия зависимостей и scopes выглядит так:
- Операция пути `read_own_items`:
- Запрашивает scopes `["items"]` с зависимостью:
- `get_current_active_user`:
- Функция‑зависимость `get_current_active_user`:
- Запрашивает scopes `["me"]` с зависимостью:
- `get_current_user`:
- Функция‑зависимость `get_current_user`:
- Собственных scopes не запрашивает.
- Имеет зависимость, использующую `oauth2_scheme`.
- Имеет параметр `security_scopes` типа `SecurityScopes`:
- Этот параметр `security_scopes` имеет свойство `scopes` с `list`, содержащим все объявленные выше scopes, то есть:
- `security_scopes.scopes` будет содержать `["me", "items"]` для операции пути `read_own_items`.
- `security_scopes.scopes` будет содержать `["me"]` для операции пути `read_users_me`, потому что он объявлен в зависимости `get_current_active_user`.
- `security_scopes.scopes` будет содержать `[]` (ничего) для операции пути `read_system_status`, потому что там не объявлялся `Security` со `scopes`, и его зависимость `get_current_user` тоже не объявляет `scopes`.
/// tip | Совет
Важный и «магический» момент здесь в том, что `get_current_user` будет иметь разный список `scopes` для проверки для каждой операции пути.
Всё это зависит от `scopes`, объявленных в каждой операции пути и в каждой зависимости в дереве зависимостей конкретной операции пути.
///
## Больше деталей о `SecurityScopes` { #more-details-about-securityscopes }
Вы можете использовать `SecurityScopes` в любой точке и в нескольких местах — необязательно в «корневой» зависимости.
Он всегда будет содержать security scopes, объявленные в текущих зависимостях `Security`, и всеми зависящими — для этой конкретной операции пути и этого конкретного дерева зависимостей.
Поскольку `SecurityScopes` будет содержать все scopes, объявленные зависящими, вы можете использовать его, чтобы централизованно проверять наличие требуемых scopes в токене в одной функции‑зависимости, а затем объявлять разные требования по scopes в разных операциях пути.
Они будут проверяться независимо для каждой операции пути.
## Проверим это { #check-it }
Откройте документацию API — вы сможете аутентифицироваться и указать, какие scopes вы хотите авторизовать.
Если вы не выберете ни один scope, вы будете «аутентифицированы», но при попытке доступа к `/users/me/` или `/users/me/items/` получите ошибку о недостаточных разрешениях. При этом доступ к `/status/` будет возможен.
Если вы выберете scope `me`, но не `items`, вы сможете получить доступ к `/users/me/`, но не к `/users/me/items/`.
Так и будет происходить со сторонним приложением, которое попытается обратиться к одной из этих операций пути с токеном, предоставленным пользователем, — в зависимости от того, сколько разрешений пользователь дал приложению.
## О сторонних интеграциях { #about-third-party-integrations }
В этом примере мы используем OAuth2 «password flow» (аутентификация по паролю).
Это уместно, когда мы входим в наше собственное приложение, вероятно, с нашим собственным фронтендом.
Мы можем ему доверять при получении `username` и `password`, потому что он под нашим контролем.
Но если вы создаёте OAuth2‑приложение, к которому будут подключаться другие (т.е. вы строите провайдера аутентификации наподобие Facebook, Google, GitHub и т.п.), вам следует использовать один из других «flows».
Самый распространённый — «implicit flow».
Самый безопасный — «code flow», но он сложнее в реализации, так как требует больше шагов. Из‑за сложности многие провайдеры в итоге рекомендуют «implicit flow».
/// note | Примечание
Часто каждый провайдер аутентификации называет свои «flows» по‑разному — как часть бренда.
Но в итоге они реализуют один и тот же стандарт OAuth2.
///
**FastAPI** включает утилиты для всех этих OAuth2‑flows в `fastapi.security.oauth2`.
## `Security` в параметре `dependencies` декоратора { #security-in-decorator-dependencies }
Точно так же, как вы можете определить `list` из `Depends` в параметре `dependencies` декоратора (см. [Зависимости в декораторах операции пути](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), вы можете использовать там и `Security` со `scopes`.
================================================
FILE: docs/ru/docs/advanced/settings.md
================================================
# Настройки и переменные окружения { #settings-and-environment-variables }
Во многих случаях вашему приложению могут понадобиться внешние настройки или конфигурации, например секретные ключи, учетные данные для базы данных, учетные данные для email‑сервисов и т.д.
Большинство таких настроек являются изменяемыми (могут меняться), например URL базы данных. И многие из них могут быть «чувствительными», например секреты.
По этой причине обычно их передают через переменные окружения, которые считываются приложением.
/// tip | Совет
Чтобы понять, что такое переменные окружения, вы можете прочитать [Переменные окружения](../environment-variables.md).
///
## Типы и валидация { #types-and-validation }
Переменные окружения могут содержать только текстовые строки, так как они внешние по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с разными операционными системами, такими как Linux, Windows, macOS).
Это означает, что любое значение, прочитанное в Python из переменной окружения, будет `str`, а любые преобразования к другим типам или любая валидация должны выполняться в коде.
## Pydantic `Settings` { #pydantic-settings }
К счастью, Pydantic предоставляет отличную утилиту для работы с этими настройками, поступающими из переменных окружения, — [Pydantic: управление настройками](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Установка `pydantic-settings` { #install-pydantic-settings }
Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет `pydantic-settings`:
### Создание объекта `Settings` { #create-the-settings-object }
Импортируйте `BaseSettings` из Pydantic и создайте подкласс, очень похожий на Pydantic‑модель.
Аналогично Pydantic‑моделям, вы объявляете атрибуты класса с аннотациями типов и, при необходимости, значениями по умолчанию.
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Совет
Если вам нужно что-то быстро скопировать и вставить, не используйте этот пример — воспользуйтесь последним ниже.
///
Затем, когда вы создаете экземпляр этого класса `Settings` (в нашем случае объект `settings`), Pydantic прочитает переменные окружения регистронезависимо, то есть переменная в верхнем регистре `APP_NAME` будет прочитана для атрибута `app_name`.
Далее он преобразует и провалидирует данные. Поэтому при использовании объекта `settings` вы получите данные тех типов, которые объявили (например, `items_per_user` будет `int`).
### Использование `settings` { #use-the-settings }
Затем вы можете использовать новый объект `settings` в вашем приложении:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Запуск сервера { #run-the-server }
Далее вы можете запустить сервер, передав конфигурации через переменные окружения. Например, можно задать `ADMIN_EMAIL` и `APP_NAME` так:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Совет
Чтобы задать несколько переменных окружения для одной команды, просто разделяйте их пробелами и укажите все перед командой.
///
Тогда параметр `admin_email` будет установлен в `"deadpool@example.com"`.
`app_name` будет `"ChimichangApp"`.
А `items_per_user` сохранит значение по умолчанию `50`.
## Настройки в другом модуле { #settings-in-another-module }
Вы можете вынести эти настройки в другой модуль, как показано в разделе [Большие приложения — несколько файлов](../tutorial/bigger-applications.md).
Например, у вас может быть файл `config.py` со следующим содержимым:
{* ../../docs_src/settings/app01_py310/config.py *}
А затем использовать его в файле `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Совет
Вам также понадобится файл `__init__.py`, как в разделе [Большие приложения — несколько файлов](../tutorial/bigger-applications.md).
///
## Настройки как зависимость { #settings-in-a-dependency }
Иногда может быть полезно предоставлять настройки через зависимость, вместо глобального объекта `settings`, используемого повсюду.
Это особенно удобно при тестировании, так как очень легко переопределить зависимость своими настройками.
### Файл конфигурации { #the-config-file }
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`.
### Основной файл приложения { #the-main-app-file }
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Совет
Скоро мы обсудим `@lru_cache`.
Пока можно считать, что `get_settings()` — это обычная функция.
///
Затем мы можем запросить ее в *функции-обработчике пути* как зависимость и использовать там, где нужно.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Настройки и тестирование { #settings-and-testing }
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект.
После этого можно протестировать, что он используется.
## Чтение файла `.env` { #reading-a-env-file }
Если у вас много настроек, которые могут часто меняться, возможно в разных окружениях, может быть удобно поместить их в файл и читать оттуда как переменные окружения.
Эта практика достаточно распространена и имеет название: такие переменные окружения обычно размещают в файле `.env`, а сам файл называют «dotenv».
/// tip | Совет
Файл, начинающийся с точки (`.`), является скрытым в системах, подобных Unix, таких как Linux и macOS.
Но файл dotenv не обязательно должен иметь именно такое имя.
///
Pydantic поддерживает чтение таких файлов с помощью внешней библиотеки. Подробнее вы можете прочитать здесь: [Pydantic Settings: поддержка Dotenv (.env)](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Совет
Чтобы это работало, вам нужно `pip install python-dotenv`.
///
### Файл `.env` { #the-env-file }
У вас может быть файл `.env` со следующим содержимым:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Чтение настроек из `.env` { #read-settings-from-env }
Затем обновите ваш `config.py` так:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Совет
Атрибут `model_config` используется только для конфигурации Pydantic. Подробнее см. [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Здесь мы задаем параметр конфигурации `env_file` внутри вашего класса Pydantic `Settings` и устанавливаем значение равным имени файла dotenv, который хотим использовать.
### Создание `Settings` только один раз с помощью `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Чтение файла с диска обычно затратная (медленная) операция, поэтому, вероятно, вы захотите сделать это один раз и затем переиспользовать один и тот же объект настроек, а не читать файл при каждом запросе.
Но каждый раз, когда мы делаем:
```Python
Settings()
```
создается новый объект `Settings`, и при создании он снова считывает файл `.env`.
Если бы функция зависимости была такой:
```Python
def get_settings():
return Settings()
```
мы бы создавали этот объект для каждого запроса и читали файл `.env` на каждый запрос. ⚠️
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова.
#### Технические детали `lru_cache` { #lru-cache-technical-details }
`@lru_cache` модифицирует декорируемую функцию так, что она возвращает то же значение, что и в первый раз, вместо повторного вычисления, то есть вместо выполнения кода функции каждый раз.
Таким образом, функция под декоратором будет выполнена один раз для каждой комбинации аргументов. Затем значения, возвращенные для каждой из этих комбинаций, будут использоваться снова и снова при вызове функции с точно такой же комбинацией аргументов.
Например, если у вас есть функция:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
ваша программа может выполняться так:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
В случае нашей зависимости `get_settings()` функция вообще не принимает аргументов, поэтому она всегда возвращает одно и то же значение.
Таким образом, она ведет себя почти как глобальная переменная. Но так как используется функция‑зависимость, мы можем легко переопределить ее для тестирования.
`@lru_cache` — часть `functools`, что входит в стандартную библиотеку Python. Подробнее можно прочитать в [документации Python по `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Итоги { #recap }
Вы можете использовать Pydantic Settings для управления настройками и конфигурациями вашего приложения с полной мощью Pydantic‑моделей.
* Используя зависимость, вы упрощаете тестирование.
* Можно использовать файлы `.env`.
* `@lru_cache` позволяет не читать файл dotenv снова и снова для каждого запроса, при этом давая возможность переопределять его во время тестирования.
================================================
FILE: docs/ru/docs/advanced/stream-data.md
================================================
# Потоковая передача данных { #stream-data }
Если вам нужно передавать потоковые данные, которые можно представить как JSON, воспользуйтесь [стримингом JSON Lines](../tutorial/stream-json-lines.md).
Но если вы хотите передавать в потоке чистые бинарные данные или строки, ниже показано, как это сделать.
/// info | Информация
Добавлено в FastAPI 0.134.0.
///
## Варианты использования { #use-cases }
Это можно использовать, если вы хотите стримить чистые строки, например, напрямую из вывода сервиса **AI LLM**.
Также вы можете стримить **большие бинарные файлы**, передавая каждый чанк данных по мере чтения, без необходимости загружать всё в память сразу.
Аналогично можно стримить **видео** или **аудио** — данные могут даже генерироваться по мере обработки и отправки.
## «`StreamingResponse` с `yield`» { #a-streamingresponse-with-yield }
Если вы укажете `response_class=StreamingResponse` в своей *функции-обработчике пути*, вы можете использовать `yield`, чтобы по очереди отправлять каждый чанк данных.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI будет передавать каждый чанк данных в `StreamingResponse` как есть, не пытаясь конвертировать его в JSON или что-то подобное.
### Не-async *функции-обработчики пути* { #non-async-path-operation-functions }
Можно использовать и обычные функции `def` (без `async`) и точно так же применять `yield`.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Без аннотации { #no-annotation }
Для потоковой передачи бинарных данных вам не нужно указывать аннотацию типа возвращаемого значения.
Поскольку FastAPI не будет пытаться конвертировать данные в JSON с помощью Pydantic или сериализовать их каким-либо образом, в данном случае аннотация типа нужна только для вашего редактора кода и инструментов, FastAPI её использовать не будет.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Это также означает, что с `StreamingResponse` у вас есть и свобода, и ответственность — производить и кодировать байты данных ровно в том виде, в котором они должны быть отправлены, независимо от аннотаций типов. 🤓
### Потоковая передача байтов { #stream-bytes }
Один из основных сценариев — стримить `bytes` вместо строк, и, конечно, это можно сделать.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Пользовательский `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
В примерах выше байты данных передавались потоком, но в ответе не было HTTP-заголовка `Content-Type`, поэтому клиент не знал, какой тип данных он получает.
Вы можете создать пользовательский подкласс `StreamingResponse`, который устанавливает HTTP-заголовок `Content-Type` в тип данных, которые вы стримите.
Например, вы можете создать `PNGStreamingResponse`, который устанавливает HTTP-заголовок `Content-Type` в `image/png` с помощью атрибута `media_type`:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Затем вы можете использовать этот новый класс в `response_class=PNGStreamingResponse` в своей *функции-обработчике пути*:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Симулировать файл { #simulate-a-file }
В этом примере мы симулируем файл с помощью `io.BytesIO` — это «файлоподобный» объект, который существует только в памяти, но позволяет использовать тот же интерфейс.
Например, мы можем итерироваться по нему, чтобы потреблять его содержимое, как и по обычному файлу.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Технические детали
Две другие переменные, `image_base64` и `binary_image`, — это изображение, закодированное в Base64, а затем преобразованное в байты, после чего переданное в `io.BytesIO`.
Только для того, чтобы всё помещалось в одном файле для этого примера, и вы могли скопировать код и запустить его как есть. 🥚
///
Используя блок `with`, мы гарантируем, что объект, ведущий себя как файл, будет закрыт после завершения работы функции‑генератора (функции с `yield`). То есть после того, как она закончит отправку ответа.
В этом конкретном примере это не столь важно, потому что это «фейковый» файл в памяти (`io.BytesIO`), но для реального файла важно удостовериться, что файл закрыт после завершения работы с ним.
### Файлы и async { #files-and-async }
В большинстве случаев «файлоподобные» объекты по умолчанию не совместимы с async и await.
Например, у них нет `await file.read()` или `async for chunk in file`.
И во многих случаях чтение таких объектов будет блокирующей операцией (которая может заблокировать цикл событий), потому что данные читаются с диска или из сети.
/// info | Информация
Приведённый выше пример — исключение, потому что объект `io.BytesIO` уже находится в памяти, поэтому чтение ничего не блокирует.
Но во многих случаях чтение файла или «файлоподобного» объекта будет блокировать.
///
Чтобы не блокировать цикл событий, вы можете просто объявить *функцию-обработчик пути* обычной `def` вместо `async def`. Тогда FastAPI выполнит её в воркере из пула потоков (threadpool), чтобы не блокировать основной цикл.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Совет
Если вам нужно вызвать блокирующий код изнутри async-функции, или async-функцию изнутри блокирующей функции, вы можете использовать [Asyncer](https://asyncer.tiangolo.com), родственную библиотеку к FastAPI.
///
### `yield from` { #yield-from }
Когда вы итерируетесь по чему‑то, например, по «файлоподобному» объекту, и делаете `yield` для каждого элемента, вы можете также использовать `yield from`, чтобы отдавать каждый элемент напрямую и не писать цикл `for`.
Это не специфично для FastAPI, это просто Python, но полезный приём. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/ru/docs/advanced/strict-content-type.md
================================================
# Строгая проверка HTTP-заголовка Content-Type { #strict-content-type-checking }
По умолчанию **FastAPI** использует строгую проверку HTTP-заголовка `Content-Type` для JSON-тел запросов. Это означает, что JSON-запросы должны включать корректный заголовок `Content-Type` (например, `application/json`), чтобы тело запроса было обработано как JSON.
## Риск CSRF { #csrf-risk }
Такое поведение по умолчанию обеспечивает защиту от класса атак **Cross-Site Request Forgery (CSRF)** в очень специфическом сценарии.
Эти атаки используют то, что браузеры позволяют скриптам отправлять запросы без выполнения CORS preflight, когда они:
- не имеют заголовка `Content-Type` (например, при использовании `fetch()` с телом `Blob`)
- и не отправляют никаких учетных данных аутентификации.
Этот тип атак в основном актуален, когда:
- приложение запускается локально (например, на `localhost`) или во внутренней сети
- и в приложении нет аутентификации, оно предполагает, что любому запросу из той же сети можно доверять.
## Пример атаки { #example-attack }
Представьте, что вы сделали способ запускать локального ИИ-агента.
Он предоставляет API по адресу
```
http://localhost:8000/v1/agents/multivac
```
Есть также фронтенд по адресу
```
http://localhost:8000
```
/// tip | Совет
Обратите внимание, что у обоих один и тот же хост.
///
Через фронтенд вы можете заставлять ИИ-агента выполнять действия от вашего имени.
Так как он работает локально и не в открытом интернете, вы решаете не настраивать аутентификацию, полагаясь на доступ к локальной сети.
Затем один из ваших пользователей может установить это и запускать локально.
Потом он может открыть вредоносный сайт, например что-то вроде
```
https://evilhackers.example.com
```
И этот вредоносный сайт отправит запросы с помощью `fetch()` с телом `Blob` к локальному API по адресу
```
http://localhost:8000/v1/agents/multivac
```
Несмотря на то, что хост вредоносного сайта и локального приложения различается, браузер не запустит CORS preflight-запрос, потому что:
- Приложение работает без аутентификации, ему не нужно отправлять какие-либо учетные данные.
- Браузер «считает», что он не отправляет JSON (из-за отсутствия заголовка `Content-Type`).
В итоге вредоносный сайт может заставить локального ИИ-агента отправить злые сообщения бывшему начальнику пользователя... или что-то похуже. 😅
## Открытый интернет { #open-internet }
Если ваше приложение доступно в открытом интернете, вы не будете «доверять сети» и позволять кому угодно отправлять привилегированные запросы без аутентификации.
Злоумышленники могут просто запустить скрипт и слать запросы к вашему API, без участия браузера, так что вы, вероятно, уже защищаете любые привилегированные эндпоинты.
В этом случае эта атака/риск на вас не распространяется.
Этот риск и атака в основном актуальны, когда приложение работает в локальной сети и это единственная предполагаемая защита.
## Разрешение запросов без Content-Type { #allowing-requests-without-content-type }
Если вам нужно поддерживать клиентов, которые не отправляют заголовок `Content-Type`, вы можете отключить строгую проверку, установив `strict_content_type=False`:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
С этой настройкой запросы без заголовка `Content-Type` будут иметь тело запроса, обработанное как JSON — это такое же поведение, как в более старых версиях FastAPI.
/// info | Информация
Это поведение и настройка были добавлены в FastAPI 0.132.0.
///
================================================
FILE: docs/ru/docs/advanced/sub-applications.md
================================================
# Подприложения — Mounts (монтирование) { #sub-applications-mounts }
Если вам нужны два независимых приложения FastAPI, каждое со своим собственным OpenAPI и собственными интерфейсами документации, вы можете иметь основное приложение и «смонтировать» одно (или несколько) подприложений.
## Монтирование приложения **FastAPI** { #mounting-a-fastapi-application }
«Монтирование» означает добавление полностью независимого приложения по конкретному пути; далее оно будет обрабатывать всё под этим путём, используя объявленные в подприложении _операции пути_.
### Приложение верхнего уровня { #top-level-application }
Сначала создайте основное, верхнего уровня, приложение **FastAPI** и его *операции пути*:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Подприложение { #sub-application }
Затем создайте подприложение и его *операции пути*.
Это подприложение — обычное стандартное приложение FastAPI, но именно оно будет «смонтировано»:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Смонтируйте подприложение { #mount-the-sub-application }
В вашем приложении верхнего уровня, `app`, смонтируйте подприложение `subapi`.
В этом случае оно будет смонтировано по пути `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Проверьте автоматическую документацию API { #check-the-automatic-api-docs }
Теперь запустите команду `fastapi`:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
И откройте документацию по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите автоматическую документацию API для основного приложения, включающую только его собственные _операции пути_:
Затем откройте документацию для подприложения по адресу [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Вы увидите автоматическую документацию API для подприложения, включающую только его собственные _операции пути_, все под корректным префиксом подпути `/subapi`:
Если вы попробуете взаимодействовать с любым из двух интерфейсов, всё будет работать корректно, потому что браузер сможет обращаться к каждому конкретному приложению и подприложению.
### Технические подробности: `root_path` { #technical-details-root-path }
Когда вы монтируете подприложение, как описано выше, FastAPI позаботится о передаче пути монтирования для подприложения, используя механизм из спецификации ASGI под названием `root_path`.
Таким образом подприложение будет знать, что для интерфейса документации нужно использовать этот префикс пути.
У подприложения также могут быть свои собственные смонтированные подприложения, и всё будет работать корректно, потому что FastAPI автоматически обрабатывает все эти `root_path`.
Вы узнаете больше о `root_path` и о том, как использовать его явно, в разделе [За прокси](behind-a-proxy.md).
================================================
FILE: docs/ru/docs/advanced/templates.md
================================================
# Шаблоны { #templates }
Вы можете использовать любой шаблонизатор вместе с **FastAPI**.
Часто выбирают Jinja2 — тот же, что используется во Flask и других инструментах.
Есть утилиты для простой настройки, которые вы можете использовать прямо в своем приложении **FastAPI** (предоставляются Starlette).
## Установка зависимостей { #install-dependencies }
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его и установили `jinja2`:
/// info | Информация
Существуют и другие форматы и инструменты для описания и установки зависимостей.
///
### Создать код **FastAPI** { #create-the-fastapi-code }
* Создайте директорию `app` и перейдите в неё.
* Создайте пустой файл `__init__.py`.
* Создайте файл `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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Теперь в той же директории проекта создайте файл `Dockerfile`:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Начинаем с официального базового образа Python.
2. Устанавливаем текущую рабочую директорию в `/code`.
Здесь мы разместим файл `requirements.txt` и директорию `app`.
3. Копируем файл с зависимостями в директорию `/code`.
Сначала копируйте **только** файл с зависимостями, не остальной код.
Так как этот файл **меняется нечасто**, Docker определит это и использует **кэш** на этом шаге, что позволит использовать кэш и на следующем шаге.
4. Устанавливаем зависимости из файла с требованиями.
Опция `--no-cache-dir` указывает `pip` не сохранять загруженные пакеты локально, т.к. это нужно только если `pip` будет запускаться снова для установки тех же пакетов, а при работе с контейнерами это обычно не требуется.
/// note | Заметка
`--no-cache-dir` относится только к `pip` и не имеет отношения к Docker или контейнерам.
///
Опция `--upgrade` указывает `pip` обновлять пакеты, если они уже установлены.
Поскольку предыдущий шаг с копированием файла может быть обработан **кэшем Docker**, этот шаг также **использует кэш Docker**, когда это возможно.
Использование кэша на этом шаге **сэкономит** вам много **времени** при повторных сборках образа во время разработки, вместо того чтобы **загружать и устанавливать** все зависимости **каждый раз**.
5. Копируем директорию `./app` внутрь директории `/code`.
Так как здесь весь код, который **меняется чаще всего**, кэш Docker **вряд ли** будет использоваться для этого шага или **последующих шагов**.
Поэтому важно разместить этот шаг **ближе к концу** `Dockerfile`, чтобы оптимизировать время сборки образа контейнера.
6. Указываем **команду** для запуска `fastapi run`, под капотом используется Uvicorn.
`CMD` принимает список строк, каждая из которых — это то, что вы бы ввели в командной строке, разделяя пробелами.
Эта команда будет выполнена из **текущей рабочей директории**, той самой `/code`, которую вы задали выше `WORKDIR /code`.
/// tip | Подсказка
Посмотрите, что делает каждая строка, кликнув по номеру рядом со строкой. 👆
///
/// warning | Предупреждение
Всегда используйте **exec-форму** инструкции `CMD`, как описано ниже.
///
#### Используйте `CMD` — exec-форма { #use-cmd-exec-form }
Инструкцию Docker [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) можно писать в двух формах:
✅ **Exec**-форма:
```Dockerfile
# ✅ Делайте так
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell**-форма:
```Dockerfile
# ⛔️ Не делайте так
CMD fastapi run app/main.py --port 80
```
Обязательно используйте **exec**-форму, чтобы FastAPI мог корректно завершаться и чтобы срабатывали [события lifespan](../advanced/events.md).
Подробнее об этом читайте в [документации Docker о shell- и exec-формах](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Это особенно заметно при использовании `docker compose`. См. раздел FAQ Docker Compose с техническими подробностями: [Почему мои сервисы пересоздаются или останавливаются 10 секунд?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Структура директорий { #directory-structure }
Теперь у вас должна быть такая структура:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### За прокси-сервером TSL-терминации { #behind-a-tls-termination-proxy }
Если вы запускаете контейнер за прокси-сервером TSL-терминации (балансировщиком нагрузки), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`. Это сообщит Uvicorn (через FastAPI CLI), что приложение работает за HTTPS и можно доверять соответствующим заголовкам.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Кэш Docker { #docker-cache }
В этом `Dockerfile` есть важная хитрость: мы сначала копируем **только файл с зависимостями**, а не весь код. Вот зачем.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker и подобные инструменты **строят** образы контейнеров **инкрементально**, добавляя **слой за слоем**, начиная с первой строки `Dockerfile` и добавляя любые файлы, создаваемые каждой инструкцией `Dockerfile`.
Docker и подобные инструменты также используют **внутренний кэш** при сборке образа: если файл не изменился с момента предыдущей сборки, будет **переиспользован слой**, созданный в прошлый раз, вместо повторного копирования файла и создания нового слоя с нуля.
Само по себе избегание копирования всех файлов не всегда даёт много, но благодаря использованию кэша на этом шаге Docker сможет **использовать кэш и на следующем шаге**. Например, на шаге установки зависимостей:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Файл с зависимостями **меняется нечасто**. Поэтому, копируя только его, Docker сможет **использовать кэш** для этого шага.
А затем Docker сможет **использовать кэш и на следующем шаге**, где скачиваются и устанавливаются зависимости. Здесь мы как раз **экономим много времени**. ✨ ...и не скучаем в ожидании. 😪😆
Скачивание и установка зависимостей **может занять минуты**, но использование **кэша** — **секунды**.
Поскольку во время разработки вы будете пересобирать образ снова и снова, чтобы проверить изменения в коде, суммарно это сэкономит немало времени.
Затем, ближе к концу `Dockerfile`, мы копируем весь код. Так как он **меняется чаще всего**, мы ставим этот шаг в конец, потому что почти всегда всё, что после него, уже не сможет использовать кэш.
```Dockerfile
COPY ./app /code/app
```
### Собрать Docker-образ { #build-the-docker-image }
Теперь, когда все файлы на месте, соберём образ контейнера.
* Перейдите в директорию проекта (где ваш `Dockerfile` и директория `app`).
* Соберите образ FastAPI:
/// tip | Подсказка
Обратите внимание на точку `.` в конце — это то же самое, что `./`. Так мы указываем Docker, из какой директории собирать образ контейнера.
В данном случае это текущая директория (`.`).
///
### Запустить Docker-контейнер { #start-the-docker-container }
* Запустите контейнер на основе вашего образа:
## Проверка { #check-it }
Проверьте работу по адресу вашего Docker-хоста, например: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) или [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (или аналогичный URL вашего Docker-хоста).
Вы увидите что-то вроде:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Интерактивная документация API { #interactive-api-docs }
Теперь зайдите на [http://192.168.99.100/docs](http://192.168.99.100/docs) или [http://127.0.0.1/docs](http://127.0.0.1/docs) (или аналогичный URL вашего Docker-хоста).
Вы увидите автоматическую интерактивную документацию API (на базе [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Альтернативная документация API { #alternative-api-docs }
Также можно открыть [http://192.168.99.100/redoc](http://192.168.99.100/redoc) или [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (или аналогичный URL вашего Docker-хоста).
Вы увидите альтернативную автоматическую документацию (на базе [ReDoc](https://github.com/Rebilly/ReDoc)):

## Собрать Docker-образ для однофайлового FastAPI { #build-a-docker-image-with-a-single-file-fastapi }
Если ваше приложение FastAPI — один файл, например `main.py` без директории `./app`, структура файлов может быть такой:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Тогда в `Dockerfile` нужно изменить пути копирования:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Копируем файл `main.py` напрямую в `/code` (без директории `./app`).
2. Используем `fastapi run` для запуска приложения из одного файла `main.py`.
Когда вы передаёте файл в `fastapi run`, он автоматически определит, что это одиночный файл, а не часть пакета, и поймёт, как его импортировать и запустить ваше FastAPI-приложение. 😎
## Концепции развертывания { #deployment-concepts }
Ещё раз рассмотрим [концепции развертывания](concepts.md) применительно к контейнерам.
Контейнеры главным образом упрощают **сборку и развёртывание** приложения, но не навязывают конкретный подход к этим **концепциям развертывания**, и существует несколько стратегий.
**Хорошая новость** в том, что при любой стратегии есть способ охватить все концепции развертывания. 🎉
Рассмотрим эти **концепции развертывания** в терминах контейнеров:
* HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
## HTTPS { #https }
Если мы рассматриваем только **образ контейнера** для приложения FastAPI (и далее запущенный **контейнер**), то HTTPS обычно обрабатывается **внешним** инструментом.
Это может быть другой контейнер, например с [Traefik](https://traefik.io/), который берёт на себя **HTTPS** и **автоматическое** получение **сертификатов**.
/// tip | Подсказка
У Traefik есть интеграции с Docker, Kubernetes и другими, поэтому очень легко настроить и сконфигурировать HTTPS для ваших контейнеров.
///
В качестве альтернативы HTTPS может быть реализован как сервис облачного провайдера (при этом приложение всё равно работает в контейнере).
## Запуск при старте и перезапуски { #running-on-startup-and-restarts }
Обычно есть другой инструмент, отвечающий за **запуск и работу** вашего контейнера.
Это может быть сам **Docker**, **Docker Compose**, **Kubernetes**, **облачный сервис** и т.п.
В большинстве (или во всех) случаев есть простая опция, чтобы включить запуск контейнера при старте системы и перезапуски при сбоях. Например, в Docker это опция командной строки `--restart`.
Без контейнеров обеспечить запуск при старте и перезапуски может быть сложно. Но при **работе с контейнерами** в большинстве случаев этот функционал доступен по умолчанию. ✨
## Репликация — количество процессов { #replication-number-of-processes }
Если у вас есть кластер машин с **Kubernetes**, Docker Swarm Mode, Nomad или другой похожей системой для управления распределёнными контейнерами на нескольких машинах, скорее всего вы будете **управлять репликацией** на **уровне кластера**, а не использовать **менеджер процессов** (например, Uvicorn с воркерами) в каждом контейнере.
Одна из таких систем управления распределёнными контейнерами, как Kubernetes, обычно имеет встроенный способ управлять **репликацией контейнеров**, поддерживая **балансировку нагрузки** для входящих запросов — всё это на **уровне кластера**.
В таких случаях вы, скорее всего, захотите собрать **Docker-образ с нуля**, как [описано выше](#dockerfile), установить зависимости и запускать **один процесс Uvicorn** вместо множества воркеров Uvicorn.
### Балансировщик нагрузки { #load-balancer }
При использовании контейнеров обычно есть компонент, **слушающий главный порт**. Это может быть другой контейнер — **прокси-сервер TSL-терминации** для обработки **HTTPS** или похожий инструмент.
Поскольку этот компонент принимает **нагрузку** запросов и распределяет её между воркерами **сбалансированно**, его часто называют **балансировщиком нагрузки**.
/// tip | Подсказка
Тот же компонент **прокси-сервер TSL-терминации**, который обрабатывает HTTPS, скорее всего также будет **балансировщиком нагрузки**.
///
При работе с контейнерами система, которую вы используете для запуска и управления ими, уже имеет внутренние средства для передачи **сетевого взаимодействия** (например, HTTP-запросов) от **балансировщика нагрузки** (который также может быть **прокси-сервером TSL-терминации**) к контейнеру(-ам) с вашим приложением.
### Один балансировщик — несколько контейнеров-воркеров { #one-load-balancer-multiple-worker-containers }
При работе с **Kubernetes** или похожими системами управления распределёнными контейнерами их внутренние механизмы сети позволяют одному **балансировщику нагрузки**, слушающему главный **порт**, передавать запросы в **несколько контейнеров**, где запущено ваше приложение.
Каждый такой контейнер с вашим приложением обычно имеет **только один процесс** (например, процесс Uvicorn с вашим приложением FastAPI). Все они — **одинаковые контейнеры**, запускающие одно и то же, но у каждого свой процесс, память и т.п. Так вы используете **параллелизм** по **разным ядрам** CPU или даже **разным машинам**.
Система распределённых контейнеров с **балансировщиком нагрузки** будет **распределять запросы** между контейнерами с вашим приложением **по очереди**. То есть каждый запрос может обрабатываться одним из нескольких **реплицированных контейнеров**.
Обычно такой **балансировщик нагрузки** может также обрабатывать запросы к *другим* приложениям в вашем кластере (например, к другому домену или под другим префиксом пути URL) и направлять их к нужным контейнерам этого *другого* приложения.
### Один процесс на контейнер { #one-process-per-container }
В таком сценарии, скорее всего, вы захотите иметь **один (Uvicorn) процесс на контейнер**, так как репликация уже управляется на уровне кластера.
Поэтому в контейнере **не нужно** поднимать несколько воркеров, например через опцию командной строки `--workers`. Нужен **один процесс Uvicorn** на контейнер (но, возможно, несколько контейнеров).
Наличие отдельного менеджера процессов внутри контейнера (как при нескольких воркерах) только добавит **лишнюю сложность**, которую, вероятно, уже берёт на себя ваша кластерная система.
### Контейнеры с несколькими процессами и особые случаи { #containers-with-multiple-processes-and-special-cases }
Конечно, есть **особые случаи**, когда может понадобиться **контейнер** с несколькими **воркерами Uvicorn** внутри.
В таких случаях вы можете использовать опцию командной строки `--workers`, чтобы указать нужное количество воркеров:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Здесь мы используем опцию `--workers`, чтобы установить число воркеров равным 4.
Примеры, когда это может быть уместно:
#### Простое приложение { #a-simple-app }
Вам может понадобиться менеджер процессов в контейнере, если приложение **достаточно простое**, чтобы запускаться на **одном сервере**, а не в кластере.
#### Docker Compose { #docker-compose }
Вы можете развёртывать на **одном сервере** (не кластере) с **Docker Compose**, и у вас не будет простого способа управлять репликацией контейнеров (в Docker Compose), сохраняя общую сеть и **балансировку нагрузки**.
Тогда вы можете захотеть **один контейнер** с **менеджером процессов**, который запускает **несколько воркеров** внутри.
---
Главное — **ни одно** из этих правил не является **строго обязательным**. Используйте эти идеи, чтобы **оценить свой конкретный случай** и решить, какой подход лучше для вашей системы, учитывая:
* Безопасность — HTTPS
* Запуск при старте
* Перезапуски
* Репликацию (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
## Память { #memory }
Если вы запускаете **один процесс на контейнер**, у каждого контейнера будет более-менее чётко определённый, стабильный и ограниченный объём потребляемой памяти (контейнеров может быть несколько при репликации).
Затем вы можете задать такие же лимиты и требования по памяти в конфигурации вашей системы управления контейнерами (например, в **Kubernetes**). Так система сможет **реплицировать контейнеры** на **доступных машинах**, учитывая объём необходимой памяти и доступной памяти в машинах кластера.
Если приложение **простое**, это, вероятно, **не будет проблемой**, и жёсткие лимиты памяти можно не указывать. Но если вы **используете много памяти** (например, с моделями **Машинного обучения**), проверьте, сколько памяти потребляется, и отрегулируйте **число контейнеров** на **каждой машине** (и, возможно, добавьте машины в кластер).
Если вы запускаете **несколько процессов в контейнере**, нужно убедиться, что их суммарное потребление **не превысит доступную память**.
## Предварительные шаги перед запуском и контейнеры { #previous-steps-before-starting-and-containers }
Если вы используете контейнеры (например, Docker, Kubernetes), есть два основных подхода.
### Несколько контейнеров { #multiple-containers }
Если у вас **несколько контейнеров**, и, вероятно, каждый запускает **один процесс** (например, в кластере **Kubernetes**), то вы, скорее всего, захотите иметь **отдельный контейнер**, выполняющий **предварительные шаги** в одном контейнере и одном процессе **до** запуска реплицированных контейнеров-воркеров.
/// info | Информация
Если вы используете Kubernetes, это, вероятно, будет [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Если в вашем случае нет проблемы с тем, чтобы выполнять эти предварительные шаги **многократно и параллельно** (например, вы не запускаете миграции БД, а только проверяете готовность БД), вы можете просто выполнить их в каждом контейнере прямо перед стартом основного процесса.
### Один контейнер { #single-container }
Если у вас простая схема с **одним контейнером**, который затем запускает несколько **воркеров** (или один процесс), можно выполнить подготовительные шаги в этом же контейнере непосредственно перед запуском процесса с приложением.
### Базовый Docker-образ { #base-docker-image }
Ранее существовал официальный Docker-образ FastAPI: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Сейчас он помечен как устаревший. ⛔️
Скорее всего, вам **не стоит** использовать этот базовый образ (или какой-либо аналогичный).
Если вы используете **Kubernetes** (или другое) и уже настраиваете **репликацию** на уровне кластера через несколько **контейнеров**, в этих случаях лучше **собрать образ с нуля**, как описано выше: [Создать Docker-образ для FastAPI](#build-a-docker-image-for-fastapi).
А если вам нужны несколько воркеров, просто используйте опцию командной строки `--workers`.
/// note | Технические подробности
Этот Docker-образ был создан в то время, когда Uvicorn не умел управлять и перезапускать «упавших» воркеров, и приходилось использовать Gunicorn вместе с Uvicorn, что добавляло заметную сложность, лишь бы Gunicorn управлял и перезапускал воркеров Uvicorn.
Но теперь, когда Uvicorn (и команда `fastapi`) поддерживают `--workers`, нет причин использовать базовый Docker-образ вместо сборки своего (кода получается примерно столько же 😅).
///
## Развёртывание образа контейнера { #deploy-the-container-image }
После того как у вас есть образ контейнера (Docker), его можно развёртывать несколькими способами.
Например:
* С **Docker Compose** на одном сервере
* В кластере **Kubernetes**
* В кластере Docker Swarm Mode
* С другим инструментом, например Nomad
* С облачным сервисом, который принимает ваш образ контейнера и разворачивает его
## Docker-образ с `uv` { #docker-image-with-uv }
Если вы используете [uv](https://github.com/astral-sh/uv) для установки и управления проектом, следуйте их [руководству по Docker для uv](https://docs.astral.sh/uv/guides/integration/docker/).
## Резюме { #recap }
Используя системы контейнеризации (например, **Docker** и **Kubernetes**), довольно просто закрыть все **концепции развертывания**:
* HTTPS
* Запуск при старте
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
В большинстве случаев вы, вероятно, не захотите использовать какой-либо базовый образ, а вместо этого **соберёте образ контейнера с нуля** на основе официального Docker-образа Python.
Заботясь о **порядке** инструкций в `Dockerfile` и используя **кэш Docker**, вы можете **минимизировать время сборки**, чтобы повысить продуктивность (и не скучать). 😎
================================================
FILE: docs/ru/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Вы можете развернуть своё приложение FastAPI в [FastAPI Cloud](https://fastapicloud.com) одной командой, присоединяйтесь к списку ожидания, если ещё не сделали этого. 🚀
## Вход { #login }
Убедитесь, что у вас уже есть аккаунт **FastAPI Cloud** (мы пригласили вас из списка ожидания 😉).
Затем выполните вход:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Деплой { #deploy }
Теперь разверните приложение одной командой:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Вот и всё! Теперь вы можете открыть своё приложение по этому URL. ✨
## О FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** создан тем же автором и командой, что и **FastAPI**.
Он упрощает процесс **создания образа**, **развертывания** и **доступа** к API с минимальными усилиями.
Он переносит тот же **опыт разработчика**, что вы получаете при создании приложений на FastAPI, на их **развертывание** в облаке. 🎉
Он также возьмёт на себя большинство вещей, которые требуются при развертывании приложения, например:
* HTTPS
* Репликация с автоматическим масштабированием на основе запросов
* и т.д.
FastAPI Cloud — основной спонсор и источник финансирования open source‑проектов «FastAPI и друзья». ✨
## Развертывание у других облачных провайдеров { #deploy-to-other-cloud-providers }
FastAPI — проект с открытым исходным кодом и основан на стандартах. Вы можете развернуть приложения FastAPI у любого облачного провайдера на ваш выбор.
Следуйте руководствам вашего облачного провайдера, чтобы развернуть приложения FastAPI у них. 🤓
## Развертывание на собственном сервере { #deploy-your-own-server }
Позже в этом руководстве по **развертыванию** я также расскажу все детали — чтобы вы понимали, что происходит, что нужно сделать и как развернуть приложения FastAPI самостоятельно, в том числе на собственных серверах. 🤓
================================================
FILE: docs/ru/docs/deployment/https.md
================================================
# Об HTTPS { #about-https }
Легко предположить, что HTTPS — это что-то, что просто «включено» или нет.
Но на самом деле всё гораздо сложнее.
/// tip | Совет
Если вы торопитесь или вам это не важно, переходите к следующим разделам с пошаговыми инструкциями по настройке всего разными способами.
///
Чтобы **изучить основы HTTPS** с точки зрения пользователя, загляните на [https://howhttps.works/](https://howhttps.works/).
Теперь, со стороны **разработчика**, вот несколько вещей, которые стоит держать в голове, размышляя об HTTPS:
* Для HTTPS **серверу** нужно **иметь «сертификаты»**, сгенерированные **третьей стороной**.
* Эти сертификаты на самом деле **приобретаются** у третьей стороны, а не «генерируются».
* У сертификатов есть **срок действия**.
* Они **истекают**.
* После этого их нужно **обновлять**, то есть **получать заново** у третьей стороны.
* Шифрование соединения происходит на **уровне TCP**.
* Это на один уровень **ниже HTTP**.
* Поэтому **сертификаты и шифрование** обрабатываются **до HTTP**.
* **TCP не знает о «доменах»**. Только об IP-адресах.
* Информация о **конкретном домене** передаётся в **данных HTTP**.
* **HTTPS-сертификаты** «сертифицируют» **определённый домен**, но протокол и шифрование происходят на уровне TCP, **до того как** становится известен домен, с которым идёт работа.
* **По умолчанию** это означает, что вы можете иметь **лишь один HTTPS-сертификат на один IP-адрес**.
* Неважно, насколько мощный у вас сервер или насколько маленькие приложения на нём работают.
* Однако у этого есть **решение**.
* Есть **расширение** протокола **TLS** (того самого, что занимается шифрованием на уровне TCP, до HTTP) под названием **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* Это расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько HTTPS-сертификатов** и обслуживать **несколько HTTPS-доменов/приложений**.
* Чтобы это работало, **один** компонент (программа), запущенный на сервере и слушающий **публичный IP-адрес**, должен иметь **все HTTPS-сертификаты** на этом сервере.
* **После** установления защищённого соединения, протокол обмена данными — **всё ещё HTTP**.
* Содержимое **зашифровано**, несмотря на то, что оно отправляется по **протоколу HTTP**.
Обычно на сервере (машине, хосте и т.п.) запускают **одну программу/HTTP‑сервер**, которая **управляет всей частью, связанной с HTTPS**: принимает **зашифрованные HTTPS-запросы**, отправляет **расшифрованные HTTP-запросы** в само HTTP‑приложение, работающее на том же сервере (в нашем случае это приложение **FastAPI**), получает **HTTP-ответ** от приложения, **шифрует его** с использованием подходящего **HTTPS‑сертификата** и отправляет клиенту по **HTTPS**. Такой сервер часто называют **[прокси‑сервером TLS-терминации](https://en.wikipedia.org/wiki/TLS_termination_proxy)**.
Некоторые варианты, которые вы можете использовать как прокси‑сервер TLS-терминации:
* Traefik (умеет обновлять сертификаты)
* Caddy (умеет обновлять сертификаты)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
До появления Let's Encrypt эти **HTTPS-сертификаты** продавались доверенными третьими сторонами.
Процесс получения таких сертификатов был неудобным, требовал бумажной волокиты, а сами сертификаты были довольно дорогими.
Затем появился **[Let's Encrypt](https://letsencrypt.org/)**.
Это проект Linux Foundation. Он предоставляет **HTTPS‑сертификаты бесплатно**, в автоматическом режиме. Эти сертификаты используют стандартные криптографические механизмы и имеют короткий срок действия (около 3 месяцев), поэтому **безопасность фактически выше** благодаря уменьшенному сроку жизни.
Домены безопасно проверяются, а сертификаты выдаются автоматически. Это также позволяет автоматизировать процесс их продления.
Идея — автоматизировать получение и продление сертификатов, чтобы у вас был **безопасный HTTPS, бесплатно и навсегда**.
## HTTPS для разработчиков { #https-for-developers }
Ниже приведён пример того, как может выглядеть HTTPS‑API, шаг за шагом, с акцентом на идеях, важных для разработчиков.
### Имя домена { #domain-name }
Чаще всего всё начинается с **приобретения** **имени домена**. Затем вы настраиваете его на DNS‑сервере (возможно, у того же облачного провайдера).
Скорее всего, вы получите облачный сервер (виртуальную машину) или что-то подобное, и у него будет постоянный **публичный IP-адрес**.
На DNS‑сервере(ах) вы настроите запись («`A record`» - запись типа A), указывающую, что **ваш домен** должен указывать на публичный **IP‑адрес вашего сервера**.
Обычно это делается один раз — при первоначальной настройке всего.
/// tip | Совет
Часть про доменное имя относится к этапам задолго до HTTPS, но так как всё зависит от домена и IP‑адреса, здесь стоит это упомянуть.
///
### DNS { #dns }
Теперь сфокусируемся на собственно частях, связанных с HTTPS.
Сначала браузер спросит у **DNS‑серверов**, какой **IP соответствует домену**, в нашем примере `someapp.example.com`.
DNS‑серверы ответят браузеру, какой **конкретный IP‑адрес** использовать. Это будет публичный IP‑адрес вашего сервера, который вы указали в настройках DNS.
### Начало TLS-рукопожатия { #tls-handshake-start }
Далее браузер будет общаться с этим IP‑адресом на **порту 443** (порт HTTPS).
Первая часть взаимодействия — установить соединение между клиентом и сервером и договориться о криптографических ключах и т.п.
Это взаимодействие клиента и сервера для установления TLS‑соединения называется **TLS‑рукопожатием**.
### TLS с расширением SNI { #tls-with-sni-extension }
На сервере **только один процесс** может слушать конкретный **порт** на конкретном **IP‑адресе**. Могут быть другие процессы, слушающие другие порты на том же IP‑адресе, но не более одного процесса на каждую комбинацию IP‑адреса и порта.
По умолчанию TLS (HTTPS) использует порт `443`. Значит, он нам и нужен.
Так как только один процесс может слушать этот порт, делать это будет **прокси‑сервер TLS-терминации**.
У прокси‑сервера TLS-терминации будет доступ к одному или нескольким **TLS‑сертификатам** (HTTPS‑сертификатам).
Используя **расширение SNI**, упомянутое выше, прокси‑сервер TLS-терминации определит, какой из доступных TLS (HTTPS)‑сертификатов нужно использовать для этого соединения, выбрав тот, который соответствует домену, ожидаемому клиентом.
В нашем случае это будет сертификат для `someapp.example.com`.
Клиент уже **доверяет** организации, выдавшей этот TLS‑сертификат (в нашем случае — Let's Encrypt, но об этом позже), поэтому может **проверить**, что сертификат действителен.
Затем, используя сертификат, клиент и прокси‑сервер TLS-терминации **договариваются о способе шифрования** остальной **TCP‑коммуникации**. На этом **TLS‑рукопожатие** завершено.
После этого у клиента и сервера есть **зашифрованное TCP‑соединение** — это и предоставляет TLS. И они могут использовать это соединение, чтобы начать собственно **HTTP‑обмен**.
Собственно, **HTTPS** — это обычный **HTTP** внутри **защищённого TLS‑соединения**, вместо чистого (незашифрованного) TCP‑соединения.
/// tip | Совет
Обратите внимание, что шифрование обмена происходит на **уровне TCP**, а не на уровне HTTP.
///
### HTTPS‑запрос { #https-request }
Теперь, когда у клиента и сервера (конкретно у браузера и прокси‑сервера TLS-терминации) есть **зашифрованное TCP‑соединение**, они могут начать **HTTP‑обмен**.
Клиент отправляет **HTTPS‑запрос**. Это обычный HTTP‑запрос через зашифрованное TLS‑соединение.
### Расшифровка запроса { #decrypt-the-request }
Прокси‑сервер TLS-терминации использует согласованное шифрование, чтобы **расшифровать запрос**, и передаёт **обычный (расшифрованный) HTTP‑запрос** процессу, запускающему приложение (например, процессу с Uvicorn, который запускает приложение FastAPI).
### HTTP‑ответ { #http-response }
Приложение обработает запрос и отправит **обычный (незашифрованный) HTTP‑ответ** прокси‑серверу TLS-терминации.
### HTTPS‑ответ { #https-response }
Затем прокси‑сервер TLS-терминации **зашифрует ответ** с использованием ранее согласованного способа шифрования (который начали использовать для сертификата для `someapp.example.com`) и отправит его обратно в браузер.
Далее браузер проверит, что ответ корректен и зашифрован правильным криптографическим ключом и т.п., затем **расшифрует ответ** и обработает его.
Клиент (браузер) узнает, что ответ пришёл от правильного сервера, потому что используется способ шифрования, о котором они договорились ранее с помощью **HTTPS‑сертификата**.
### Несколько приложений { #multiple-applications }
На одном и том же сервере (или серверах) могут работать **несколько приложений**, например другие программы с API или база данных.
Только один процесс может обрабатывать конкретную комбинацию IP и порта (в нашем примере — прокси‑сервер TLS-терминации), но остальные приложения/процессы тоже могут работать на сервере(ах), пока они не пытаются использовать ту же **комбинацию публичного IP и порта**.
Таким образом, прокси‑сервер TLS-терминации может обрабатывать HTTPS и сертификаты для **нескольких доменов** (для нескольких приложений), а затем передавать запросы нужному приложению в каждом случае.
### Продление сертификата { #certificate-renewal }
Со временем каждый сертификат **истечёт** (примерно через 3 месяца после получения).
Затем будет другая программа (иногда это отдельная программа, иногда — тот же прокси‑сервер TLS-терминации), которая свяжется с Let's Encrypt и продлит сертификат(ы).
**TLS‑сертификаты** **связаны с именем домена**, а не с IP‑адресом.
Поэтому, чтобы продлить сертификаты, программа продления должна **доказать** удостоверяющему центру (Let's Encrypt), что она действительно **«владеет» и контролирует этот домен**.
Для этого, учитывая разные потребности приложений, есть несколько способов. Популярные из них:
* **Изменить некоторые DNS‑записи**.
* Для этого программа продления должна поддерживать API DNS‑провайдера, поэтому, в зависимости от используемого провайдера DNS, этот вариант может быть доступен или нет.
* **Запуститься как сервер** (как минимум на время получения сертификатов) на публичном IP‑адресе, связанном с доменом.
* Как сказано выше, только один процесс может слушать конкретный IP и порт.
* Это одна из причин, почему очень удобно, когда тот же прокси‑сервер TLS-терминации также занимается процессом продления сертификатов.
* В противном случае вам, возможно, придётся временно остановить прокси‑сервер TLS-терминации, запустить программу продления для получения сертификатов, затем настроить их в прокси‑сервере TLS-терминации и перезапустить его. Это не идеально, так как ваше приложение(я) будут недоступны, пока прокси‑сервер TLS-терминации остановлен.
Весь этот процесс продления, совмещённый с обслуживанием приложения, — одна из главных причин иметь **отдельную систему для работы с HTTPS** в виде прокси‑сервера TLS-терминации, вместо использования TLS‑сертификатов напрямую в сервере приложения (например, Uvicorn).
## Пересылаемые HTTP-заголовки прокси { #proxy-forwarded-headers }
Когда вы используете прокси для обработки HTTPS, ваш **сервер приложения** (например, Uvicorn через FastAPI CLI) ничего не знает о процессе HTTPS, он общается обычным HTTP с **прокси‑сервером TLS-терминации**.
Обычно этот **прокси** на лету добавляет некоторые HTTP‑заголовки перед тем, как переслать запрос на **сервер приложения**, чтобы тот знал, что запрос был **проксирован**.
/// note | Технические детали
Заголовки прокси:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Тем не менее, так как **сервер приложения** не знает, что он находится за доверенным **прокси**, по умолчанию он не будет доверять этим заголовкам.
Но вы можете настроить **сервер приложения**, чтобы он доверял *пересылаемым* заголовкам, отправленным **прокси**. Если вы используете FastAPI CLI, вы можете использовать *опцию CLI* `--forwarded-allow-ips`, чтобы указать, с каких IP‑адресов следует доверять этим *пересылаемым* заголовкам.
Например, если **сервер приложения** получает запросы только от доверенного **прокси**, вы можете установить `--forwarded-allow-ips="*"`, чтобы доверять всем входящим IP, так как он всё равно будет получать запросы только с IP‑адреса, используемого **прокси**.
Таким образом, приложение сможет знать свой публичный URL, использует ли оно HTTPS, какой домен и т.п.
Это будет полезно, например, для корректной обработки редиректов.
/// tip | Совет
Подробнее об этом вы можете узнать в документации: [За прокси — Включить пересылаемые заголовки прокси](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Резюме { #recap }
Наличие **HTTPS** очень важно и во многих случаях довольно **критично**. Большая часть усилий, которые вам, как разработчику, нужно приложить вокруг HTTPS, — это просто **понимание этих концепций** и того, как они работают.
Зная базовую информацию о **HTTPS для разработчиков**, вы сможете легко комбинировать и настраивать разные инструменты, чтобы управлять всем этим простым способом.
В некоторых из следующих глав я покажу вам несколько конкретных примеров настройки **HTTPS** для приложений **FastAPI**. 🔒
================================================
FILE: docs/ru/docs/deployment/index.md
================================================
# Развёртывание { #deployment }
Развернуть приложение **FastAPI** довольно просто.
## Что означает развёртывание { #what-does-deployment-mean }
Термин **развёртывание** (приложения) означает выполнение необходимых шагов, чтобы сделать приложение **доступным для пользователей**.
Для **веб-API** это обычно означает размещение его на **удалённой машине** с **серверной программой**, обеспечивающей хорошую производительность, стабильность и т.д., чтобы ваши **пользователи** могли **получать доступ** к приложению эффективно и без перебоев или проблем.
Это отличается от этапов **разработки**, когда вы постоянно меняете код, ломаете его и исправляете, останавливаете и перезапускаете сервер разработки и т.д.
## Стратегии развёртывания { #deployment-strategies }
Есть несколько способов сделать это, в зависимости от вашего конкретного случая и используемых вами инструментов.
Вы можете **развернуть сервер** самостоятельно, используя различные инструменты. Например, можно использовать **облачный сервис**, который выполнит часть работы за вас. Также возможны и другие варианты.
Например, мы, команда, стоящая за FastAPI, создали [**FastAPI Cloud**](https://fastapicloud.com), чтобы сделать развёртывание приложений FastAPI в облаке как можно более простым и прямолинейным, с тем же удобством для разработчика, что и при работе с FastAPI.
Я покажу вам некоторые из основных концепций, которые вы, вероятно, должны иметь в виду при развертывании приложения **FastAPI** (хотя большинство из них применимо к любому другому типу веб-приложений).
В последующих разделах вы узнаете больше деталей и методов, необходимых для этого. ✨
================================================
FILE: docs/ru/docs/deployment/manually.md
================================================
# Запуск сервера вручную { #run-a-server-manually }
## Используйте команду `fastapi run` { #use-the-fastapi-run-command }
Коротко: используйте `fastapi run`, чтобы запустить ваше приложение FastAPI:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
В большинстве случаев этого достаточно. 😎
Этой командой, например, можно запускать приложение **FastAPI** в контейнере, на сервере и т.д.
## ASGI‑серверы { #asgi-servers }
Давайте немного углубимся в детали.
FastAPI использует стандарт для построения Python‑веб‑фреймворков и серверов под названием ASGI. FastAPI — ASGI-веб‑фреймворк.
Главное, что вам нужно, чтобы запустить приложение **FastAPI** (или любое другое ASGI‑приложение) на удалённой серверной машине, — это программа ASGI‑сервера, такая как **Uvicorn**; именно он используется по умолчанию в команде `fastapi`.
Есть несколько альтернатив, например:
* [Uvicorn](https://www.uvicorn.dev/): высокопроизводительный ASGI‑сервер.
* [Hypercorn](https://hypercorn.readthedocs.io/): ASGI‑сервер, среди прочего совместимый с HTTP/2 и Trio.
* [Daphne](https://github.com/django/daphne): ASGI‑сервер, созданный для Django Channels.
* [Granian](https://github.com/emmett-framework/granian): HTTP‑сервер на Rust для Python‑приложений.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit — лёгкая и многофункциональная среда выполнения веб‑приложений.
## Сервер как машина и сервер как программа { #server-machine-and-server-program }
Есть небольшой нюанс в терминологии, о котором стоит помнить. 💡
Слово «сервер» обычно используют и для обозначения удалённого/облачного компьютера (физической или виртуальной машины), и для программы, работающей на этой машине (например, Uvicorn).
Имейте в виду, что слово «сервер» в целом может означать любое из этих двух.
Когда речь идёт об удалённой машине, её зачастую называют **сервер**, а также **машина**, **VM** (виртуальная машина), **нода**. Всё это — варианты названия удалённой машины, обычно под управлением Linux, на которой вы запускаете программы.
## Установка серверной программы { #install-the-server-program }
При установке FastAPI он поставляется с продакшн‑сервером Uvicorn, и вы можете запустить его командой `fastapi run`.
Но вы также можете установить ASGI‑сервер вручную.
Создайте [виртуальное окружение](../virtual-environments.md), активируйте его и затем установите серверное приложение.
Например, чтобы установить Uvicorn:
Аналогично устанавливаются и другие ASGI‑серверы.
/// tip | Совет
С добавлением `standard` Uvicorn установит и будет использовать ряд рекомендованных дополнительных зависимостей.
В их числе `uvloop` — высокопроизводительная замена `asyncio`, дающая серьёзный прирост производительности при параллельной работе.
Если вы устанавливаете FastAPI, например так: `pip install "fastapi[standard]"`, вы уже получаете и `uvicorn[standard]`.
///
## Запуск серверной программы { #run-the-server-program }
Если вы установили ASGI‑сервер вручную, обычно нужно передать строку импорта в специальном формате, чтобы он смог импортировать ваше приложение FastAPI:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Примечание
Команда `uvicorn main:app` означает:
* `main`: файл `main.py` (Python‑«модуль»).
* `app`: объект, созданный в `main.py` строкой `app = FastAPI()`.
Эквивалентно:
```Python
from main import app
```
///
У каждого альтернативного ASGI‑сервера будет похожая команда; подробнее см. в их документации.
/// warning | Предупреждение
Uvicorn и другие серверы поддерживают опцию `--reload`, полезную в период разработки.
Опция `--reload` потребляет значительно больше ресурсов, менее стабильна и т.п.
Она сильно помогает во время **разработки**, но в **продакшн** её использовать **не следует**.
///
## Концепции развёртывания { #deployment-concepts }
В этих примерах серверная программа (например, Uvicorn) запускает **один процесс**, слушающий все IP‑адреса (`0.0.0.0`) на заранее заданном порту (например, `80`).
Это базовая идея. Но, вероятно, вам понадобится позаботиться и о некоторых дополнительных вещах, например:
* Безопасность — HTTPS
* Запуск при старте системы
* Перезапуски
* Репликация (количество запущенных процессов)
* Память
* Предварительные шаги перед запуском
В следующих главах я расскажу подробнее про каждую из этих концепций, о том, как о них думать, и приведу конкретные примеры со стратегиями, как с ними работать. 🚀
================================================
FILE: docs/ru/docs/deployment/server-workers.md
================================================
# Серверные воркеры — Uvicorn с воркерами { #server-workers-uvicorn-with-workers }
Давайте снова вспомним те концепции деплоя, о которых говорили ранее:
* Безопасность — HTTPS
* Запуск при старте
* Перезапуски
* **Репликация (количество запущенных процессов)**
* Память
* Предварительные шаги перед запуском
До этого момента, следуя руководствам в документации, вы, вероятно, запускали **серверную программу**, например с помощью команды `fastapi`, которая запускает Uvicorn в **одном процессе**.
При деплое приложения вам, скорее всего, захочется использовать **репликацию процессов**, чтобы задействовать **несколько ядер** и иметь возможность обрабатывать больше запросов.
Как вы видели в предыдущей главе о [Концепциях деплоя](concepts.md), существует несколько стратегий.
Здесь я покажу, как использовать **Uvicorn** с **воркер-процессами** через команду `fastapi` или напрямую через команду `uvicorn`.
/// info | Информация
Если вы используете контейнеры, например Docker или Kubernetes, я расскажу об этом подробнее в следующей главе: [FastAPI в контейнерах — Docker](docker.md).
В частности, при запуске в **Kubernetes** вам, скорее всего, **не** понадобится использовать воркеры — вместо этого запускайте **один процесс Uvicorn на контейнер**, но об этом подробнее далее в той главе.
///
## Несколько воркеров { #multiple-workers }
Можно запустить несколько воркеров с помощью опции командной строки `--workers`:
//// tab | `fastapi`
Если вы используете команду `fastapi`:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Если вы предпочитаете использовать команду `uvicorn` напрямую:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
Единственная новая опция здесь — `--workers`, она говорит Uvicorn запустить 4 воркер-процесса.
Также видно, что выводится **PID** каждого процесса: `27365` — для родительского процесса (это **менеджер процессов**) и по одному для каждого воркер-процесса: `27368`, `27369`, `27370` и `27367`.
## Концепции деплоя { #deployment-concepts }
Здесь вы увидели, как использовать несколько **воркеров**, чтобы **распараллелить** выполнение приложения, задействовать **несколько ядер** CPU и обслуживать **больше запросов**.
Из списка концепций деплоя выше использование воркеров в основном помогает с **репликацией**, и немного — с **перезапусками**, но об остальных по-прежнему нужно позаботиться:
* **Безопасность — HTTPS**
* **Запуск при старте**
* ***Перезапуски***
* Репликация (количество запущенных процессов)
* **Память**
* **Предварительные шаги перед запуском**
## Контейнеры и Docker { #containers-and-docker }
В следующей главе о [FastAPI в контейнерах — Docker](docker.md) я объясню стратегии, которые можно использовать для решения остальных **концепций деплоя**.
Я покажу, как **собрать свой образ с нуля**, чтобы запускать один процесс Uvicorn. Это простой подход и, вероятно, именно то, что вам нужно при использовании распределённой системы управления контейнерами, такой как **Kubernetes**.
## Резюме { #recap }
Вы можете использовать несколько воркер-процессов с опцией командной строки `--workers` в командах `fastapi` или `uvicorn`, чтобы задействовать **многоядерные CPU**, запуская **несколько процессов параллельно**.
Вы можете использовать эти инструменты и идеи, если настраиваете **собственную систему деплоя** и самостоятельно закрываете остальные концепции деплоя.
Перейдите к следующей главе, чтобы узнать о **FastAPI** в контейнерах (например, Docker и Kubernetes). Вы увидите, что эти инструменты тоже предлагают простые способы решить другие **концепции деплоя**. ✨
================================================
FILE: docs/ru/docs/deployment/versions.md
================================================
# О версиях FastAPI { #about-fastapi-versions }
**FastAPI** уже используется в продакшн во многих приложениях и системах. Покрытие тестами поддерживается на уровне 100%. Но его разработка всё ещё движется быстрыми темпами.
Часто добавляются новые функции, регулярно исправляются баги, код продолжает постоянно совершенствоваться.
По указанным причинам текущие версии до сих пор `0.x.x`. Это говорит о том, что каждая версия может содержать обратно несовместимые изменения, следуя [Семантическому версионированию](https://semver.org/).
Уже сейчас вы можете создавать приложения в продакшн, используя **FastAPI** (и скорее всего так и делаете), главное убедиться в том, что вы используете версию, которая корректно работает с вашим кодом.
## Закрепите вашу версию `fastapi` { #pin-your-fastapi-version }
Первым делом вам следует "закрепить" конкретную последнюю используемую версию **FastAPI**, которая корректно работает с вашим приложением.
Например, в своём приложении вы используете версию `0.112.0`.
Если вы используете файл `requirements.txt`, вы можете указать версию следующим способом:
```txt
fastapi[standard]==0.112.0
```
это означает, что вы будете использовать именно версию `0.112.0`.
Или вы можете закрепить версию следующим способом:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
это значит, что вы используете версии `0.112.0` или выше, но меньше чем `0.113.0`. Например, версия `0.112.2` всё ещё будет подходить.
Если вы используете любой другой инструмент для управления установками/зависимостями, например `uv`, Poetry, Pipenv или др., у них у всех имеется способ определения специфической версии для ваших пакетов.
## Доступные версии { #available-versions }
Вы можете посмотреть доступные версии (например, проверить последнюю на данный момент) в [Примечаниях к выпуску](../release-notes.md).
## О версиях { #about-versions }
Следуя соглашению о Семантическом Версионировании, любые версии ниже `1.0.0` потенциально могут добавить обратно несовместимые изменения.
FastAPI следует соглашению в том, что любые изменения "ПАТЧ"-версии предназначены для исправления багов и внесения обратно совместимых изменений.
/// tip | Подсказка
"ПАТЧ" — это последнее число. Например, в `0.2.3`, ПАТЧ-версия — это `3`.
///
Итак, вы можете закрепить версию следующим образом:
```txt
fastapi>=0.45.0,<0.46.0
```
Обратно несовместимые изменения и новые функции добавляются в "МИНОРНЫЕ" версии.
/// tip | Подсказка
"МИНОРНАЯ" версия — это число в середине. Например, в `0.2.3` МИНОРНАЯ версия — это `2`.
///
## Обновление версий FastAPI { #upgrading-the-fastapi-versions }
Вам следует добавить тесты для вашего приложения.
С помощью **FastAPI** это очень просто (благодаря Starlette), см. документацию: [Тестирование](../tutorial/testing.md)
После создания тестов вы можете обновить свою версию **FastAPI** до более новой. После этого следует убедиться, что ваш код работает корректно, запустив тесты.
Если всё работает корректно, или после внесения необходимых изменений все ваши тесты проходят, только тогда вы можете закрепить вашу новую версию `fastapi`.
## О Starlette { #about-starlette }
Не следует закреплять версию `starlette`.
Разные версии **FastAPI** будут использовать более новые версии Starlette.
Так что решение об используемой версии Starlette, вы можете оставить **FastAPI**.
## О Pydantic { #about-pydantic }
Pydantic включает свои собственные тесты для **FastAPI**, так что новые версии Pydantic (выше `1.0.0`) всегда совместимы с FastAPI.
Вы можете закрепить любую версию Pydantic, которая вам подходит, выше `1.0.0`.
Например:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/ru/docs/editor-support.md
================================================
# Поддержка редактора кода { #editor-support }
Официальное [расширение FastAPI](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) улучшает ваш процесс разработки на FastAPI за счет обнаружения и навигации по *операциям пути* (обработчикам пути), а также развертывания в FastAPI Cloud и потоковой передачи логов в реальном времени.
Подробности о расширении смотрите в README в [репозитории GitHub](https://github.com/fastapi/fastapi-vscode).
## Установка и настройка { #setup-and-installation }
**Расширение FastAPI** доступно как для [VS Code](https://code.visualstudio.com/), так и для [Cursor](https://www.cursor.com/). Его можно установить напрямую из панели расширений в каждом редакторе кода, выполнив поиск по «FastAPI» и выбрав расширение от **FastAPI Labs**. Расширение также работает в браузерных редакторах кода, таких как [vscode.dev](https://vscode.dev) и [github.dev](https://github.dev).
### Обнаружение приложения { #application-discovery }
По умолчанию расширение автоматически обнаруживает приложения FastAPI в вашем рабочем пространстве, сканируя файлы, где создается экземпляр `FastAPI()`. Если авто-обнаружение не подходит для структуры вашего проекта, вы можете указать точку входа через `[tool.fastapi]` в `pyproject.toml` или настройку VS Code `fastapi.entryPoint`, используя модульную нотацию (например, `myapp.main:app`).
## Возможности { #features }
- **Обозреватель операций пути** — древовидное представление на боковой панели всех *операций пути* вашего приложения. Нажмите, чтобы перейти к любому маршруту или определению роутера.
- **Поиск маршрутов** — поиск по пути, методу или имени с помощью Ctrl + Shift + E (на macOS: Cmd + Shift + E).
- **Навигация CodeLens** — кликабельные ссылки над вызовами тестового клиента (например, `client.get('/items')`), которые переходят к соответствующей *операции пути* для быстрой навигации между тестами и реализацией.
- **Развернуть в FastAPI Cloud** — развертывание вашего приложения в один клик в [FastAPI Cloud](https://fastapicloud.com/).
- **Поток логов приложения** — потоковая передача логов в реальном времени из вашего приложения, развернутого в FastAPI Cloud, с фильтрацией по уровню и текстовым поиском.
Если вы хотите поверхностно ознакомиться с возможностями расширения, откройте палитру команд (Ctrl + Shift + P или на macOS: Cmd + Shift + P), выберите «Welcome: Open walkthrough...», а затем «Get started with FastAPI».
================================================
FILE: docs/ru/docs/environment-variables.md
================================================
# Переменные окружения { #environment-variables }
/// tip | Совет
Если вы уже знаете, что такое «переменные окружения» и как их использовать, можете пропустить это.
///
Переменная окружения (также известная как «**env var**») - это переменная, которая живет **вне** кода Python, в **операционной системе**, и может быть прочитана вашим кодом Python (или другими программами).
Переменные окружения могут быть полезны для работы с **настройками** приложений, как часть **установки** Python и т.д.
## Создание и использование переменных окружения { #create-and-use-env-vars }
Можно **создавать** и использовать переменные окружения в **оболочке (терминале)**, не прибегая к помощи Python:
//// tab | Linux, macOS, Windows Bash
```console
// Вы можете создать переменную окружения MY_NAME с помощью
$ export MY_NAME="Wade Wilson"
// Затем её можно использовать в других программах, например
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Создайте переменную окружения MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Используйте её с другими программами, например
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Чтение переменных окружения в python { #read-env-vars-in-python }
Так же существует возможность создания переменных окружения **вне** Python, в терминале (или любым другим способом), а затем **чтения их в Python**.
Например, у вас есть файл `main.py`:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Совет
Второй аргумент [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) - это возвращаемое по умолчанию значение.
Если значение не указано, то по умолчанию оно равно `None`. В данном случае мы указываем `«World»` в качестве значения по умолчанию.
///
Затем можно запустить эту программу на Python:
//// tab | Linux, macOS, Windows Bash
```console
// Здесь мы еще не устанавливаем переменную окружения
$ python main.py
// Поскольку мы не задали переменную окружения, мы получим значение по умолчанию
Hello World from Python
// Но если мы сначала создадим переменную окружения
$ export MY_NAME="Wade Wilson"
// А затем снова запустим программу
$ python main.py
// Теперь она прочитает переменную окружения
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Здесь мы еще не устанавливаем переменную окружения
$ python main.py
// Поскольку мы не задали переменную окружения, мы получим значение по умолчанию
Hello World from Python
// Но если мы сначала создадим переменную окружения
$ $Env:MY_NAME = "Wade Wilson"
// А затем снова запустим программу
$ python main.py
// Теперь она может прочитать переменную окружения
Hello Wade Wilson from Python
```
////
Поскольку переменные окружения могут быть установлены вне кода, но могут быть прочитаны кодом, и их не нужно хранить (фиксировать в `git`) вместе с остальными файлами, их принято использовать для конфигураций или **настроек**.
Вы также можете создать переменную окружения только для **конкретного вызова программы**, которая будет доступна только для этой программы и только на время ее выполнения.
Для этого создайте её непосредственно перед самой программой, в той же строке:
```console
// Создайте переменную окружения MY_NAME в строке для этого вызова программы
$ MY_NAME="Wade Wilson" python main.py
// Теперь она может прочитать переменную окружения
Hello Wade Wilson from Python
// После этого переменная окружения больше не существует
$ python main.py
Hello World from Python
```
/// tip | Совет
Подробнее об этом можно прочитать на сайте [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Типизация и Валидация { #types-and-validation }
Эти переменные окружения могут работать только с **текстовыми строками**, поскольку они являются внешними по отношению к Python и должны быть совместимы с другими программами и остальной системой (и даже с различными операционными системами, такими как Linux, Windows, macOS).
Это означает, что **любое значение**, считанное в Python из переменной окружения, **будет `str`**, и любое преобразование к другому типу или любая валидация должны быть выполнены в коде.
Подробнее об использовании переменных окружения для работы с **настройками приложения** вы узнаете в [Расширенное руководство пользователя - Настройки и переменные среды](./advanced/settings.md).
## Переменная окружения `PATH` { #path-environment-variable }
Существует **специальная** переменная окружения **`PATH`**, которая используется операционными системами (Linux, macOS, Windows) для поиска программ для запуска.
Значение переменной `PATH` - это длинная строка, состоящая из каталогов, разделенных двоеточием `:` в Linux и macOS, и точкой с запятой `;` в Windows.
Например, переменная окружения `PATH` может выглядеть следующим образом:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система должна искать программы в каталогах:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Это означает, что система должна искать программы в каталогах:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Когда вы вводите **команду** в терминале, операционная система **ищет** программу в **каждой из тех директорий**, которые перечислены в переменной окружения `PATH`.
Например, когда вы вводите `python` в терминале, операционная система ищет программу под названием `python` в **первой директории** в этом списке.
Если она ее находит, то **использует ее**. В противном случае она продолжает искать в **других каталогах**.
### Установка Python и обновление `PATH` { #installing-python-and-updating-the-path }
При установке Python вас могут спросить, нужно ли обновить переменную окружения `PATH`.
//// tab | Linux, macOS
Допустим, вы устанавливаете Python, и он оказывается в каталоге `/opt/custompython/bin`.
Если вы скажете «да», чтобы обновить переменную окружения `PATH`, то программа установки добавит `/opt/custompython/bin` в переменную окружения `PATH`.
Это может выглядеть следующим образом:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Таким образом, когда вы набираете `python` в терминале, система найдет программу Python в `/opt/custompython/bin` (последний каталог) и использует ее.
////
//// tab | Windows
Допустим, вы устанавливаете Python, и он оказывается в каталоге `C:\opt\custompython\bin`.
Если вы согласитесь обновить переменную окружения `PATH`, то программа установки добавит `C:\opt\custompython\bin` в переменную окружения `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Таким образом, когда вы набираете `python` в терминале, система найдет программу Python в `C:\opt\custompython\bin` (последний каталог) и использует ее.
////
Итак, если вы напечатаете:
```console
$ python
```
//// tab | Linux, macOS
Система **найдет** программу `python` в `/opt/custompython/bin` и запустит ее.
Это примерно эквивалентно набору текста:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
Система **найдет** программу `python` в каталоге `C:\opt\custompython\bin\python` и запустит ее.
Это примерно эквивалентно набору текста:
```console
$ C:\opt\custompython\bin\python
```
////
Эта информация будет полезна при изучении [Виртуальных окружений](virtual-environments.md).
## Вывод { #conclusion }
Благодаря этому вы должны иметь базовое представление о том, что такое **переменные окружения** и как использовать их в Python.
Подробнее о них вы также можете прочитать в [статье о переменных окружения на википедии](https://en.wikipedia.org/wiki/Environment_variable).
Во многих случаях не всегда очевидно, как переменные окружения могут быть полезны и применимы. Но они постоянно появляются в различных сценариях разработки, поэтому знать о них полезно.
Например, эта информация понадобится вам в следующем разделе, посвященном [Виртуальным окружениям](virtual-environments.md).
================================================
FILE: docs/ru/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** - это программа командной строки, которую вы можете использовать, чтобы предоставлять доступ к вашему приложению FastAPI, управлять проектом FastAPI и т.д.
При установке FastAPI (например, с помощью `pip install "fastapi[standard]"`) вместе с ним устанавливается программа командной строки, которую можно запускать в терминале.
Чтобы запустить ваше приложение FastAPI в режиме разработки, используйте команду `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Подсказка
В продакшн вы будете использовать `fastapi run` вместо `fastapi dev`. 🚀
///
Внутри **FastAPI CLI** используется [Uvicorn](https://www.uvicorn.dev), высокопроизводительный, готовый к работе в продакшн ASGI-сервер. 😎
Инструмент командной строки `fastapi` попытается автоматически обнаружить приложение FastAPI для запуска, предполагая, что это объект с именем `app` в файле `main.py` (или в некоторых других вариантах).
Но вы можете явно указать, какое приложение использовать.
## Настройте `entrypoint` приложения в `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Вы можете настроить, где находится ваше приложение, в файле `pyproject.toml`, например так:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Этот `entrypoint` сообщит команде `fastapi`, что приложение нужно импортировать так:
```python
from main import app
```
Если ваш код организован так:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Тогда следует указать `entrypoint` так:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
что будет эквивалентно:
```python
from backend.main import app
```
### `fastapi dev` с указанием пути { #fastapi-dev-with-path }
Вы также можете передать путь к файлу команде `fastapi dev`, и она постарается определить объект приложения FastAPI:
```console
$ fastapi dev main.py
```
Но тогда вам придется каждый раз не забывать передавать правильный путь при вызове команды `fastapi`.
Кроме того, другие инструменты могут не найти его, например [Расширение VS Code](editor-support.md) или [FastAPI Cloud](https://fastapicloud.com), поэтому рекомендуется использовать `entrypoint` в `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
Вызов `fastapi dev` запускает режим разработки.
По умолчанию включена авто-перезагрузка (**auto-reload**), благодаря этому при изменении кода происходит перезагрузка сервера приложения. Эта установка требует значительных ресурсов и делает систему менее стабильной. Используйте её только при разработке. Приложение слушает входящие подключения на IP `127.0.0.1`. Это IP адрес вашей машины, предназначенный для внутренних коммуникаций (`localhost`).
## `fastapi run` { #fastapi-run }
Вызов `fastapi run` по умолчанию запускает FastAPI в режиме продакшн.
По умолчанию авто-перезагрузка (**auto-reload**) отключена. Приложение слушает входящие подключения на IP `0.0.0.0`, т.е. на всех доступных адресах компьютера. Таким образом, приложение будет находиться в публичном доступе для любого, кто может подсоединиться к вашей машине. Продуктовые приложения запускаются именно так, например, с помощью контейнеров.
В большинстве случаев вы будете (и должны) использовать прокси-сервер ("termination proxy"), который будет поддерживать HTTPS поверх вашего приложения. Всё будет зависеть от того, как вы развертываете приложение: за вас это либо сделает ваш провайдер, либо вам придется сделать настройки самостоятельно.
/// tip | Подсказка
Вы можете больше узнать об этом в [документации по развертыванию](deployment/index.md).
///
================================================
FILE: docs/ru/docs/features.md
================================================
# Возможности { #features }
## Возможности FastAPI { #fastapi-features }
**FastAPI** предлагает вам следующее:
### Основано на открытых стандартах { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) для создания API, включая объявления операций пути, параметров, тел запросов, безопасности и т.д.
* Автоматическая документация моделей данных с помощью [**JSON Schema**](https://json-schema.org/) (так как сама спецификация OpenAPI основана на JSON Schema).
* Разработан вокруг этих стандартов, после тщательного их изучения. Это не дополнительная надстройка поверх.
* Это также позволяет использовать автоматическую **генерацию клиентского кода** на многих языках.
### Автоматическая документация { #automatic-docs }
Интерактивная документация для API и исследовательские веб-интерфейсы. Поскольку фреймворк основан на OpenAPI, существует несколько вариантов документирования, 2 из них включены по умолчанию.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui), с интерактивным исследованием, вызовом и тестированием вашего API прямо из браузера.

* Альтернативная документация API в [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Только современный Python { #just-modern-python }
Все основано на стандартных **аннотациях типов Python** (благодаря Pydantic). Не нужно изучать новый синтаксис. Только стандартный современный Python.
Если вам нужно освежить знания о типах в Python (даже если вы не используете FastAPI), выделите 2 минуты и просмотрите краткое руководство: [Типы Python](python-types.md).
Вы пишете стандартный Python с типами:
```Python
from datetime import date
from pydantic import BaseModel
# Объявляем параметр как `str`
# и получаем поддержку редактора кода внутри функции
def main(user_id: str):
return user_id
# Модель Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Это можно использовать так:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Информация
`**second_user_data` означает:
Передать ключи и значения словаря `second_user_data` в качестве аргументов "ключ-значение", эквивалентно: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Поддержка редакторов (IDE) { #editor-support }
Весь фреймворк был продуман так, чтобы быть простым и интуитивно понятным в использовании, все решения были проверены на множестве редакторов еще до начала разработки, чтобы обеспечить наилучшие условия при написании кода.
В опросах Python‑разработчиков видно, [что одной из самых часто используемых функций является «автозавершение»](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Вся структура **FastAPI** основана на удовлетворении этой возможности. Автозавершение работает везде.
Вам редко нужно будет возвращаться к документации.
Вот как ваш редактор может вам помочь:
* в [Visual Studio Code](https://code.visualstudio.com/):

* в [PyCharm](https://www.jetbrains.com/pycharm/):

Вы будете получать автозавершение кода даже там, где вы считали это невозможным раньше. Как пример, ключ `price` внутри тела JSON (который может быть вложенным), приходящего в запросе.
Больше никаких неправильных имён ключей, метания по документации или прокручивания кода вверх и вниз в попытках узнать — использовали вы ранее `username` или `user_name`.
### Краткость { #short }
FastAPI имеет продуманные значения **по умолчанию** для всего, с опциональными настройками везде. Все параметры могут быть тонко подстроены так, чтобы делать то, что вам нужно, и определять необходимый вам API.
Но по умолчанию всё **«просто работает»**.
### Проверка значений { #validation }
* Проверка значений для большинства (или всех?) **типов данных** Python, включая:
* Объекты JSON (`dict`).
* Массив JSON (`list`) с определёнными типами элементов.
* Строковые (`str`) поля с ограничением минимальной и максимальной длины.
* Числа (`int`, `float`) с минимальными и максимальными значениями и т.п.
* Проверка для более экзотических типов, таких как:
* URL.
* Email.
* UUID.
* ...и другие.
Все проверки обрабатываются хорошо зарекомендовавшим себя и надёжным **Pydantic**.
### Безопасность и аутентификация { #security-and-authentication }
Встроенные функции безопасности и аутентификации. Без каких‑либо компромиссов с базами данных или моделями данных.
Все схемы безопасности, определённые в OpenAPI, включая:
* HTTP Basic.
* **OAuth2** (также с **токенами JWT**). Ознакомьтесь с руководством [OAuth2 с JWT](tutorial/security/oauth2-jwt.md).
* Ключи API в:
* HTTP-заголовках.
* Параметрах запросов.
* Cookies и т.п.
Вдобавок все функции безопасности от Starlette (включая **сессионные cookies**).
Все инструменты и компоненты спроектированы для многократного использования и легко интегрируются с вашими системами, хранилищами данных, реляционными и NoSQL базами данных и т.д.
### Внедрение зависимостей { #dependency-injection }
FastAPI включает в себя чрезвычайно простую в использовании, но чрезвычайно мощную систему Внедрения зависимостей.
* Даже зависимости могут иметь зависимости, создавая иерархию или **«граф» зависимостей**.
* Всё **автоматически обрабатывается** фреймворком.
* Все зависимости могут запрашивать данные из запросов и **дополнять операции пути** ограничениями и автоматической документацией.
* **Автоматическая проверка** даже для параметров *операций пути*, определённых в зависимостях.
* Поддержка сложных систем аутентификации пользователей, **соединений с базами данных** и т.д.
* **Никаких компромиссов** с базами данных, интерфейсами и т.д. Но при этом — лёгкая интеграция со всеми ними.
### Нет ограничений на "Плагины" { #unlimited-plug-ins }
Или, другими словами, нет необходимости в них — просто импортируйте и используйте нужный вам код.
Любая интеграция разработана настолько простой в использовании (с зависимостями), что вы можете создать «плагин» для своего приложения в пару строк кода, используя ту же структуру и синтаксис, что и для ваших *операций пути*.
### Проверен { #tested }
* 100% покрытие тестами.
* 100% аннотирование типов в кодовой базе.
* Используется в продакшн‑приложениях.
## Возможности Starlette { #starlette-features }
**FastAPI** основан на [**Starlette**](https://www.starlette.dev/) и полностью совместим с ним. Так что любой дополнительный код Starlette, который у вас есть, также будет работать.
На самом деле, `FastAPI` — это подкласс `Starlette`. Таким образом, если вы уже знаете или используете Starlette, большая часть функционала будет работать так же.
С **FastAPI** вы получаете все возможности **Starlette** (так как FastAPI — это всего лишь Starlette на стероидах):
* Серьёзно впечатляющая производительность. Это [один из самых быстрых фреймворков на Python, наравне с **NodeJS** и **Go**](https://github.com/encode/starlette#performance).
* Поддержка **WebSocket**.
* Фоновые задачи в том же процессе.
* События запуска и выключения.
* Тестовый клиент построен на HTTPX.
* **CORS**, GZip, статические файлы, потоковые ответы.
* Поддержка **сессий и cookie**.
* 100% покрытие тестами.
* 100% аннотирование типов в кодовой базе.
## Возможности Pydantic { #pydantic-features }
**FastAPI** полностью совместим с (и основан на) [**Pydantic**](https://docs.pydantic.dev/). Поэтому любой дополнительный код Pydantic, который у вас есть, также будет работать.
Включая внешние библиотеки, также основанные на Pydantic, такие как ORM’ы, ODM’ы для баз данных.
Это также означает, что во многих случаях вы можете передавать тот же объект, который получили из запроса, **непосредственно в базу данных**, так как всё проверяется автоматически.
И наоборот, во многих случаях вы можете просто передать объект, полученный из базы данных, **непосредственно клиенту**.
С **FastAPI** вы получаете все возможности **Pydantic** (так как FastAPI основан на Pydantic для обработки данных):
* **Никакой нервотрёпки**:
* Не нужно изучать новые схемы в микроязыках.
* Если вы знаете типы в Python, вы знаете, как использовать Pydantic.
* Прекрасно сочетается с вашим **IDE/линтер/мозгом**:
* Потому что структуры данных pydantic — это всего лишь экземпляры классов, определённых вами; автозавершение, проверка кода, mypy и ваша интуиция — всё будет работать с вашими валидированными данными.
* Валидация **сложных структур**:
* Использование иерархических моделей Pydantic; `List`, `Dict` и т.п. из модуля `typing` (входит в стандартную библиотеку Python).
* Валидаторы позволяют чётко и легко определять, проверять и документировать сложные схемы данных в виде JSON Schema.
* У вас могут быть глубоко **вложенные объекты JSON**, и все они будут проверены и аннотированы.
* **Расширяемость**:
* Pydantic позволяет определять пользовательские типы данных или расширять проверку методами модели с помощью декораторов валидаторов.
* 100% покрытие тестами.
================================================
FILE: docs/ru/docs/help-fastapi.md
================================================
# Помочь FastAPI - Получить помощь { #help-fastapi-get-help }
Нравится ли Вам **FastAPI**?
Хотели бы Вы помочь FastAPI, другим пользователям и автору?
Или Вы хотите получить помощь по **FastAPI**?
Есть несколько очень простых способов помочь (иногда достаточно всего лишь одного-двух кликов).
И также есть несколько способов получить помощь.
## Подписаться на новостную рассылку { #subscribe-to-the-newsletter }
Вы можете подписаться на редкую [новостную рассылку **FastAPI и его друзья**](newsletter.md) и быть в курсе о:
* Новостях о FastAPI и его друзьях 🚀
* Руководствах 📝
* Возможностях ✨
* Ломающих изменениях 🚨
* Подсказках и хитростях ✅
## Подписаться на FastAPI в X (Twitter) { #follow-fastapi-on-x-twitter }
[Подписаться на @fastapi в **X (Twitter)**](https://x.com/fastapi) для получения наисвежайших новостей о **FastAPI**. 🐦
## Добавить **FastAPI** звезду на GitHub { #star-fastapi-in-github }
Вы можете добавить FastAPI "звезду" на GitHub (кликнув на кнопку звезды в правом верхнем углу): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Чем больше звёзд, тем легче другим пользователям найти проект и увидеть, что он уже оказался полезным для многих.
## Отслеживать свежие выпуски в репозитории на GitHub { #watch-the-github-repository-for-releases }
Вы можете "отслеживать" FastAPI на GitHub (кликнув по кнопке "watch" наверху справа): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Там же Вы можете выбрать "Releases only".
С такой настройкой Вы будете получать уведомления на вашу электронную почту каждый раз, когда появится новый релиз (новая версия) **FastAPI** с исправлениями ошибок и новыми возможностями.
## Связаться с автором { #connect-with-the-author }
Можно связаться со [мной (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), автором.
Вы можете:
* [Подписаться на меня на **GitHub**](https://github.com/tiangolo).
* Посмотреть другие мои проекты с открытым кодом, которые могут быть полезны Вам.
* Подписаться, чтобы видеть, когда я создаю новый проект с открытым кодом.
* [Подписаться на меня в **X (Twitter)**](https://x.com/tiangolo) или в [Mastodon](https://fosstodon.org/@tiangolo).
* Поделиться со мной, как Вы используете FastAPI (я обожаю это читать).
* Узнавать, когда я делаю объявления или выпускаю новые инструменты.
* Вы также можете [подписаться на @fastapi в X (Twitter)](https://x.com/fastapi) (это отдельный аккаунт).
* [Подписаться на меня в **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Узнавать, когда я делаю объявления или выпускаю новые инструменты (хотя чаще я использую X (Twitter) 🤷♂).
* Читать, что я пишу (или подписаться на меня) на [**Dev.to**](https://dev.to/tiangolo) или [**Medium**](https://medium.com/@tiangolo).
* Читать другие идеи, статьи и о созданных мной инструментах.
* Подписаться, чтобы читать, когда я публикую что-то новое.
## Оставить сообщение в X (Twitter) о **FastAPI** { #tweet-about-fastapi }
[Оставьте сообщение в X (Twitter) о **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) и позвольте мне и другим узнать, почему он Вам нравится. 🎉
Я люблю узнавать о том, как **FastAPI** используется, что Вам понравилось в нём, в каких проектах/компаниях Вы его используете и т.д.
## Оставить голос за FastAPI { #vote-for-fastapi }
* [Голосуйте за **FastAPI** в Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Голосуйте за **FastAPI** в AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Расскажите, что Вы используете **FastAPI** на StackShare](https://stackshare.io/pypi-fastapi).
## Помочь другим с вопросами на GitHub { #help-others-with-questions-in-github }
Вы можете попробовать помочь другим с их вопросами в:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
Во многих случаях Вы уже можете знать ответы на эти вопросы. 🤓
Если Вы много помогаете людям с их вопросами, Вы станете официальным [Экспертом FastAPI](fastapi-people.md#fastapi-experts). 🎉
Только помните, самое важное — постарайтесь быть добрыми. Люди приходят со своими разочарованиями и часто задают вопросы не лучшим образом, но постарайтесь, насколько можете, быть доброжелательными. 🤗
Идея сообщества **FastAPI** — быть доброжелательным и гостеприимным. В то же время не допускайте травлю или неуважительное поведение по отношению к другим. Мы должны заботиться друг о друге.
---
Как помочь другим с вопросами (в обсуждениях или Issues):
### Понять вопрос { #understand-the-question }
* Убедитесь, что поняли **цель** и кейс использования задающего вопрос.
* Затем проверьте, что вопрос (в подавляющем большинстве это вопросы) сформулирован **ясно**.
* Во многих случаях спрашивают о воображаемом решении пользователя, но может быть решение **получше**. Если Вы лучше поймёте проблему и кейс, сможете предложить **альтернативное решение**.
* Если вопрос непонятен, запросите больше **деталей**.
### Воспроизвести проблему { #reproduce-the-problem }
В большинстве случаев и вопросов есть что-то связанное с **исходным кодом** автора.
Во многих случаях предоставляют только фрагмент кода, но этого недостаточно, чтобы **воспроизвести проблему**.
* Попросите предоставить [минимальный воспроизводимый пример](https://stackoverflow.com/help/minimal-reproducible-example), который Вы сможете **скопировать-вставить** и запустить локально, чтобы увидеть ту же ошибку или поведение, или лучше понять их кейс.
* Если чувствуете себя особенно великодушными, можете попытаться **создать такой пример** сами, основываясь только на описании проблемы. Просто помните, что это может занять много времени, и, возможно, сначала лучше попросить уточнить проблему.
### Предложить решение { #suggest-solutions }
* После того как Вы поняли вопрос, Вы можете дать возможный **ответ**.
* Во многих случаях лучше понять **исходную проблему или кейс**, потому что может существовать способ решить её лучше, чем то, что пытаются сделать.
### Попросить закрыть { #ask-to-close }
Если Вам ответили, велика вероятность, что Вы решили их проблему, поздравляю, **Вы — герой**! 🦸
* Теперь, если проблема решена, можно попросить их:
* В GitHub Discussions: пометить комментарий как **answer** (ответ).
* В GitHub Issues: **закрыть** Issue.
## Отслеживать репозиторий на GitHub { #watch-the-github-repository }
Вы можете "отслеживать" FastAPI на GitHub (кликнув по кнопке "watch" наверху справа): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Если Вы выберете "Watching" вместо "Releases only", то будете получать уведомления, когда кто-либо создаёт новый вопрос или Issue. Вы также можете указать, что хотите получать уведомления только о новых Issues, или обсуждениях, или пулл-реквестах и т.д.
Тогда Вы можете попробовать помочь им с решением этих вопросов.
## Задать вопросы { #ask-questions }
Вы можете [создать новый вопрос](https://github.com/fastapi/fastapi/discussions/new?category=questions) в репозитории GitHub, например:
* Задать **вопрос** или спросить о **проблеме**.
* Предложить новую **возможность**.
**Заметка**: если Вы это сделаете, то я попрошу Вас также помогать другим. 😉
## Проверять пулл-реквесты { #review-pull-requests }
Вы можете помочь мне проверять пулл-реквесты других участников.
И, снова, постарайтесь быть доброжелательными. 🤗
---
О том, что нужно иметь в виду и как проверять пулл-реквест:
### Понять проблему { #understand-the-problem }
* Во-первых, убедитесь, что **поняли проблему**, которую пулл-реквест пытается решить. Возможно, это обсуждалось более подробно в GitHub Discussion или Issue.
* Также есть вероятность, что пулл-реквест не нужен, так как проблему можно решить **другим путём**. Тогда Вы можете предложить или спросить об этом.
### Не переживайте о стиле { #dont-worry-about-style }
* Не стоит слишком беспокоиться о таких вещах, как стиль сообщений в коммитах — при слиянии я выполню squash и настрою коммит вручную.
* Также не беспокойтесь о правилах стиля, это уже проверяют автоматизированные инструменты.
Если будет нужна какая-то другая стилистика или единообразие, я попрошу об этом напрямую или добавлю поверх свои коммиты с нужными изменениями.
### Проверить код { #check-the-code }
* Проверьте и прочитайте код, посмотрите, логичен ли он, **запустите его локально** и проверьте, действительно ли он решает проблему.
* Затем оставьте **комментарий**, что Вы это сделали, так я пойму, что Вы действительно проверили код.
/// info | Информация
К сожалению, я не могу просто доверять PR-ам только потому, что у них есть несколько одобрений.
Несколько раз было так, что у PR-ов было 3, 5 или больше одобрений, вероятно из-за привлекательного описания, но когда я их проверял, они оказывались сломанными, содержали баги или вовсе не решали заявленную проблему. 😅
Поэтому очень важно действительно прочитать и запустить код и сообщить мне об этом в комментарии. 🤓
///
* Если PR можно упростить, Вы можете попросить об этом, но не нужно быть слишком придирчивым — может быть много субъективных мнений (и у меня тоже 🙈), поэтому лучше сосредоточиться на фундаментальных вещах.
### Тестировать { #tests }
* Помогите мне проверить, что у PR есть **тесты**.
* Проверьте, что тесты **падают** до PR. 🚨
* Затем проверьте, что тесты **проходят** после PR. ✅
* Многие PR не имеют тестов — Вы можете **напомнить** добавить тесты или даже **предложить** некоторые тесты сами. Это одна из самых трудозатратных частей, и здесь Вы можете очень помочь.
* Затем добавьте комментарий, что Вы попробовали, чтобы я знал, что Вы это проверили. 🤓
## Создать пулл-реквест { #create-a-pull-request }
Вы можете [сделать вклад](contributing.md) в исходный код пулл-реквестами, например:
* Исправить опечатку, найденную в документации.
* Поделиться статьёй, видео или подкастом о FastAPI, которые Вы создали или нашли, [изменив этот файл](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Убедитесь, что добавили свою ссылку в начало соответствующего раздела.
* Помочь с [переводом документации](contributing.md#translations) на Ваш язык.
* Вы также можете проверять переводы, сделанные другими.
* Предложить новые разделы документации.
* Исправить существующую проблему/баг.
* Убедитесь, что добавили тесты.
* Добавить новую возможность.
* Убедитесь, что добавили тесты.
* Убедитесь, что добавили документацию, если это уместно.
## Помочь поддерживать FastAPI { #help-maintain-fastapi }
Помогите мне поддерживать **FastAPI**! 🤓
Предстоит ещё много работы, и, по большей части, **ВЫ** можете её сделать.
Основные задачи, которые Вы можете выполнить прямо сейчас:
* [Помочь другим с вопросами на GitHub](#help-others-with-questions-in-github) (смотрите секцию выше).
* [Проверять пулл-реквесты](#review-pull-requests) (смотрите секцию выше).
Именно эти две задачи **забирают больше всего времени**. Это основная работа по поддержке FastAPI.
Если Вы можете помочь мне с этим, **Вы помогаете поддерживать FastAPI** и делаете так, чтобы он продолжал **развиваться быстрее и лучше**. 🚀
## Подключиться к чату { #join-the-chat }
Подключайтесь к 👥 [серверу чата в Discord](https://discord.gg/VQjSZaeJmf) 👥 и общайтесь с другими участниками сообщества FastAPI.
/// tip | Подсказка
По вопросам — задавайте их в [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), так гораздо выше шанс, что Вы получите помощь от [Экспертов FastAPI](fastapi-people.md#fastapi-experts).
Используйте чат только для прочих общих бесед.
///
### Не используйте чат для вопросов { #dont-use-the-chat-for-questions }
Имейте в виду, что в чатах, благодаря "свободному общению", легко задать вопросы, которые слишком общие и на которые сложнее ответить, поэтому Вы можете не получить ответы.
На GitHub шаблон поможет Вам правильно сформулировать вопрос, чтобы Вам было легче получить хороший ответ или даже решить проблему самостоятельно ещё до того, как спросите. И на GitHub я могу следить за тем, чтобы всегда отвечать на всё, даже если это занимает время. А с чатами я не могу сделать этого лично. 😅
Кроме того, переписка в чатах хуже ищется, чем на GitHub, поэтому вопросы и ответы могут теряться среди остальных сообщений. И только те, что на GitHub, учитываются для получения лычки [Эксперт FastAPI](fastapi-people.md#fastapi-experts), так что вероятнее всего Вы получите больше внимания именно на GitHub.
С другой стороны, в чатах тысячи пользователей, так что почти всегда есть шанс найти там кого-то для разговора. 😄
## Спонсировать автора { #sponsor-the-author }
Если Ваш **продукт/компания** зависят от **FastAPI** или связаны с ним и Вы хотите донести до пользователей информацию о себе, Вы можете спонсировать автора (меня) через [GitHub Sponsors](https://github.com/sponsors/tiangolo). В зависимости от уровня поддержки Вы можете получить дополнительные бонусы, например, бейдж в документации. 🎁
---
Спасибо! 🚀
================================================
FILE: docs/ru/docs/history-design-future.md
================================================
# История, проектирование и будущее { #history-design-and-future }
Однажды, [один из пользователей **FastAPI** задал вопрос](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> Какова история этого проекта? Создаётся впечатление, что он явился из ниоткуда и завоевал мир за несколько недель [...]
Что ж, вот небольшая часть истории проекта.
## Альтернативы { #alternatives }
В течение нескольких лет я, возглавляя различные команды разработчиков, создавал довольно сложные API для машинного обучения, распределённых систем, асинхронных задач, баз данных NoSQL и т.д.
В рамках работы над этими проектами я исследовал, проверял и использовал многие фреймворки.
Во многом история **FastAPI** - история его предшественников.
Как написано в разделе [Альтернативы](alternatives.md):
**FastAPI** не существовал бы, если б не было более ранних работ других людей.
Они создали большое количество инструментов, которые и вдохновили меня на создание **FastAPI**.
Я всячески избегал создания нового фреймворка в течение нескольких лет. Сначала я пытался собрать все нужные возможности, которые ныне есть в **FastAPI**, используя множество различных фреймворков, плагинов и инструментов.
Но в какой-то момент не осталось другого выбора, кроме как создать что-то, что предоставляло бы все эти возможности сразу. Взять самые лучшие идеи из предыдущих инструментов и, используя введённые в Python аннотации типов (которых не было до версии 3.6), объединить их.
## Исследования { #investigation }
Используя все существовавшие ранее альтернативы, я получил возможность у каждой из них чему-то научиться, позаимствовать идеи и объединить их наилучшим образом для себя и для команд разработчиков, с которыми я работал.
Например, стало ясно, что необходимо брать за основу стандартные аннотации типов Python.
Также наилучшим подходом является использование уже существующих стандартов.
Итак, прежде чем приступить к написанию **FastAPI**, я потратил несколько месяцев на изучение OpenAPI, JSON Schema, OAuth2, и т.п. для понимания их взаимосвязей, совпадений и различий.
## Проектирование { #design }
Затем я потратил некоторое время на придумывание "API" разработчика, который я хотел иметь как пользователь (как разработчик, использующий FastAPI).
Я проверил несколько идей на самых популярных редакторах кода: PyCharm, VS Code, редакторы на базе Jedi.
Согласно последнему [опросу Python-разработчиков](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), который охватывает около 80% пользователей.
Это означает, что **FastAPI** был специально проверен на редакторах, используемых 80% Python-разработчиками. И поскольку большинство других редакторов, как правило, работают аналогичным образом, все его преимущества должны работать практически для всех редакторов.
Таким образом, я смог найти наилучшие способы сократить дублирование кода, обеспечить повсеместное автозавершение, проверку типов и ошибок и т.д.
И все это, чтобы все разработчики могли получать наилучший опыт разработки.
## Зависимости { #requirements }
Протестировав несколько вариантов, я решил, что в качестве основы буду использовать [**Pydantic**](https://docs.pydantic.dev/) и его преимущества.
По моим предложениям был изменён код этого фреймворка, чтобы сделать его полностью совместимым с JSON Schema, поддержать различные способы определения ограничений и улучшить поддержку в редакторах кода (проверки типов, автозавершение) на основе тестов в нескольких редакторах.
Во время разработки я также внес вклад в [**Starlette**](https://www.starlette.dev/), другую ключевую зависимость.
## Разработка { #development }
К тому времени, когда я начал создавать **FastAPI**, большинство необходимых деталей уже существовало, дизайн был определён, зависимости и прочие инструменты были готовы, а знания о стандартах и спецификациях были четкими и свежими.
## Будущее { #future }
Сейчас уже ясно, что **FastAPI** со своими идеями стал полезен многим людям.
При сравнении с альтернативами, выбор падает на него, поскольку он лучше подходит для множества вариантов использования.
Многие разработчики и команды уже используют **FastAPI** в своих проектах (включая меня и мою команду).
Но, тем не менее, грядёт добавление ещё многих улучшений и возможностей.
У **FastAPI** великое будущее.
И [ваша помощь](help-fastapi.md) очень ценится.
================================================
FILE: docs/ru/docs/how-to/authentication-error-status-code.md
================================================
# Использование старых статус-кодов ошибок аутентификации 403 { #use-old-403-authentication-error-status-codes }
До версии FastAPI `0.122.0`, когда встроенные утилиты безопасности возвращали ошибку клиенту после неудачной аутентификации, они использовали HTTP статус-код `403 Forbidden`.
Начиная с версии FastAPI `0.122.0`, используется более подходящий HTTP статус-код `401 Unauthorized`, и в ответе возвращается имеющий смысл HTTP-заголовок `WWW-Authenticate` в соответствии со спецификациями HTTP, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Но если по какой-то причине ваши клиенты зависят от старого поведения, вы можете вернуть его, переопределив метод `make_not_authenticated_error` в ваших Security-классах.
Например, вы можете создать подкласс `HTTPBearer`, который будет возвращать ошибку `403 Forbidden` вместо стандартной `401 Unauthorized`:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Совет
Обратите внимание, что функция возвращает экземпляр исключения, не вызывает его. Выброс выполняется остальным внутренним кодом.
///
================================================
FILE: docs/ru/docs/how-to/conditional-openapi.md
================================================
# Условный OpenAPI { #conditional-openapi }
При необходимости вы можете использовать настройки и переменные окружения, чтобы условно настраивать OpenAPI в зависимости от окружения и даже полностью его отключать.
## О безопасности, API и документации { #about-security-apis-and-docs }
Скрытие пользовательских интерфейсов документации в продакшн не должно быть способом защиты вашего API.
Это не добавляет дополнительной безопасности вашему API, операции пути (обработчики пути) всё равно будут доступны по своим путям.
Если в вашем коде есть уязвимость, она всё равно останется.
Сокрытие документации лишь усложняет понимание того, как взаимодействовать с вашим API, и может усложнить его отладку в продакшн. Это можно считать просто разновидностью [безопасности через сокрытие](https://en.wikipedia.org/wiki/Security_through_obscurity).
Если вы хотите обезопасить свой API, есть несколько более эффективных вещей, которые можно сделать, например:
* Убедитесь, что у вас чётко определены Pydantic-модели для тел запросов и ответов.
* Настройте необходимые разрешения и роли с помощью зависимостей.
* Никогда не храните пароли в открытом виде, только хэши паролей.
* Реализуйте и используйте известные криптографические инструменты, например pwdlib и JWT-токены, и т.д.
* Добавьте более тонкое управление доступом с помощью OAuth2 scopes (областей) там, где это необходимо.
* ...и т.п.
Тем не менее, у вас может быть очень специфичный случай использования, когда действительно нужно отключить документацию API для некоторых окружений (например, в продакшн) или в зависимости от настроек из переменных окружения.
## Условный OpenAPI из настроек и переменных окружения { #conditional-openapi-from-settings-and-env-vars }
Вы можете легко использовать те же настройки Pydantic, чтобы настроить сгенерированный OpenAPI и интерфейсы документации.
Например:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию — `"/openapi.json"`.
Затем используем её при создании приложения FastAPI.
Далее вы можете отключить OpenAPI (включая интерфейсы документации), установив переменную окружения `OPENAPI_URL` в пустую строку, например:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
После этого, если перейти по адресам `/openapi.json`, `/docs` или `/redoc`, вы получите ошибку `404 Not Found`, например:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/ru/docs/how-to/configure-swagger-ui.md
================================================
# Настройка Swagger UI { #configure-swagger-ui }
Вы можете настроить дополнительные [параметры Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
Чтобы настроить их, передайте аргумент `swagger_ui_parameters` при создании объекта приложения `FastAPI()` или в функцию `get_swagger_ui_html()`.
`swagger_ui_parameters` принимает словарь с настройками, которые передаются в Swagger UI напрямую.
FastAPI преобразует эти настройки в **JSON**, чтобы они были совместимы с JavaScript, поскольку именно это требуется Swagger UI.
## Отключить подсветку синтаксиса { #disable-syntax-highlighting }
Например, вы можете отключить подсветку синтаксиса в Swagger UI.
Без изменения настроек подсветка синтаксиса включена по умолчанию:
Но вы можете отключить её, установив `syntaxHighlight` в `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
…и после этого Swagger UI больше не будет показывать подсветку синтаксиса:
## Изменить тему { #change-the-theme }
Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Эта настройка изменит цветовую тему подсветки синтаксиса:
## Изменить параметры Swagger UI по умолчанию { #change-default-swagger-ui-parameters }
FastAPI включает некоторые параметры конфигурации по умолчанию, подходящие для большинства случаев.
Это включает следующие настройки по умолчанию:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Вы можете переопределить любую из них, указав другое значение в аргументе `swagger_ui_parameters`.
Например, чтобы отключить `deepLinking`, можно передать такие настройки в `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Другие параметры Swagger UI { #other-swagger-ui-parameters }
Чтобы увидеть все остальные возможные настройки, прочитайте официальную [документацию по параметрам Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Настройки только для JavaScript { #javascript-only-settings }
Swagger UI также допускает другие настройки, которые являются **чисто JavaScript-объектами** (например, JavaScript-функциями).
FastAPI также включает следующие настройки `presets` (только для JavaScript):
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Это объекты **JavaScript**, а не строки, поэтому напрямую передать их из Python-кода нельзя.
Если вам нужны такие настройки только для JavaScript, используйте один из методов выше. Переопределите *операцию пути* Swagger UI и вручную напишите любой необходимый JavaScript.
================================================
FILE: docs/ru/docs/how-to/custom-docs-ui-assets.md
================================================
# Свои статические ресурсы UI документации (самостоятельный хостинг) { #custom-docs-ui-static-assets-self-hosting }
Документация API использует **Swagger UI** и **ReDoc**, и для каждого из них нужны некоторые файлы JavaScript и CSS.
По умолчанию эти файлы отдаются с CDN.
Но это можно настроить: вы можете указать конкретный CDN или отдавать файлы самостоятельно.
## Пользовательский CDN для JavaScript и CSS { #custom-cdn-for-javascript-and-css }
Допустим, вы хотите использовать другой CDN, например `https://unpkg.com/`.
Это может быть полезно, если, например, вы живёте в стране, где некоторые URL ограничены.
### Отключить автоматическую документацию { #disable-the-automatic-docs }
Первый шаг — отключить автоматическую документацию, так как по умолчанию она использует стандартный CDN.
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Подключить пользовательскую документацию { #include-the-custom-docs }
Теперь вы можете создать *операции пути* для пользовательской документации.
Вы можете переиспользовать внутренние функции FastAPI для создания HTML-страниц документации и передать им необходимые аргументы:
* `openapi_url`: URL, по которому HTML-страница документации сможет получить схему OpenAPI для вашего API. Здесь можно использовать атрибут `app.openapi_url`.
* `title`: заголовок вашего API.
* `oauth2_redirect_url`: здесь можно использовать `app.swagger_ui_oauth2_redirect_url`, чтобы оставить значение по умолчанию.
* `swagger_js_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **JavaScript**. Это URL вашего пользовательского CDN.
* `swagger_css_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **CSS**. Это URL вашего пользовательского CDN.
Аналогично и для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Совет
*Операция пути* для `swagger_ui_redirect` — это вспомогательный эндпоинт на случай, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете аутентифицироваться и вернуться к документации API с полученными учётными данными, а затем взаимодействовать с ним, используя реальную аутентификацию OAuth2.
Swagger UI сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
///
### Создайте *операцию пути*, чтобы проверить { #create-a-path-operation-to-test-it }
Чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Тестирование { #test-it }
Теперь вы должны иметь возможность открыть свою документацию по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) и перезагрузить страницу — «ассеты» (статические файлы) будут загружаться с нового CDN.
## Самостоятельный хостинг JavaScript и CSS для документации { #self-hosting-javascript-and-css-for-docs }
Самостоятельный хостинг JavaScript и CSS может быть полезен, если, например, вам нужно, чтобы приложение продолжало работать в офлайне, без доступа к открытому Интернету, или в локальной сети.
Здесь вы увидите, как отдавать эти файлы самостоятельно, в том же приложении FastAPI, и настроить документацию на их использование.
### Структура файлов проекта { #project-file-structure }
Допустим, структура файлов вашего проекта выглядит так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Теперь создайте директорию для хранения этих статических файлов.
Новая структура файлов может выглядеть так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Скачайте файлы { #download-the-files }
Скачайте статические файлы, необходимые для документации, и поместите их в директорию `static/`.
Скорее всего, вы можете кликнуть правой кнопкой на каждой ссылке и выбрать что-то вроде «Сохранить ссылку как...».
**Swagger UI** использует файлы:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
А **ReDoc** использует файл:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
После этого структура файлов может выглядеть так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Предоставьте доступ к статическим файлам { #serve-the-static-files }
* Импортируйте `StaticFiles`.
* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Протестируйте статические файлы { #test-the-static-files }
Запустите своё приложение и откройте [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Вы должны увидеть очень длинный JavaScript-файл для **ReDoc**.
Он может начинаться примерно так:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Это подтверждает, что ваше приложение умеет отдавать статические файлы и что вы поместили файлы документации в нужное место.
Теперь можно настроить приложение так, чтобы документация использовала эти статические файлы.
### Отключить автоматическую документацию для статических файлов { #disable-the-automatic-docs-for-static-files }
Так же, как и при использовании пользовательского CDN, первым шагом будет отключение автоматической документации, так как по умолчанию она использует CDN.
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
Аналогично пользовательскому CDN, теперь вы можете создать *операции пути* для собственной документации.
Снова можно переиспользовать внутренние функции FastAPI для создания HTML-страниц документации и передать им необходимые аргументы:
* `openapi_url`: URL, по которому HTML-страница документации сможет получить схему OpenAPI для вашего API. Здесь можно использовать атрибут `app.openapi_url`.
* `title`: заголовок вашего API.
* `oauth2_redirect_url`: здесь можно использовать `app.swagger_ui_oauth2_redirect_url`, чтобы оставить значение по умолчанию.
* `swagger_js_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **JavaScript**. **Это тот файл, который теперь отдаёт ваше собственное приложение**.
* `swagger_css_url`: URL, по которому HTML для документации Swagger UI сможет получить файл **CSS**. **Это тот файл, который теперь отдаёт ваше собственное приложение**.
Аналогично и для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Совет
*Операция пути* для `swagger_ui_redirect` — это вспомогательный эндпоинт на случай, когда вы используете OAuth2.
Если вы интегрируете свой API с провайдером OAuth2, вы сможете аутентифицироваться и вернуться к документации API с полученными учётными данными, а затем взаимодействовать с ним, используя реальную аутентификацию OAuth2.
Swagger UI сделает это за вас «за кулисами», но для этого ему нужен этот вспомогательный «redirect» эндпоинт.
///
### Создайте *операцию пути* для теста статических файлов { #create-a-path-operation-to-test-static-files }
Чтобы убедиться, что всё работает, создайте *операцию пути*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Тестирование UI со статическими файлами { #test-static-files-ui }
Теперь вы можете отключить Wi‑Fi, открыть свою документацию по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) и перезагрузить страницу.
Даже без Интернета вы сможете видеть документацию к своему API и взаимодействовать с ним.
================================================
FILE: docs/ru/docs/how-to/custom-request-and-route.md
================================================
# Пользовательские классы Request и APIRoute { #custom-request-and-apiroute-class }
В некоторых случаях может понадобиться переопределить логику, используемую классами `Request` и `APIRoute`.
В частности, это может быть хорошей альтернативой логике в middleware.
Например, если вы хотите прочитать или изменить тело запроса до того, как оно будет обработано вашим приложением.
/// danger | Опасность
Это «продвинутая» возможность.
Если вы только начинаете работать с **FastAPI**, возможно, стоит пропустить этот раздел.
///
## Сценарии использования { #use-cases }
Некоторые сценарии:
* Преобразование тел запросов, не в формате JSON, в JSON (например, [`msgpack`](https://msgpack.org/index.html)).
* Распаковка тел запросов, сжатых с помощью gzip.
* Автоматическое логирование всех тел запросов.
## Обработка пользовательского кодирования тела запроса { #handling-custom-request-body-encodings }
Посмотрим как использовать пользовательский подкласс `Request` для распаковки gzip-запросов.
И подкласс `APIRoute`, чтобы использовать этот пользовательский класс запроса.
### Создать пользовательский класс `GzipRequest` { #create-a-custom-gziprequest-class }
/// tip | Совет
Это учебный пример, демонстрирующий принцип работы. Если вам нужна поддержка Gzip, вы можете использовать готовый [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware).
///
Сначала создадим класс `GzipRequest`, который переопределит метод `Request.body()` и распакует тело запроса при наличии соответствующего HTTP-заголовка.
Если в заголовке нет `gzip`, он не будет пытаться распаковывать тело.
Таким образом, один и тот же класс маршрута сможет обрабатывать как gzip-сжатые, так и несжатые запросы.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Создать пользовательский класс `GzipRoute` { #create-a-custom-gziproute-class }
Далее создадим пользовательский подкласс `fastapi.routing.APIRoute`, который будет использовать `GzipRequest`.
На этот раз он переопределит метод `APIRoute.get_route_handler()`.
Этот метод возвращает функцию. Именно эта функция получает HTTP-запрос и возвращает HTTP-ответ.
Здесь мы используем её, чтобы создать `GzipRequest` из исходного HTTP-запроса.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Технические детали
У `Request` есть атрибут `request.scope` — это просто Python-`dict`, содержащий метаданные, связанные с HTTP-запросом.
У `Request` также есть `request.receive` — функция для «получения» тела запроса.
И `dict` `scope`, и функция `receive` являются частью спецификации ASGI.
Именно этих двух компонентов — `scope` и `receive` — достаточно, чтобы создать новый экземпляр `Request`.
Чтобы узнать больше о `Request`, см. [документацию Starlette о запросах](https://www.starlette.dev/requests/).
///
Единственное, что делает по-другому функция, возвращённая `GzipRequest.get_route_handler`, — преобразует `Request` в `GzipRequest`.
Благодаря этому наш `GzipRequest` позаботится о распаковке данных (при необходимости) до передачи их в наши *операции пути*.
Дальше вся логика обработки остаётся прежней.
Но благодаря изменениям в `GzipRequest.body` тело запроса будет автоматически распаковано при необходимости, когда оно будет загружено **FastAPI**.
## Доступ к телу запроса в обработчике исключений { #accessing-the-request-body-in-an-exception-handler }
/// tip | Совет
Для решения этой задачи, вероятно, намного проще использовать `body` в пользовательском обработчике `RequestValidationError` ([Обработка ошибок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Но этот пример всё равно актуален и показывает, как взаимодействовать с внутренними компонентами.
///
Тем же подходом можно воспользоваться, чтобы получить доступ к телу запроса в обработчике исключений.
Нужно лишь обработать запрос внутри блока `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Если произойдёт исключение, экземпляр `Request` всё ещё будет в области видимости, поэтому мы сможем прочитать тело запроса и использовать его при обработке ошибки:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Пользовательский класс `APIRoute` в роутере { #custom-apiroute-class-in-a-router }
Вы также можете задать параметр `route_class` у `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
В этом примере *операции пути*, объявленные в `router`, будут использовать пользовательский класс `TimedRoute` и получат дополнительный HTTP-заголовок `X-Response-Time` в ответе с временем, затраченным на формирование ответа:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/ru/docs/how-to/extending-openapi.md
================================================
# Расширение OpenAPI { #extending-openapi }
Иногда может понадобиться изменить сгенерированную схему OpenAPI.
В этом разделе показано, как это сделать.
## Обычный процесс { #the-normal-process }
Обычный (по умолчанию) процесс выглядит так.
Приложение `FastAPI` (экземпляр) имеет метод `.openapi()`, который должен возвращать схему OpenAPI.
В процессе создания объекта приложения регистрируется *операция пути* (обработчик пути) для `/openapi.json` (или для того, что указано в вашем `openapi_url`).
Она просто возвращает JSON-ответ с результатом вызова метода приложения `.openapi()`.
По умолчанию метод `.openapi()` проверяет свойство `.openapi_schema`: если в нём уже есть данные, возвращает их.
Если нет — генерирует схему с помощью вспомогательной функции `fastapi.openapi.utils.get_openapi`.
Функция `get_openapi()` принимает параметры:
* `title`: Заголовок OpenAPI, отображается в документации.
* `version`: Версия вашего API, например `2.5.0`.
* `openapi_version`: Версия используемой спецификации OpenAPI. По умолчанию — последняя: `3.1.0`.
* `summary`: Краткое описание API.
* `description`: Описание вашего API; может включать Markdown и будет отображаться в документации.
* `routes`: Список маршрутов — это каждая зарегистрированная *операция пути*. Берутся из `app.routes`.
/// info | Информация
Параметр `summary` доступен в OpenAPI 3.1.0 и выше, поддерживается FastAPI версии 0.99.0 и выше.
///
## Переопределение значений по умолчанию { #overriding-the-defaults }
Используя информацию выше, вы можете той же вспомогательной функцией сгенерировать схему OpenAPI и переопределить любые нужные части.
Например, добавим [расширение OpenAPI ReDoc для включения собственного логотипа](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### Обычный **FastAPI** { #normal-fastapi }
Сначала напишите приложение **FastAPI** как обычно:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Измените схему OpenAPI { #modify-the-openapi-schema }
Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
Вы можете использовать свойство `.openapi_schema` как «кэш» для хранения сгенерированной схемы.
Так приложению не придётся генерировать схему каждый раз, когда пользователь открывает документацию API.
Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Переопределите метод { #override-the-method }
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Проверьте { #check-it }
Перейдите на [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) — вы увидите, что используется ваш кастомный логотип (в этом примере — логотип **FastAPI**):
================================================
FILE: docs/ru/docs/how-to/general.md
================================================
# Общее — Как сделать — Рецепты { #general-how-to-recipes }
Здесь несколько указателей на другие места в документации для общих или частых вопросов.
## Фильтрация данных — Безопасность { #filter-data-security }
Чтобы убедиться, что вы не возвращаете больше данных, чем следует, прочитайте документацию: [Руководство — Модель ответа — Возвращаемый тип](../tutorial/response-model.md).
## Оптимизация производительности ответа — Модель ответа — Возвращаемый тип { #optimize-response-performance-response-model-return-type }
Чтобы оптимизировать производительность при возврате JSON-данных, используйте возвращаемый тип или модель ответа; таким образом Pydantic выполнит сериализацию в JSON на стороне Rust, без прохождения через Python. Подробнее читайте в документации: [Руководство — Модель ответа — Возвращаемый тип](../tutorial/response-model.md).
## Теги в документации — OpenAPI { #documentation-tags-openapi }
Чтобы добавить теги к вашим *операциям пути* и группировать их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Теги](../tutorial/path-operation-configuration.md#tags).
## Краткое описание и описание в документации — OpenAPI { #documentation-summary-and-description-openapi }
Чтобы добавить краткое описание и описание к вашим *операциям пути* и отобразить их в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Краткое описание и описание](../tutorial/path-operation-configuration.md#summary-and-description).
## Описание ответа в документации — OpenAPI { #documentation-response-description-openapi }
Чтобы задать описание ответа, отображаемое в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Описание ответа](../tutorial/path-operation-configuration.md#response-description).
## Документация — пометить операцию пути устаревшей — OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Чтобы пометить *операцию пути* как устаревшую и показать это в интерфейсе документации, прочитайте документацию: [Руководство — Конфигурации операций пути — Пометить операцию пути устаревшей](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Преобразование любых данных к формату, совместимому с JSON { #convert-any-data-to-json-compatible }
Чтобы преобразовать любые данные к формату, совместимому с JSON, прочитайте документацию: [Руководство — JSON-совместимый кодировщик](../tutorial/encoder.md).
## Метаданные OpenAPI — Документация { #openapi-metadata-docs }
Чтобы добавить метаданные в вашу схему OpenAPI, включая лицензию, версию, контакты и т.д., прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md).
## Пользовательский URL OpenAPI { #openapi-custom-url }
Чтобы настроить URL OpenAPI (или удалить его), прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#openapi-url).
## URL документации OpenAPI { #openapi-docs-urls }
Чтобы изменить URL, используемые для автоматически сгенерированных пользовательских интерфейсов документации, прочитайте документацию: [Руководство — Метаданные и URL документации](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/ru/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Так как **FastAPI** основан на стандарте **ASGI**, очень легко интегрировать любую библиотеку **GraphQL**, также совместимую с ASGI.
Вы можете комбинировать обычные *операции пути* FastAPI с GraphQL в одном приложении.
/// tip | Совет
**GraphQL** решает некоторые очень специфические задачи.
У него есть как **преимущества**, так и **недостатки** по сравнению с обычными **веб-API**.
Убедитесь, что **выгоды** для вашего случая использования перевешивают **недостатки**. 🤓
///
## Библиотеки GraphQL { #graphql-libraries }
Ниже приведены некоторые библиотеки **GraphQL** с поддержкой **ASGI**. Их можно использовать с **FastAPI**:
* [Strawberry](https://strawberry.rocks/) 🍓
* С [документацией для FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* С [документацией для FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* С [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) для интеграции с ASGI
* [Graphene](https://graphene-python.org/)
* С [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL со Strawberry { #graphql-with-strawberry }
Если вам нужно или хочется работать с **GraphQL**, [**Strawberry**](https://strawberry.rocks/) — **рекомендуемая** библиотека, так как её дизайн ближе всего к дизайну **FastAPI**, всё основано на **аннотациях типов**.
В зависимости от вашего сценария использования вы можете предпочесть другую библиотеку, но если бы вы спросили меня, я, скорее всего, предложил бы попробовать **Strawberry**.
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Подробнее о Strawberry можно узнать в [документации Strawberry](https://strawberry.rocks/).
А также в документации по [интеграции Strawberry с FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Устаревший `GraphQLApp` из Starlette { #older-graphqlapp-from-starlette }
В предыдущих версиях Starlette был класс `GraphQLApp` для интеграции с [Graphene](https://graphene-python.org/).
Он был объявлен устаревшим в Starlette, но если у вас есть код, который его использовал, вы можете легко **мигрировать** на [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), который решает ту же задачу и имеет **почти идентичный интерфейс**.
/// tip | Совет
Если вам нужен GraphQL, я всё же рекомендую посмотреть [Strawberry](https://strawberry.rocks/), так как он основан на аннотациях типов, а не на пользовательских классах и типах.
///
## Подробнее { #learn-more }
Подробнее о **GraphQL** вы можете узнать в [официальной документации GraphQL](https://graphql.org/).
Также можно почитать больше о каждой из указанных выше библиотек по приведённым ссылкам.
================================================
FILE: docs/ru/docs/how-to/index.md
================================================
# Как сделать — Рецепты { #how-to-recipes }
Здесь вы найдете разные рецепты и руководства «как сделать» по **различным темам**.
Большинство из этих идей более-менее **независимы**, и в большинстве случаев вам стоит изучать их только если они напрямую относятся к **вашему проекту**.
Если что-то кажется интересным и полезным для вашего проекта, смело изучайте; в противном случае, вероятно, можно просто пропустить.
/// tip | Совет
Если вы хотите **изучить FastAPI** структурированно (рекомендуется), вместо этого читайте [Учебник - Руководство пользователя](../tutorial/index.md) по главам.
///
================================================
FILE: docs/ru/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Миграция с Pydantic v1 на Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Если у вас старое приложение FastAPI, возможно, вы используете Pydantic версии 1.
FastAPI версии 0.100.0 поддерживал либо Pydantic v1, либо v2. Он использовал ту версию, которая была установлена.
FastAPI версии 0.119.0 добавил частичную поддержку Pydantic v1 изнутри Pydantic v2 (как `pydantic.v1`), чтобы упростить миграцию на v2.
FastAPI 0.126.0 убрал поддержку Pydantic v1, при этом ещё некоторое время продолжал поддерживать `pydantic.v1`.
/// warning | Предупреждение
Команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**.
Это включает `pydantic.v1`, который больше не поддерживается в Python 3.14 и выше.
Если вы хотите использовать последние возможности Python, вам нужно убедиться, что вы используете Pydantic v2.
///
Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и **возможности FastAPI 0.119.0**, которые помогут выполнить постепенную миграцию.
## Официальное руководство { #official-guide }
У Pydantic есть официальное [руководство по миграции](https://docs.pydantic.dev/latest/migration/) с v1 на v2.
Там также описано, что изменилось, как валидации стали более корректными и строгими, возможные нюансы и т.д.
Прочитайте его, чтобы лучше понять, что изменилось.
## Тесты { #tests }
Убедитесь, что у вас есть [тесты](../tutorial/testing.md) для вашего приложения и что вы запускаете их в системе непрерывной интеграции (CI).
Так вы сможете выполнить обновление и убедиться, что всё работает как ожидается.
## `bump-pydantic` { #bump-pydantic }
Во многих случаях, когда вы используете обычные Pydantic‑модели без пользовательских настроек, вы сможете автоматизировать большую часть процесса миграции с Pydantic v1 на Pydantic v2.
Вы можете использовать [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) от той же команды Pydantic.
Этот инструмент поможет автоматически изменить большую часть кода, который нужно изменить.
После этого вы можете запустить тесты и проверить, что всё работает. Если да — на этом всё. 😎
## Pydantic v1 в v2 { #pydantic-v1-in-v2 }
Pydantic v2 включает всё из Pydantic v1 как подмодуль `pydantic.v1`. Но это больше не поддерживается в версиях Python выше 3.13.
Это означает, что вы можете установить последнюю версию Pydantic v2 и импортировать и использовать старые компоненты Pydantic v1 из этого подмодуля так, как если бы у вас был установлен старый Pydantic v1.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Поддержка FastAPI для Pydantic v1 внутри v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 изнутри Pydantic v2, чтобы упростить миграцию на v2.
Таким образом, вы можете обновить Pydantic до последней версии 2 и сменить импорты на подмодуль `pydantic.v1` — во многих случаях всё просто заработает.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Предупреждение
Имейте в виду, что так как команда Pydantic больше не поддерживает Pydantic v1 в последних версиях Python, начиная с Python 3.14, использование `pydantic.v1` также не поддерживается в Python 3.14 и выше.
///
### Pydantic v1 и v2 в одном приложении { #pydantic-v1-and-v2-on-the-same-app }
В Pydantic **не поддерживается** ситуация, когда в одной модели Pydantic v2 используются поля, определённые как модели Pydantic v1, и наоборот.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
…но в одном и том же приложении вы можете иметь отдельные модели на Pydantic v1 и v2.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же **операции пути** (обработчике пути) вашего приложения FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
В примере выше модель входных данных — это модель Pydantic v1, а модель выходных данных (указанная в `response_model=ItemV2`) — это модель Pydantic v2.
### Параметры Pydantic v1 { #pydantic-v1-parameters }
Если вам нужно использовать некоторые специфичные для FastAPI инструменты для параметров, такие как `Body`, `Query`, `Form` и т.п., с моделями Pydantic v1, вы можете импортировать их из `fastapi.temp_pydantic_v1_params`, пока завершаете миграцию на Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Мигрируйте по шагам { #migrate-in-steps }
/// tip | Совет
Сначала попробуйте `bump-pydantic`: если тесты проходят и всё работает, вы справились одной командой. ✨
///
Если `bump-pydantic` не подходит для вашего случая, вы можете использовать поддержку одновременной работы моделей Pydantic v1 и v2 в одном приложении, чтобы мигрировать на Pydantic v2 постепенно.
Сначала вы можете обновить Pydantic до последней 2-й версии и изменить импорты так, чтобы все ваши модели использовали `pydantic.v1`.
Затем вы можете начать мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶
================================================
FILE: docs/ru/docs/how-to/separate-openapi-schemas.md
================================================
# Разделять схемы OpenAPI для входа и выхода или нет { #separate-openapi-schemas-for-input-and-output-or-not }
При использовании **Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON-схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
Посмотрим, как это работает, и как это изменить при необходимости.
## Pydantic‑модели для входа и выхода { #pydantic-models-for-input-and-output }
Предположим, у вас есть Pydantic‑модель со значениями по умолчанию, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Модель для входа { #model-for-input }
Если использовать эту модель как входную, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
…то поле `description` **не будет обязательным**, потому что у него значение по умолчанию `None`.
### Входная модель в документации { #input-model-in-docs }
В документации это видно: у поля `description` нет **красной звёздочки** — оно не отмечено как обязательное:
### Модель для выхода { #model-for-output }
Но если использовать ту же модель как выходную, как здесь:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
…то, поскольку у `description` есть значение по умолчанию, если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**.
### Модель для данных ответа { #model-for-output-response-data }
Если поработать с интерактивной документацией и посмотреть ответ, то, хотя код ничего не добавил в одно из полей `description`, JSON‑ответ содержит значение по умолчанию (`null`):
Это означает, что у него **всегда будет какое‑то значение**, просто иногда это значение может быть `None` (или `null` в JSON).
Это означает, что клиентам, использующим ваш API, не нужно проверять, существует ли это значение или нет: они могут **исходить из того, что поле всегда присутствует**, но в некоторых случаях оно будет иметь значение по умолчанию `None`.
В OpenAPI это описывается тем, что поле помечается как **обязательное**, поскольку оно всегда присутствует.
Из‑за этого JSON Schema для модели может отличаться в зависимости от использования для **входа** или **выхода**:
* для **входа** `description` **не будет обязательным**
* для **выхода** оно будет **обязательным** (и при этом может быть `None`, или, в терминах JSON, `null`)
### Выходная модель в документации { #model-for-output-in-docs }
В документации это тоже видно, что **оба**: `name` и `description`, помечены **красной звёздочкой** как **обязательные**:
### Модели для входа и выхода в документации { #model-for-input-and-output-in-docs }
Если посмотреть все доступные схемы (JSON Schema) в OpenAPI, вы увидите две: `Item-Input` и `Item-Output`.
Для `Item-Input` поле `description` **не является обязательным** — красной звёздочки нет.
А для `Item-Output` `description` **обязательно** — красная звёздочка есть.
Благодаря этой возможности **Pydantic v2** документация вашего API становится более **точной**; если у вас есть сгенерированные клиенты и SDK, они тоже будут точнее, с лучшим **удобством для разработчиков** и большей консистентностью. 🎉
## Не разделять схемы { #do-not-separate-schemas }
Однако бывают случаи, когда вы хотите иметь **одну и ту же схему для входа и выхода**.
Главный сценарий — когда у вас уже есть сгенерированный клиентский код/SDK, и вы пока не хотите обновлять весь этот автогенерируемый клиентский код/SDK, вероятно, вы захотите сделать это в какой-то момент, но, возможно, не прямо сейчас.
В таком случае вы можете отключить эту функциональность в **FastAPI** с помощью параметра `separate_input_output_schemas=False`.
/// info | Информация
Поддержка `separate_input_output_schemas` появилась в FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Одна и та же схема для входной и выходной моделей в документации { #same-schema-for-input-and-output-models-in-docs }
И теперь для модели будет одна общая схема и для входа, и для выхода — только `Item`, и в ней `description` будет **не обязательным**:
================================================
FILE: docs/ru/docs/how-to/testing-database.md
================================================
# Тестирование базы данных { #testing-a-database }
Вы можете изучить базы данных, SQL и SQLModel в [документации SQLModel](https://sqlmodel.tiangolo.com/). 🤓
Есть мини-[руководство по использованию SQLModel с FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
В этом руководстве есть раздел о [тестировании SQL-баз данных](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/ru/docs/index.md
================================================
# FastAPI { #fastapi }
Фреймворк FastAPI: высокая производительность, прост в изучении, позволяет быстро писать код, готов к продакшн
---
**Документация**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com/ru)
**Исходный код**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI — это современный, быстрый (высокопроизводительный) веб-фреймворк для создания API на Python, основанный на стандартных аннотациях типов Python.
Ключевые особенности:
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance).
* **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. *
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
* **Интуитивность**: Отличная поддержка редактора кода. Автозавершение везде. Меньше времени на отладку.
* **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
* **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок.
* **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией.
* **На основе стандартов**: Основан на открытых стандартах API и полностью совместим с ними: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (ранее известный как Swagger) и [JSON Schema](https://json-schema.org/).
* оценка на основе тестов внутренней команды разработчиков, создающих продакшн-приложения.
## Спонсоры { #sponsors }
### Ключевой-спонсор { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Золотые и серебряные спонсоры { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Другие спонсоры](https://fastapi.tiangolo.com/ru/fastapi-people/#sponsors)
## Мнения { #opinions }
"_[...] В последнее время я много где использую **FastAPI**. [...] На самом деле я планирую использовать его для всех **ML-сервисов моей команды в Microsoft**. Некоторые из них интегрируются в основной продукт **Windows**, а некоторые — в продукты **Office**._"
---
"_Мы начали использовать библиотеку **FastAPI**, чтобы поднять **REST**-сервер, к которому можно обращаться за **предсказаниями**. [для Ludwig]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber(ref)
---
"_**Netflix** рада объявить об открытом релизе нашего фреймворка оркестрации **антикризисного управления**: **Dispatch**! [создан с помощью **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_Я в полном восторге от **FastAPI**. Это так весело!_"
Brian Okken - Ведущий подкаста [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855)(ref)
---
"_Честно говоря, то, что вы создали, выглядит очень солидно и отполировано. Во многих смыслах это то, чем я хотел видеть **Hug** — очень вдохновляет видеть, как кто-то это создал._"
---
"_Если вы хотите изучить один **современный фреймворк** для создания REST API, посмотрите **FastAPI** [...] Он быстрый, простой в использовании и лёгкий в изучении [...]_"
"_Мы переключились на **FastAPI** для наших **API** [...] Думаю, вам тоже понравится [...]_"
---
"_Если кто-то собирается делать продакшн-API на Python, я настоятельно рекомендую **FastAPI**. Он **прекрасно спроектирован**, **прост в использовании** и **отлично масштабируется**, стал **ключевым компонентом** нашей стратегии API-first и приводит в действие множество автоматизаций и сервисов, таких как наш Virtual TAC Engineer._"
---
## Мини-документальный фильм о FastAPI { #fastapi-mini-documentary }
В конце 2025 года вышел [мини-документальный фильм о FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE), вы можете посмотреть его онлайн:
## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis }
Если вы создаёте приложение CLI для использования в терминале вместо веб-API, посмотрите [**Typer**](https://typer.tiangolo.com/).
**Typer** — младший брат FastAPI. И он задуман как **FastAPI для CLI**. ⌨️ 🚀
## Зависимости { #requirements }
FastAPI стоит на плечах гигантов:
* [Starlette](https://www.starlette.dev/) для части, связанной с вебом.
* [Pydantic](https://docs.pydantic.dev/) для части, связанной с данными.
## Установка { #installation }
Создайте и активируйте [виртуальное окружение](https://fastapi.tiangolo.com/ru/virtual-environments/), затем установите FastAPI:
**Примечание**: Обязательно заключите `"fastapi[standard]"` в кавычки, чтобы это работало во всех терминалах.
## Пример { #example }
### Создание { #create-it }
Создайте файл `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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Или используйте async def...
Если ваш код использует `async` / `await`, используйте `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Примечание**:
Если не уверены, посмотрите раздел _«Нет времени?»_ о [`async` и `await` в документации](https://fastapi.tiangolo.com/ru/async/#in-a-hurry).
### Запуск { #run-it }
Запустите сервер командой:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
О команде fastapi dev...
Команда `fastapi dev` читает ваш файл `main.py`, находит в нём приложение **FastAPI** и запускает сервер с помощью [Uvicorn](https://www.uvicorn.dev).
По умолчанию `fastapi dev` запускается с включённой авто-перезагрузкой для локальной разработки.
Подробнее в [документации по FastAPI CLI](https://fastapi.tiangolo.com/ru/fastapi-cli/).
### Проверка { #check-it }
Откройте браузер на [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Вы увидите JSON-ответ:
```JSON
{"item_id": 5, "q": "somequery"}
```
Вы уже создали API, который:
* Получает HTTP-запросы по _путям_ `/` и `/items/{item_id}`.
* Оба _пути_ используют `GET` операции (также известные как HTTP _методы_).
* _Путь_ `/items/{item_id}` имеет _path-параметр_ `item_id`, который должен быть `int`.
* _Путь_ `/items/{item_id}` имеет необязательный `str` _параметр запроса_ `q`.
### Интерактивная документация API { #interactive-api-docs }
Перейдите на [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите автоматическую интерактивную документацию API (предоставлена [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Альтернативная документация API { #alternative-api-docs }
Теперь откройте [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Вы увидите альтернативную автоматическую документацию (предоставлена [ReDoc](https://github.com/Rebilly/ReDoc)):

## Пример обновления { #example-upgrade }
Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` HTTP-запроса.
Объявите тело запроса, используя стандартные типы Python, спасибо Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Сервер `fastapi dev` должен перезагрузиться автоматически.
### Обновление интерактивной документации API { #interactive-api-docs-upgrade }
Перейдите на [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* Интерактивная документация API будет автоматически обновлена, включая новое тело запроса:

* Нажмите кнопку «Try it out», это позволит вам заполнить параметры и напрямую взаимодействовать с API:

* Затем нажмите кнопку «Execute», интерфейс свяжется с вашим API, отправит параметры, получит результаты и отобразит их на экране:

### Обновление альтернативной документации API { #alternative-api-docs-upgrade }
Теперь откройте [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* Альтернативная документация также отразит новый параметр запроса и тело запроса:

### Подведём итоги { #recap }
Итак, вы объявляете **один раз** типы параметров, тело запроса и т.д. как параметры функции.
Вы делаете это с помощью стандартных современных типов Python.
Вам не нужно изучать новый синтаксис, методы или классы конкретной библиотеки и т.п.
Только стандартный **Python**.
Например, для `int`:
```Python
item_id: int
```
или для более сложной модели `Item`:
```Python
item: Item
```
...и с этим единственным объявлением вы получаете:
* Поддержку редактора кода, включая:
* Автозавершение.
* Проверку типов.
* Валидацию данных:
* Автоматические и понятные ошибки, когда данные некорректны.
* Валидацию даже для глубоко вложенных объектов JSON.
* Преобразование входных данных: из сети в данные и типы Python. Чтение из:
* JSON.
* Параметров пути.
* Параметров запроса.
* Cookies.
* HTTP-заголовков.
* Форм.
* Файлов.
* Преобразование выходных данных: из данных и типов Python в данные сети (например, JSON):
* Преобразование типов Python (`str`, `int`, `float`, `bool`, `list` и т.д.).
* Объекты `datetime`.
* Объекты `UUID`.
* Модели баз данных.
* ...и многое другое.
* Автоматическую интерактивную документацию API, включая 2 альтернативных интерфейса:
* Swagger UI.
* ReDoc.
---
Возвращаясь к предыдущему примеру кода, **FastAPI** будет:
* Валидировать наличие `item_id` в пути для `GET` и `PUT` HTTP-запросов.
* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` HTTP-запросов.
* Если это не так, клиент увидит полезную понятную ошибку.
* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` HTTP-запросов.
* Поскольку параметр `q` объявлен с `= None`, он необязателен.
* Без `None` он был бы обязательным (как тело запроса в случае с `PUT`).
* Для `PUT` HTTP-запросов к `/items/{item_id}` читать тело запроса как JSON:
* Проверять, что есть обязательный атрибут `name`, который должен быть `str`.
* Проверять, что есть обязательный атрибут `price`, который должен быть `float`.
* Проверять, что есть необязательный атрибут `is_offer`, который должен быть `bool`, если он присутствует.
* Всё это также будет работать для глубоко вложенных объектов JSON.
* Автоматически преобразовывать из и в JSON.
* Документировать всё с помощью OpenAPI, что может быть использовано:
* Системами интерактивной документации.
* Системами автоматической генерации клиентского кода для многих языков.
* Предоставлять 2 веб-интерфейса интерактивной документации напрямую.
---
Мы только поверхностно ознакомились, но вы уже понимаете, как всё это работает.
Попробуйте изменить строку:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...из:
```Python
... "item_name": item.name ...
```
...на:
```Python
... "item_price": item.price ...
```
...и посмотрите, как ваш редактор кода будет автоматически дополнять атрибуты и знать их типы:

Более полный пример с дополнительными возможностями см. в Учебник - Руководство пользователя.
**Осторожно, спойлер**: учебник - руководство пользователя включает:
* Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**.
* Как задать **ограничения валидации** вроде `maximum_length` или `regex`.
* Очень мощную и простую в использовании систему **внедрения зависимостей**.
* Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию.
* Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic).
* Интеграцию **GraphQL** с [Strawberry](https://strawberry.rocks) и другими библиотеками.
* Множество дополнительных функций (благодаря Starlette), таких как:
* **WebSockets**
* чрезвычайно простые тесты на основе HTTPX и `pytest`
* **CORS**
* **сессии с использованием cookie**
* ...и многое другое.
### Разверните приложение (опционально) { #deploy-your-app-optional }
При желании вы можете развернуть своё приложение FastAPI в [FastAPI Cloud](https://fastapicloud.com), присоединяйтесь к списку ожидания, если ещё не сделали этого. 🚀
Если у вас уже есть аккаунт **FastAPI Cloud** (мы пригласили вас из списка ожидания 😉), вы можете развернуть ваше приложение одной командой.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Вот и всё! Теперь вы можете открыть ваше приложение по этой ссылке. ✨
#### О FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** создан тем же автором и командой, что и **FastAPI**.
Он упрощает процесс **создания образа**, **развертывания** и **доступа** к API при минимальных усилиях.
Он переносит тот же **опыт разработчика**, что и при создании приложений на FastAPI, на их **развертывание** в облаке. 🎉
FastAPI Cloud — основной спонсор и источник финансирования для проектов с открытым исходным кодом из экосистемы *FastAPI and friends*. ✨
#### Развертывание у других облачных провайдеров { #deploy-to-other-cloud-providers }
FastAPI — это open source и стандартизированный фреймворк. Вы можете развернуть приложения FastAPI у любого облачного провайдера на ваш выбор.
Следуйте руководствам вашего облачного провайдера по развертыванию приложений FastAPI. 🤓
## Производительность { #performance }
Независимые бенчмарки TechEmpower показывают приложения **FastAPI**, работающие под управлением Uvicorn, как [один из самых быстрых доступных фреймворков Python](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), уступающий только самим Starlette и Uvicorn (используются внутри FastAPI). (*)
Чтобы узнать больше, см. раздел [Бенчмарки](https://fastapi.tiangolo.com/ru/benchmarks/).
## Зависимости { #dependencies }
FastAPI зависит от Pydantic и Starlette.
### Зависимости `standard` { #standard-dependencies }
Когда вы устанавливаете FastAPI с помощью `pip install "fastapi[standard]"`, он идёт с группой опциональных зависимостей `standard`:
Используется Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) — для проверки адресов электронной почты.
Используется Starlette:
* [`httpx`](https://www.python-httpx.org) — обязателен, если вы хотите использовать `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) — обязателен, если вы хотите использовать конфигурацию шаблонов по умолчанию.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - обязателен, если вы хотите поддерживать «парсинг» форм через `request.form()`.
Используется FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) — сервер, который загружает и «отдаёт» ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
* `fastapi-cli[standard]` — чтобы предоставить команду `fastapi`.
* Включает `fastapi-cloud-cli`, который позволяет развернуть ваше приложение FastAPI в [FastAPI Cloud](https://fastapicloud.com).
### Без зависимостей `standard` { #without-standard-dependencies }
Если вы не хотите включать опциональные зависимости `standard`, можно установить `pip install fastapi` вместо `pip install "fastapi[standard]"`.
### Без `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Если вы хотите установить FastAPI со стандартными зависимостями, но без `fastapi-cloud-cli`, установите `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Дополнительные опциональные зависимости { #additional-optional-dependencies }
Есть дополнительные зависимости, которые вы можете установить.
Дополнительные опциональные зависимости Pydantic:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) — для управления настройками.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) — дополнительные типы для использования с Pydantic.
Дополнительные опциональные зависимости FastAPI:
* [`orjson`](https://github.com/ijl/orjson) — обязателен, если вы хотите использовать `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) — обязателен, если вы хотите использовать `UJSONResponse`.
## Лицензия { #license }
Этот проект распространяется на условиях лицензии MIT.
================================================
FILE: docs/ru/docs/learn/index.md
================================================
# Обучение { #learn }
Здесь представлены вводные разделы и учебные пособия для изучения **FastAPI**.
Вы можете считать это **книгой**, **курсом**, **официальным** и рекомендуемым способом изучения FastAPI. 😎
================================================
FILE: docs/ru/docs/project-generation.md
================================================
# Шаблон Full Stack FastAPI { #full-stack-fastapi-template }
Шаблоны, хотя обычно поставляются с определённой конфигурацией, спроектированы так, чтобы быть гибкими и настраиваемыми. Это позволяет вам изменять их и адаптировать под требования вашего проекта, что делает их отличной отправной точкой. 🏁
Вы можете использовать этот шаблон для старта: в нём уже сделана значительная часть начальной настройки, безопасность, база данных и несколько эндпоинтов API.
Репозиторий GitHub: [Full Stack FastAPI Template](https://github.com/tiangolo/full-stack-fastapi-template)
## Шаблон Full Stack FastAPI — Технологический стек и возможности { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/ru) для бэкенд‑API на Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) для взаимодействия с SQL‑базой данных на Python (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), используется FastAPI, для валидации данных и управления настройками.
- 💾 [PostgreSQL](https://www.postgresql.org) в качестве SQL‑базы данных.
- 🚀 [React](https://react.dev) для фронтенда.
- 💃 Используются TypeScript, хуки, Vite и другие части современного фронтенд‑стека.
- 🎨 [Tailwind CSS](https://tailwindcss.com) и [shadcn/ui](https://ui.shadcn.com) для компонентов фронтенда.
- 🤖 Автоматически сгенерированный фронтенд‑клиент.
- 🧪 [Playwright](https://playwright.dev) для End‑to‑End тестирования.
- 🦇 Поддержка тёмной темы.
- 🐋 [Docker Compose](https://www.docker.com) для разработки и продакшн.
- 🔒 Безопасное хэширование паролей по умолчанию.
- 🔑 Аутентификация по JWT‑токенам.
- 📫 Восстановление пароля по электронной почте.
- ✅ Тесты с [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) в роли обратного прокси / балансировщика нагрузки.
- 🚢 Инструкции по развёртыванию с использованием Docker Compose, включая настройку фронтенд‑прокси Traefik для автоматического получения сертификатов HTTPS.
- 🏭 CI (continuous integration) и CD (continuous deployment) на основе GitHub Actions.
================================================
FILE: docs/ru/docs/python-types.md
================================================
# Введение в типы Python { #python-types-intro }
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять тип переменной.
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
Это всего лишь **краткое руководство / напоминание** о подсказках типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.
**FastAPI** целиком основан на этих подсказках типов — они дают ему множество преимуществ и выгод.
Но даже если вы никогда не используете **FastAPI**, вам будет полезно немного узнать о них.
/// note | Примечание
Если вы являетесь экспертом в Python и уже знаете всё о подсказках типов, переходите к следующей главе.
///
## Мотивация { #motivation }
Давайте начнем с простого примера:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Вызов этой программы выводит:
```
John Doe
```
Функция делает следующее:
* Принимает `first_name` и `last_name`.
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
* Соединяет их пробелом посередине.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Отредактируем пример { #edit-it }
Это очень простая программа.
А теперь представьте, что вы пишете её с нуля.
В какой-то момент вы бы начали определение функции, у вас были бы готовы параметры...
Но затем нужно вызвать «тот метод, который делает первую букву заглавной».
Это был `upper`? Или `uppercase`? `first_uppercase`? `capitalize`?
Тогда вы пробуете старого друга программиста — автозавершение редактора кода.
Вы вводите первый параметр функции, `first_name`, затем точку (`.`) и нажимаете `Ctrl+Space`, чтобы вызвать автозавершение.
Но, к сожалению, ничего полезного не находится:
### Добавим типы { #add-types }
Давайте изменим одну строку из предыдущей версии.
Мы поменяем ровно этот фрагмент — параметры функции — с:
```Python
first_name, last_name
```
на:
```Python
first_name: str, last_name: str
```
Вот и всё.
Это и есть «подсказки типов»:
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Это не то же самое, что объявление значений по умолчанию, как, например:
```Python
first_name="john", last_name="doe"
```
Это другая вещь.
Здесь мы используем двоеточия (`:`), а не знак равенства (`=`).
И добавление подсказок типов обычно не меняет поведение программы по сравнению с вариантом без них.
Но теперь представьте, что вы снова посередине написания этой функции, только уже с подсказками типов.
В тот же момент вы пробуете вызвать автозавершение с помощью `Ctrl+Space` — и видите:
С этим вы можете прокручивать варианты, пока не найдёте тот самый:
## Больше мотивации { #more-motivation }
Посмотрите на эту функцию — у неё уже есть подсказки типов:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:
Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Объявление типов { #declaring-types }
Вы только что увидели основное место, где объявляют подсказки типов — параметры функции.
Это также основное место, где вы будете использовать их с **FastAPI**.
### Простые типы { #simple-types }
Вы можете объявлять все стандартные типы Python, не только `str`.
Можно использовать, например:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Модуль `typing` { #typing-module }
Для некоторых дополнительных сценариев может понадобиться импортировать что-то из стандартного модуля `typing`. Например, когда вы хотите объявить, что что-то имеет «любой тип», можно использовать `Any` из `typing`:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic-типы { #generic-types }
Некоторые типы могут принимать «параметры типов» в квадратных скобках, чтобы определить их внутренние типы. Например, «список строк» объявляется как `list[str]`.
Такие типы, которые принимают параметры типов, называются **Generic-типами** или **Generics**.
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
Например, давайте определим переменную как `list` из `str`.
Объявите переменную с тем же синтаксисом двоеточия (`:`).
В качестве типа укажите `list`.
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Информация
Эти внутренние типы в квадратных скобках называются «параметрами типов».
В данном случае `str` — это параметр типа, передаваемый в `list`.
///
Это означает: «переменная `items` — это `list`, и каждый элемент этого списка — `str`».
Таким образом, ваш редактор кода сможет помогать даже при обработке элементов списка:
Без типов добиться этого почти невозможно.
Обратите внимание, что переменная `item` — один из элементов списка `items`.
И всё же редактор кода знает, что это `str`, и даёт соответствующую поддержку.
#### Tuple и Set { #tuple-and-set }
Аналогично вы бы объявили `tuple` и `set`:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Это означает:
* Переменная `items_t` — это `tuple` из 3 элементов: `int`, ещё один `int` и `str`.
* Переменная `items_s` — это `set`, и каждый элемент имеет тип `bytes`.
#### Dict { #dict }
Чтобы определить `dict`, вы передаёте 2 параметра типов, разделённые запятой.
Первый параметр типа — для ключей `dict`.
Второй параметр типа — для значений `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Это означает:
* Переменная `prices` — это `dict`:
* Ключи этого `dict` имеют тип `str` (скажем, название каждой позиции).
* Значения этого `dict` имеют тип `float` (скажем, цена каждой позиции).
#### Union { #union }
Вы можете объявить, что переменная может быть **одним из нескольких типов**, например, `int` или `str`.
Чтобы это определить, используйте вертикальную черту (`|`) для разделения обоих типов.
Это называется «объединение» (union), потому что переменная может быть чем угодно из объединения этих двух множеств типов.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Это означает, что `item` может быть `int` или `str`.
#### Возможно `None` { #possibly-none }
Вы можете объявить, что значение может иметь определённый тип, например `str`, но также может быть и `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Использование `str | None` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
### Классы как типы { #classes-as-types }
Вы также можете объявлять класс как тип переменной.
Допустим, у вас есть класс `Person` с именем:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Тогда вы можете объявить переменную типа `Person`:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
И снова вы получите полную поддержку редактора кода:
Обратите внимание, что это означает: «`one_person` — это **экземпляр** класса `Person`».
Это не означает: «`one_person` — это **класс** с именем `Person`».
## Pydantic-модели { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) — это библиотека Python для валидации данных.
Вы объявляете «форму» данных как классы с атрибутами.
И у каждого атрибута есть тип.
Затем вы создаёте экземпляр этого класса с некоторыми значениями — он провалидирует значения, преобразует их к соответствующему типу (если это применимо) и вернёт вам объект со всеми данными.
И вы получите полную поддержку редактора кода для этого результирующего объекта.
Пример из официальной документации Pydantic:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Информация
Чтобы узнать больше о [Pydantic, ознакомьтесь с его документацией](https://docs.pydantic.dev/).
///
**FastAPI** целиком основан на Pydantic.
Вы увидите намного больше всего этого на практике в [Учебник - Руководство пользователя](tutorial/index.md).
## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }
В Python также есть возможность добавлять **дополнительные метаданные** к подсказкам типов с помощью `Annotated`.
Вы можете импортировать `Annotated` из `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.
Но вы можете использовать это место в `Annotated`, чтобы передать **FastAPI** дополнительные метаданные о том, как вы хотите, чтобы ваше приложение себя вело.
Важно помнить, что **первый параметр типа**, который вы передаёте в `Annotated`, — это **фактический тип**. Всё остальное — просто метаданные для других инструментов.
Пока вам достаточно знать, что `Annotated` существует и это — стандартный Python. 😎
Позже вы увидите, насколько это **мощно**.
/// tip | Совет
Тот факт, что это **стандартный Python**, означает, что вы по-прежнему получите **лучший возможный разработческий опыт** в вашем редакторе кода, с инструментами для анализа и рефакторинга кода и т.д. ✨
А ещё ваш код будет очень совместим со множеством других инструментов и библиотек Python. 🚀
///
## Аннотации типов в **FastAPI** { #type-hints-in-fastapi }
**FastAPI** использует эти подсказки типов для выполнения нескольких задач.
С **FastAPI** вы объявляете параметры с подсказками типов и получаете:
* **Поддержку редактора кода**.
* **Проверки типов**.
...и **FastAPI** использует эти же объявления для:
* **Определения требований**: из path-параметров пути запроса, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
* **Преобразования данных**: из HTTP-запроса к требуемому типу.
* **Валидации данных**: приходящих с каждого HTTP-запроса:
* Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные некорректны.
* **Документирования** API с использованием OpenAPI:
* что затем используется пользовательскими интерфейсами автоматической интерактивной документации.
Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в [Учебник - Руководство пользователя](tutorial/index.md).
Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), **FastAPI** сделает за вас большую часть работы.
/// info | Информация
Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом будет [«шпаргалка» от `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/ru/docs/resources/index.md
================================================
# Ресурсы { #resources }
Дополнительные ресурсы, внешние ссылки и многое другое. ✈️
================================================
FILE: docs/ru/docs/translation-banner.md
================================================
/// details | 🌐 Перевод выполнен с помощью ИИ и людей
Этот перевод был сделан ИИ под руководством людей. 🤝
В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖
Вы можете улучшить этот перевод, [помогая нам лучше направлять ИИ LLM](https://fastapi.tiangolo.com/ru/contributing/#translations).
[Английская версия](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/ru/docs/tutorial/background-tasks.md
================================================
# Фоновые задачи { #background-tasks }
Вы можете создавать фоновые задачи, которые будут выполняться после возврата ответа.
Это полезно для операций, которые должны произойти после HTTP-запроса, но клиенту не обязательно ждать их завершения, чтобы получить ответ.
Например:
* Уведомления по электронной почте, отправляемые после выполнения действия:
* Так как подключение к почтовому серверу и отправка письма обычно «медленные» (несколько секунд), вы можете сразу вернуть ответ, а отправку уведомления выполнить в фоне.
* Обработка данных:
* Например, если вы получаете файл, который должен пройти через медленный процесс, вы можете вернуть ответ «Accepted» (HTTP 202) и обработать файл в фоне.
## Использование `BackgroundTasks` { #using-backgroundtasks }
Сначала импортируйте `BackgroundTasks` и объявите параметр в вашей функции‑обработчике пути с типом `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** создаст объект типа `BackgroundTasks` для вас и передаст его через этот параметр.
## Создание функции для фоновой задачи { #create-a-task-function }
Создайте функцию, которую нужно запустить как фоновую задачу.
Это обычная функция, которая может принимать параметры.
Это может быть как `async def`, так и обычная функция `def`, **FastAPI** знает, как корректно её выполнить.
В этом случае функция задачи будет записывать данные в файл (имитируя отправку письма).
Так как операция записи не использует `async` и `await`, мы определим функцию как обычную `def`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Добавление фоновой задачи { #add-the-background-task }
Внутри вашей функции‑обработчика пути передайте функцию задачи объекту фоновых задач методом `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` принимает следующие аргументы:
* Функцию задачи, которую нужно выполнить в фоне (`write_notification`).
* Последовательность позиционных аргументов, которые должны быть переданы функции задачи, в порядке (`email`).
* Любые именованные аргументы, которые должны быть переданы функции задачи (`message="some notification"`).
## Встраивание зависимостей { #dependency-injection }
Использование `BackgroundTasks` также работает с системой встраивания зависимостей, вы можете объявить параметр типа `BackgroundTasks` на нескольких уровнях: в функции‑обработчике пути, в зависимости (dependable), в подзависимости и т.д.
**FastAPI** знает, что делать в каждом случае и как переиспользовать один и тот же объект, так чтобы все фоновые задачи были объединены и затем выполнены в фоне:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
В этом примере сообщения будут записаны в файл `log.txt` после отправки ответа.
Если в запросе была строка запроса (query), она будет записана в лог фоновой задачей.
Затем другая фоновая задача, созданная в функции‑обработчике пути, запишет сообщение, используя path‑параметр `email`.
## Технические детали { #technical-details }
Класс `BackgroundTasks` приходит напрямую из [`starlette.background`](https://www.starlette.dev/background/).
Он импортируется/включается прямо в FastAPI, чтобы вы могли импортировать его из `fastapi` и избежать случайного импорта альтернативного `BackgroundTask` (без `s` на конце) из `starlette.background`.
Используя только `BackgroundTasks` (а не `BackgroundTask`), его можно применять как параметр функции‑обработчика пути, и **FastAPI** сделает остальное за вас, как при использовании объекта `Request` напрямую.
По‑прежнему можно использовать один `BackgroundTask` в FastAPI, но тогда вам нужно создать объект в своём коде и вернуть Starlette `Response`, включающий его.
Подробнее см. в [официальной документации Starlette по фоновым задачам](https://www.starlette.dev/background/).
## Предостережение { #caveat }
Если вам нужно выполнять тяжелые вычисления в фоне, и при этом они не обязательно должны запускаться тем же процессом (например, вам не нужно делиться памятью, переменными и т.п.), вам могут подойти более мощные инструменты, такие как [Celery](https://docs.celeryq.dev).
Они обычно требуют более сложной конфигурации, менеджера очереди сообщений/заданий (например, RabbitMQ или Redis), но позволяют запускать фоновые задачи в нескольких процессах и, что особенно важно, на нескольких серверах.
Но если вам нужен доступ к переменным и объектам из того же приложения **FastAPI**, или нужно выполнять небольшие фоновые задачи (например, отправку email‑уведомления), вы можете просто использовать `BackgroundTasks`.
## Резюме { #recap }
Импортируйте и используйте `BackgroundTasks` с параметрами в функциях‑обработчиках пути и зависимостях, чтобы добавлять фоновые задачи.
================================================
FILE: docs/ru/docs/tutorial/bigger-applications.md
================================================
# Большие приложения — несколько файлов { #bigger-applications-multiple-files }
При построении приложения или веб-API нам редко удается поместить всё в один файл.
**FastAPI** предоставляет удобный инструментарий, который позволяет нам структурировать приложение, сохраняя при этом всю необходимую гибкость.
/// info | Примечание
Если вы раньше использовали Flask, то это аналог шаблонов Flask (Flask's Blueprints).
///
## Пример структуры приложения { #an-example-file-structure }
Давайте предположим, что наше приложение имеет следующую структуру:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Подсказка
Есть несколько файлов `__init__.py`: по одному в каждом каталоге или подкаталоге.
Это как раз то, что позволяет импортировать код из одного файла в другой.
Например, в файле `app/main.py` может быть следующая строка:
```
from app.routers import items
```
///
* Всё помещается в каталоге `app`. В нём также находится пустой файл `app/__init__.py`. Таким образом, `app` является "Python-пакетом" (коллекцией "Python-модулей"): `app`.
* Он содержит файл `app/main.py`. Данный файл является частью Python-пакета (т.е. находится внутри каталога, содержащего файл `__init__.py`), и, соответственно, он является модулем этого пакета: `app.main`.
* Он также содержит файл `app/dependencies.py`, который также, как и `app/main.py`, является модулем: `app.dependencies`.
* Здесь также находится подкаталог `app/routers/`, содержащий `__init__.py`. Он является Python-подпакетом: `app.routers`.
* Файл `app/routers/items.py` находится внутри пакета `app/routers/`. Таким образом, он является подмодулем: `app.routers.items`.
* Точно так же `app/routers/users.py` является ещё одним подмодулем: `app.routers.users`.
* Подкаталог `app/internal/`, содержащий файл `__init__.py`, является ещё одним Python-подпакетом: `app.internal`.
* А файл `app/internal/admin.py` является ещё одним подмодулем: `app.internal.admin`.
Та же самая файловая структура приложения, но с комментариями:
```bash
.
├── app # "app" пакет
│ ├── __init__.py # этот файл превращает "app" в "Python-пакет"
│ ├── main.py # модуль "main", напр.: import app.main
│ ├── dependencies.py # модуль "dependencies", напр.: import app.dependencies
│ └── routers # подпакет "routers"
│ │ ├── __init__.py # превращает "routers" в подпакет
│ │ ├── items.py # подмодуль "items", напр.: import app.routers.items
│ │ └── users.py # подмодуль "users", напр.: import app.routers.users
│ └── internal # подпакет "internal"
│ ├── __init__.py # превращает "internal" в подпакет
│ └── admin.py # подмодуль "admin", напр.: import app.internal.admin
```
## `APIRouter` { #apirouter }
Давайте предположим, что для работы с пользователями используется отдельный файл (подмодуль) `/app/routers/users.py`.
Вы хотите отделить *операции пути*, связанные с пользователями, от остального кода, чтобы сохранить порядок.
Но это всё равно часть того же приложения/веб-API на **FastAPI** (часть того же «Python-пакета»).
С помощью `APIRouter` вы можете создать *операции пути* для этого модуля.
### Импорт `APIRouter` { #import-apirouter }
Точно так же, как и в случае с классом `FastAPI`, вам нужно импортировать и создать его «экземпляр»:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Операции пути* с `APIRouter` { #path-operations-with-apirouter }
И затем вы используете его, чтобы объявить ваши *операции пути*.
Используйте его так же, как вы использовали бы класс `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Вы можете думать об `APIRouter` как об «мини-классе `FastAPI`».
Поддерживаются все те же опции.
Все те же `parameters`, `responses`, `dependencies`, `tags` и т.д.
/// tip | Подсказка
В данном примере, в качестве названия переменной используется `router`, но вы можете использовать любое другое имя.
///
Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и ещё один `APIRouter`.
## Зависимости { #dependencies }
Мы видим, что нам понадобятся некоторые зависимости, которые будут использоваться в нескольких местах приложения.
Поэтому мы поместим их в отдельный модуль `dependencies` (`app/dependencies.py`).
Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомный HTTP-заголовок `X-Token`:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Подсказка
Для простоты мы воспользовались выдуманным заголовком.
В реальных случаях для получения наилучших результатов используйте интегрированные [утилиты безопасности](security/index.md).
///
## Ещё один модуль с `APIRouter` { #another-module-with-apirouter }
Давайте также предположим, что у вас есть эндпоинты, отвечающие за обработку «items» в вашем приложении, и они находятся в модуле `app/routers/items.py`.
У вас определены *операции пути* для:
* `/items/`
* `/items/{item_id}`
Тут всё та же структура, как и в случае с `app/routers/users.py`.
Но мы хотим поступить умнее и слегка упростить код.
Мы знаем, что все *операции пути* этого модуля имеют одинаковые:
* `prefix` пути: `/items`.
* `tags`: (один единственный тег: `items`).
* Дополнительные `responses`.
* `dependencies`: всем им нужна та зависимость `X-Token`, которую мы создали.
Таким образом, вместо того чтобы добавлять всё это в каждую *операцию пути*, мы можем добавить это в `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Так как путь каждой *операции пути* должен начинаться с `/`, как здесь:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...то префикс не должен заканчиваться символом `/`.
В нашем случае префиксом является `/items`.
Мы также можем добавить список `tags` и дополнительные `responses`, которые будут применяться ко всем *операциям пути*, включённым в этот маршрутизатор.
И ещё мы можем добавить список `dependencies`, которые будут добавлены ко всем *операциям пути* в маршрутизаторе и будут выполняться/разрешаться для каждого HTTP-запроса к ним.
/// tip | Подсказка
Обратите внимание, что так же, как и в случае с [зависимостями в декораторах *операций пути*](dependencies/dependencies-in-path-operation-decorators.md), никакое значение не будет передано в вашу *функцию-обработчик пути*.
///
В результате пути для items теперь такие:
* `/items/`
* `/items/{item_id}`
...как мы и планировали.
* Они будут помечены списком тегов, содержащим одну строку `"items"`.
* Эти «теги» особенно полезны для систем автоматической интерактивной документации (с использованием OpenAPI).
* Все они будут включать предопределённые `responses`.
* Все эти *операции пути* будут иметь список `dependencies`, вычисляемых/выполняемых перед ними.
* Если вы также объявите зависимости в конкретной *операции пути*, **они тоже будут выполнены**.
* Сначала выполняются зависимости маршрутизатора, затем [`dependencies` в декораторе](dependencies/dependencies-in-path-operation-decorators.md), и затем обычные параметрические зависимости.
* Вы также можете добавить [`Security`-зависимости с `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Подсказка
Например, с помощью зависимостей в `APIRouter` мы можем потребовать аутентификации для доступа ко всей группе *операций пути*. Даже если зависимости не добавляются по отдельности к каждой из них.
///
/// check | Заметка
Параметры `prefix`, `tags`, `responses` и `dependencies` — это (как и во многих других случаях) просто возможность **FastAPI**, помогающая избежать дублирования кода.
///
### Импорт зависимостей { #import-the-dependencies }
Этот код находится в модуле `app.routers.items`, в файле `app/routers/items.py`.
И нам нужно получить функцию зависимости из модуля `app.dependencies`, файла `app/dependencies.py`.
Поэтому мы используем относительный импорт с `..` для зависимостей:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Как работает относительный импорт { #how-relative-imports-work }
/// tip | Подсказка
Если вы прекрасно знаете, как работает импорт, переходите к следующему разделу ниже.
///
Одна точка `.`, как здесь:
```Python
from .dependencies import get_token_header
```
означает:
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* найти модуль `dependencies` (воображаемый файл `app/routers/dependencies.py`)...
* и импортировать из него функцию `get_token_header`.
Но такого файла не существует, наши зависимости находятся в файле `app/dependencies.py`.
Вспомните, как выглядит файловая структура нашего приложения:
---
Две точки `..`, как здесь:
```Python
from ..dependencies import get_token_header
```
означают:
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* перейти в родительский пакет (каталог `app/`)...
* и там найти модуль `dependencies` (файл `app/dependencies.py`)...
* и импортировать из него функцию `get_token_header`.
Это работает корректно! 🎉
---
Аналогично, если бы мы использовали три точки `...`, как здесь:
```Python
from ...dependencies import get_token_header
```
то это бы означало:
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) расположен в (каталоге `app/routers/`)...
* перейти в родительский пакет (каталог `app/`)...
* затем перейти в родительский пакет этого пакета (родительского пакета нет, `app` — верхний уровень 😱)...
* и там найти модуль `dependencies` (файл `app/dependencies.py`)...
* и импортировать из него функцию `get_token_header`.
Это ссылалось бы на какой-то пакет выше `app/`, со своим файлом `__init__.py` и т.п. Но у нас такого нет. Поэтому это вызвало бы ошибку в нашем примере. 🚨
Но теперь вы знаете, как это работает, так что можете использовать относительные импорты в своих приложениях, независимо от того, насколько они сложные. 🤓
### Добавление пользовательских `tags`, `responses` и `dependencies` { #add-some-custom-tags-responses-and-dependencies }
Мы не добавляем префикс `/items` и `tags=["items"]` к каждой *операции пути*, потому что мы добавили их в `APIRouter`.
Но мы всё равно можем добавить _ещё_ `tags`, которые будут применяться к конкретной *операции пути*, а также дополнительные `responses`, специфичные для этой *операции пути*:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Подсказка
Эта последняя операция пути будет иметь комбинацию тегов: `["items", "custom"]`.
И в документации у неё будут оба ответа: один для `404` и один для `403`.
///
## Модуль main в `FastAPI` { #the-main-fastapi }
Теперь давайте посмотрим на модуль `app/main.py`.
Именно сюда вы импортируете и именно здесь вы используете класс `FastAPI`.
Это основной файл вашего приложения, который связывает всё воедино.
И так как большая часть вашей логики теперь будет находиться в отдельных специфичных модулях, основной файл будет довольно простым.
### Импорт `FastAPI` { #import-fastapi }
Вы импортируете и создаёте класс `FastAPI` как обычно.
И мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md), которые будут объединены с зависимостями для каждого `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Импорт `APIRouter` { #import-the-apirouter }
Теперь мы импортируем другие подмодули, содержащие `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются подмодулями, входящими в один и тот же Python-пакет `app`, мы можем использовать одну точку `.` для импорта через «относительные импорты».
### Как работает импорт { #how-the-importing-works }
Этот фрагмент:
```Python
from .routers import items, users
```
означает:
* Начать в том же пакете, в котором находится этот модуль (файл `app/main.py`) расположен в (каталоге `app/`)...
* найти подпакет `routers` (каталог `app/routers/`)...
* и импортировать из него подмодули `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)...
В модуле `items` будет переменная `router` (`items.router`). Это та же самая, которую мы создали в файле `app/routers/items.py`, это объект `APIRouter`.
И затем мы делаем то же самое для модуля `users`.
Мы также могли бы импортировать их так:
```Python
from app.routers import items, users
```
/// info | Примечание
Первая версия — это «относительный импорт»:
```Python
from .routers import items, users
```
Вторая версия — это «абсолютный импорт»:
```Python
from app.routers import items, users
```
Чтобы узнать больше о Python-пакетах и модулях, прочитайте [официальную документацию Python о модулях](https://docs.python.org/3/tutorial/modules.html).
///
### Избегайте конфликтов имён { #avoid-name-collisions }
Мы импортируем подмодуль `items` напрямую, вместо того чтобы импортировать только его переменную `router`.
Это потому, что у нас также есть другая переменная с именем `router` в подмодуле `users`.
Если бы мы импортировали их одну за другой, как здесь:
```Python
from .routers.items import router
from .routers.users import router
```
то `router` из `users` перезаписал бы `router` из `items`, и мы не смогли бы использовать их одновременно.
Поэтому, чтобы иметь возможность использовать обе в одном файле, мы импортируем подмодули напрямую:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Подключение `APIRouter` для `users` и `items` { #include-the-apirouters-for-users-and-items }
Теперь давайте подключим `router` из подмодулей `users` и `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Примечание
`users.router` содержит `APIRouter` из файла `app/routers/users.py`.
А `items.router` содержит `APIRouter` из файла `app/routers/items.py`.
///
С помощью `app.include_router()` мы можем добавить каждый `APIRouter` в основное приложение `FastAPI`.
Он включит все маршруты этого маршрутизатора как часть приложения.
/// note | Технические детали
Фактически, внутри он создаст *операцию пути* для каждой *операции пути*, объявленной в `APIRouter`.
Так что под капотом всё будет работать так, как будто всё было одним приложением.
///
/// check | Заметка
При подключении маршрутизаторов не нужно беспокоиться о производительности.
Это займёт микросекунды и произойдёт только при старте.
Так что это не повлияет на производительность. ⚡
///
### Подключение `APIRouter` с пользовательскими `prefix`, `tags`, `responses` и `dependencies` { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Теперь давайте представим, что ваша организация передала вам файл `app/internal/admin.py`.
Он содержит `APIRouter` с некоторыми административными *операциями пути*, которые ваша организация использует в нескольких проектах.
Для этого примера всё будет очень просто. Но допустим, что поскольку он используется совместно с другими проектами в организации, мы не можем модифицировать его и добавить `prefix`, `dependencies`, `tags` и т.д. непосредственно в `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Но мы всё равно хотим задать пользовательский `prefix` при подключении `APIRouter`, чтобы все его *операции пути* начинались с `/admin`, хотим защитить его с помощью `dependencies`, которые у нас уже есть для этого проекта, и хотим включить `tags` и `responses`.
Мы можем объявить всё это, не изменяя исходный `APIRouter`, передав эти параметры в `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Таким образом исходный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации.
В результате в нашем приложении каждая из *операций пути* из модуля `admin` будет иметь:
* Префикс `/admin`.
* Тег `admin`.
* Зависимость `get_token_header`.
* Ответ `418`. 🍵
Но это повлияет только на этот `APIRouter` в нашем приложении, а не на любой другой код, который его использует.
Так что, например, другие проекты могут использовать тот же `APIRouter` с другим методом аутентификации.
### Подключение *операции пути* { #include-a-path-operation }
Мы также можем добавлять *операции пути* напрямую в приложение `FastAPI`.
Здесь мы делаем это... просто чтобы показать, что можем 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
и это будет работать корректно вместе со всеми другими *операциями пути*, добавленными через `app.include_router()`.
/// info | Очень технические детали
**Примечание**: это очень техническая деталь, которую, вероятно, можно **просто пропустить**.
---
`APIRouter` не «монтируются», они не изолированы от остального приложения.
Это потому, что мы хотим включить их *операции пути* в OpenAPI-схему и пользовательские интерфейсы.
Так как мы не можем просто изолировать их и «смонтировать» независимо от остального, *операции пути* «клонируются» (пересоздаются), а не включаются напрямую.
///
## Настройка `entrypoint` в `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
Так как ваш объект FastAPI `app` находится в `app/main.py`, вы можете настроить `entrypoint` в файле `pyproject.toml` следующим образом:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
это эквивалентно импорту:
```python
from app.main import app
```
Таким образом, команда `fastapi` будет знать, где найти ваше приложение.
/// Note | Примечание
Вы также можете передать путь в команду, например:
```console
$ fastapi dev app/main.py
```
Но вам придётся каждый раз помнить и указывать корректный путь при вызове команды `fastapi`.
Кроме того, другие инструменты могут не суметь его найти, например [расширение VS Code](../editor-support.md) или [FastAPI Cloud](https://fastapicloud.com), поэтому рекомендуется использовать `entrypoint` в `pyproject.toml`.
///
## Проверка автоматической документации API { #check-the-automatic-api-docs }
Теперь запустите приложение:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Откройте документацию по адресу [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите автоматическую документацию API, включая пути из всех подмодулей, с использованием корректных путей (и префиксов) и корректных тегов:
## Подключение одного и того же маршрутизатора несколько раз с разными `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
Вы можете использовать `.include_router()` несколько раз с *одним и тем же* маршрутизатором, используя разные префиксы.
Это может быть полезно, например, чтобы предоставить доступ к одному и тому же API с разными префиксами, например `/api/v1` и `/api/latest`.
Это продвинутое использование, которое вам может и не понадобиться, но оно есть на случай, если понадобится.
## Подключение `APIRouter` в другой `APIRouter` { #include-an-apirouter-in-another }
Точно так же, как вы можете подключить `APIRouter` к приложению `FastAPI`, вы можете подключить `APIRouter` к другому `APIRouter`, используя:
```Python
router.include_router(other_router)
```
Убедитесь, что вы сделали это до подключения `router` к приложению `FastAPI`, чтобы *операции пути* из `other_router` также были подключены.
================================================
FILE: docs/ru/docs/tutorial/body-fields.md
================================================
# Body - Поля { #body-fields }
Таким же способом, как вы объявляете дополнительную валидацию и метаданные в параметрах *функции обработки пути* с помощью функций `Query`, `Path` и `Body`, вы можете объявлять валидацию и метаданные внутри Pydantic моделей, используя функцию `Field` из Pydantic.
## Импорт `Field` { #import-field }
Сначала вы должны импортировать его:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Внимание
Обратите внимание, что функция `Field` импортируется непосредственно из `pydantic`, а не из `fastapi`, как все остальные функции (`Query`, `Path`, `Body` и т.д.).
///
## Объявление атрибутов модели { #declare-model-attributes }
Вы можете использовать функцию `Field` с атрибутами модели:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
Функция `Field` работает так же, как `Query`, `Path` и `Body`, у неё такие же параметры и т.д.
/// note | Технические детали
На самом деле, `Query`, `Path` и другие функции, которые вы увидите в дальнейшем, создают объекты подклассов общего класса `Param`, который сам по себе является подклассом `FieldInfo` из Pydantic.
И `Field` (из Pydantic) также возвращает экземпляр `FieldInfo`.
`Body` также напрямую возвращает объекты подкласса `FieldInfo`. И есть и другие, с которыми вы познакомитесь позже, которые являются подклассами класса `Body`.
Помните, что когда вы импортируете `Query`, `Path` и другое из `fastapi`, это фактически функции, которые возвращают специальные классы.
///
/// tip | Подсказка
Обратите внимание, что каждый атрибут модели с типом, значением по умолчанию и `Field` имеет ту же структуру, что и параметр *функции обработки пути* с `Field` вместо `Path`, `Query` и `Body`.
///
## Добавление дополнительной информации { #add-extra-information }
Вы можете объявлять дополнительную информацию в `Field`, `Query`, `Body` и т.п. Она будет включена в сгенерированную JSON схему.
Вы узнаете больше о добавлении дополнительной информации позже в документации, когда будете изучать, как задавать примеры.
/// warning | Внимание
Дополнительные ключи, переданные в функцию `Field`, также будут присутствовать в сгенерированной OpenAPI схеме вашего приложения.
Поскольку эти ключи не являются обязательной частью спецификации OpenAPI, некоторые инструменты OpenAPI, например, [валидатор OpenAPI](https://validator.swagger.io/), могут не работать с вашей сгенерированной схемой.
///
## Резюме { #recap }
Вы можете использовать функцию `Field` из Pydantic, чтобы задавать дополнительную валидацию и метаданные для атрибутов модели.
Вы также можете использовать дополнительные ключевые аргументы, чтобы добавить метаданные JSON схемы.
================================================
FILE: docs/ru/docs/tutorial/body-multiple-params.md
================================================
# Body - Множество параметров { #body-multiple-parameters }
Теперь, когда мы увидели, как использовать `Path` и `Query` параметры, давайте рассмотрим более продвинутые примеры объявления тела запроса.
## Объединение `Path`, `Query` и параметров тела запроса { #mix-path-query-and-body-parameters }
Во-первых, конечно, вы можете объединять параметры `Path`, `Query` и объявления тела запроса в своих функциях обработки, **FastAPI** автоматически определит, что с ними нужно делать.
Вы также можете объявить параметры тела запроса как необязательные, установив значение по умолчанию, равное `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Заметка
Заметьте, что в данном случае параметр `item`, который будет взят из тела запроса, необязателен. Так как было установлено значение `None` по умолчанию.
///
## Несколько параметров тела запроса { #multiple-body-parameters }
В предыдущем примере, *операции пути* ожидали тело запроса в формате JSON, с параметрами, соответствующими атрибутам `Item`, например:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Но вы также можете объявить множество параметров тела запроса, например `item` и `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
В этом случае **FastAPI** заметит, что в функции есть более одного параметра тела (два параметра, которые являются Pydantic-моделями).
Таким образом, имена параметров будут использоваться в качестве ключей (имён полей) в теле запроса, и будет ожидаться запрос следующего формата:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Заметка
Обратите внимание, что хотя параметр `item` был объявлен таким же способом, как и раньше, теперь предполагается, что он находится внутри тела с ключом `item`.
///
**FastAPI** сделает автоматическое преобразование из запроса, так что параметр `item` получит своё конкретное содержимое, и то же самое происходит с пользователем `user`.
Произойдёт проверка составных данных, и создание документации в схеме OpenAPI и автоматических документах.
## Отдельные значения в теле запроса { #singular-values-in-body }
Точно так же, как `Query` и `Path` используются для определения дополнительных данных для query и path параметров, **FastAPI** предоставляет аналогичный инструмент - `Body`.
Например, расширяя предыдущую модель, вы можете решить, что вам нужен еще один ключ `importance` в том же теле запроса, помимо параметров `item` и `user`.
Если вы объявите его без указания, какой именно объект (Path, Query, Body и т.п.) ожидаете, то, поскольку это является простым типом данных, **FastAPI** будет считать, что это query-параметр.
Но вы можете указать **FastAPI** обрабатывать его, как ещё один ключ тела запроса, используя `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
В этом случае, **FastAPI** будет ожидать тело запроса в формате:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
И всё будет работать так же - преобразование типов данных, валидация, документирование и т.д.
## Множество body и query параметров { #multiple-body-params-and-query }
Конечно, вы также можете объявлять query-параметры в любое время, дополнительно к любым body-параметрам.
Поскольку по умолчанию, отдельные значения интерпретируются как query-параметры, вам не нужно явно добавлять `Query`, вы можете просто сделать так:
```Python
q: str | None = None
```
Например:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Информация
`Body` также имеет все те же дополнительные параметры валидации и метаданных, как у `Query`, `Path` и других, которые вы увидите позже.
///
## Вложить один body-параметр { #embed-a-single-body-parameter }
Предположим, у вас есть только один body-параметр `item` из Pydantic-модели `Item`.
По умолчанию, **FastAPI** ожидает получить тело запроса напрямую.
Но если вы хотите чтобы он ожидал JSON с ключом `item` с содержимым модели внутри, также как это происходит при объявлении дополнительных body-параметров, вы можете использовать специальный параметр `embed` у типа `Body`:
```Python
item: Item = Body(embed=True)
```
так же, как в этом примере:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
В этом случае **FastAPI** будет ожидать тело запроса в формате:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
вместо этого:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Резюме { #recap }
Вы можете добавлять несколько body-параметров вашей *функции-обработчика пути*, несмотря даже на то, что запрос может содержать только одно тело.
Но **FastAPI** справится с этим, предоставит правильные данные в вашей функции, а также сделает валидацию и документацию правильной схемы *операции пути*.
Вы также можете объявить отдельные значения для получения в рамках тела запроса.
И вы можете настроить **FastAPI** таким образом, чтобы включить тело запроса в ключ, даже если объявлен только один параметр.
================================================
FILE: docs/ru/docs/tutorial/body-nested-models.md
================================================
# Body - Вложенные модели { #body-nested-models }
С помощью **FastAPI** вы можете определять, валидировать, документировать и использовать модели произвольной глубины вложенности (благодаря Pydantic).
## Поля-списки { #list-fields }
Вы можете определить атрибут как подтип. Например, Python-тип `list`:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Это приведёт к тому, что `tags` будет списком, несмотря на то, что тип его элементов не объявлен.
## Поля-списки с параметром типа { #list-fields-with-type-parameter }
В Python есть специальный способ объявлять списки с внутренними типами, или «параметрами типа»:
### Объявите `list` с параметром типа { #declare-a-list-with-a-type-parameter }
Для объявления типов, у которых есть параметры типа (внутренние типы), таких как `list`, `dict`, `tuple`, передайте внутренний(ие) тип(ы) как «параметры типа», используя квадратные скобки: `[` и `]`
```Python
my_list: list[str]
```
Это всё стандартный синтаксис Python для объявления типов.
Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами.
Таким образом, в нашем примере мы можем явно указать тип данных для поля `tags` как «список строк»:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Типы множеств { #set-types }
Но затем мы подумали и поняли, что теги не должны повторяться, вероятно, это должны быть уникальные строки.
И в Python есть специальный тип данных для множеств уникальных элементов — `set`.
Тогда мы можем объявить поле `tags` как множество строк:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
С помощью этого, даже если вы получите запрос с повторяющимися данными, они будут преобразованы в множество уникальных элементов.
И когда вы выводите эти данные, даже если исходный набор содержал дубликаты, они будут выведены в виде множества уникальных элементов.
И они также будут соответствующим образом аннотированы / задокументированы.
## Вложенные модели { #nested-models }
У каждого атрибута Pydantic-модели есть тип.
Но этот тип сам может быть другой моделью Pydantic.
Таким образом, вы можете объявлять глубоко вложенные JSON «объекты» с определёнными именами атрибутов, типами и валидацией.
Всё это может быть произвольно вложенным.
### Определение подмодели { #define-a-submodel }
Например, мы можем определить модель `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Использование подмодели как типа { #use-the-submodel-as-a-type }
Также мы можем использовать эту модель как тип атрибута:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Это означает, что **FastAPI** будет ожидать тело запроса, аналогичное этому:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Ещё раз: сделав такое объявление, с помощью **FastAPI** вы получите:
* Поддержку редактора кода (автозавершение и т.д.), даже для вложенных моделей
* Преобразование данных
* Валидацию данных
* Автоматическую документацию
## Особые типы и валидация { #special-types-and-validation }
Помимо обычных простых типов, таких как `str`, `int`, `float` и т.д., вы можете использовать более сложные простые типы, которые наследуются от `str`.
Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с [обзором типов Pydantic](https://docs.pydantic.dev/latest/concepts/types/). Вы увидите некоторые примеры в следующей главе.
Например, так как в модели `Image` у нас есть поле `url`, то мы можем объявить его как тип `HttpUrl` из Pydantic вместо типа `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON Schema / OpenAPI как таковая.
## Атрибуты, содержащие списки подмоделей { #attributes-with-lists-of-submodels }
Вы также можете использовать модели Pydantic в качестве подтипов для `list`, `set` и т.д.:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д.) JSON-содержимое в следующем формате:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Информация
Заметьте, что теперь у ключа `images` есть список объектов изображений.
///
## Глубоко вложенные модели { #deeply-nested-models }
Вы можете определять модели с произвольным уровнем вложенности:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Информация
Заметьте, что у объекта `Offer` есть список объектов `Item`, которые, в свою очередь, могут содержать необязательный список объектов `Image`
///
## Тела с чистыми списками элементов { #bodies-of-pure-lists }
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:
```Python
images: list[Image]
```
например так:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Поддержка редактора кода везде { #editor-support-everywhere }
И вы получаете поддержку редактора кода везде.
Даже для элементов внутри списков:
Вы не могли бы получить такую поддержку редактора кода, если бы работали напрямую с `dict`, а не с моделями Pydantic.
Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON.
## Тела запросов с произвольными словарями (`dict`) { #bodies-of-arbitrary-dicts }
Вы также можете объявить тело запроса как `dict` с ключами определённого типа и значениями другого типа.
Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic).
Это было бы полезно, если вы хотите получить ключи, которые вы ещё не знаете.
---
Другой полезный случай — когда вы хотите, чтобы ключи были другого типа данных, например, `int`.
Именно это мы сейчас и увидим здесь.
В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Совет
Имейте в виду, что JSON поддерживает только ключи типа `str`.
Но Pydantic обеспечивает автоматическое преобразование данных.
Это значит, что даже если клиенты вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматически преобразует и валидирует эти данные.
А `dict`, который вы получите как `weights`, действительно будет иметь ключи типа `int` и значения типа `float`.
///
## Резюме { #recap }
С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода.
И дополнительно вы получаете:
* Поддержку редактора кода (автозавершение доступно везде!)
* Преобразование данных (также известно как парсинг / сериализация)
* Валидацию данных
* Документацию схемы данных
* Автоматическую генерацию документации
================================================
FILE: docs/ru/docs/tutorial/body-updates.md
================================================
# Тело запроса - Обновления { #body-updates }
## Обновление с заменой при помощи `PUT` { #update-replacing-with-put }
Чтобы обновить элемент, вы можете использовать операцию [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT).
Вы можете использовать `jsonable_encoder`, чтобы преобразовать входные данные в данные, которые можно сохранить как JSON (например, в NoSQL-базе данных). Например, преобразование `datetime` в `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` используется для получения данных, которые должны заменить существующие данные.
### Предупреждение о замене { #warning-about-replacing }
Это означает, что если вы хотите обновить элемент `bar`, используя `PUT` с телом, содержащим:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
поскольку оно не включает уже сохраненный атрибут `"tax": 20.2`, входная модель примет значение по умолчанию `"tax": 10.5`.
И данные будут сохранены с этим «новым» `tax`, равным `10.5`.
## Частичное обновление с помощью `PATCH` { #partial-updates-with-patch }
Также можно использовать операцию [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) для частичного обновления данных.
Это означает, что можно передавать только те данные, которые необходимо обновить, оставляя остальные нетронутыми.
/// note | Примечание
`PATCH` менее распространен и известен, чем `PUT`.
А многие команды используют только `PUT`, даже для частичного обновления.
Вы можете **свободно** использовать их как угодно, **FastAPI** не накладывает никаких ограничений.
Но в данном руководстве более или менее понятно, как они должны использоваться.
///
### Использование параметра `exclude_unset` в Pydantic { #using-pydantics-exclude-unset-parameter }
Если вы хотите получать частичные обновления, очень полезно использовать параметр `exclude_unset` в `.model_dump()` модели Pydantic.
Например, `item.model_dump(exclude_unset=True)`.
В результате будет сгенерирован `dict`, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию.
Затем вы можете использовать это для создания `dict` только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Использование параметра `update` в Pydantic { #using-pydantics-update-parameter }
Теперь можно создать копию существующей модели, используя `.model_copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления.
Например, `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Кратко о частичном обновлении { #partial-updates-recap }
В целом, для применения частичных обновлений необходимо:
* (Опционально) использовать `PATCH` вместо `PUT`.
* Извлечь сохранённые данные.
* Поместить эти данные в Pydantic-модель.
* Сгенерировать `dict` без значений по умолчанию из входной модели (с использованием `exclude_unset`).
* Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять уже сохраненные значения значениями по умолчанию из вашей модели.
* Создать копию хранимой модели, обновив ее атрибуты полученными частичными обновлениями (с помощью параметра `update`).
* Преобразовать скопированную модель в то, что может быть сохранено в вашей БД (например, с помощью `jsonable_encoder`).
* Это сравнимо с повторным использованием метода модели `.model_dump()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`.
* Сохранить данные в своей БД.
* Вернуть обновленную модель.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Подсказка
На самом деле эту же технику можно использовать и для операции HTTP `PUT`.
Но в приведенном примере используется `PATCH`, поскольку он был создан именно для таких случаев использования.
///
/// note | Примечание
Обратите внимание, что входная модель по-прежнему валидируется.
Таким образом, если вы хотите получать частичные обновления, в которых могут быть опущены все атрибуты, вам необходимо иметь модель, в которой все атрибуты помечены как необязательные (со значениями по умолчанию или `None`).
Чтобы отличить модели со всеми необязательными значениями для обновления и модели с обязательными значениями для создания, можно воспользоваться идеями, описанными в [Дополнительные модели](extra-models.md).
///
================================================
FILE: docs/ru/docs/tutorial/body.md
================================================
# Тело запроса { #request-body }
Когда вам необходимо отправить данные из клиента (например, браузера) в ваш API, вы отправляете их как **тело запроса**.
Тело **запроса** — это данные, отправляемые клиентом в ваш API. Тело **ответа** — это данные, которые ваш API отправляет клиенту.
Ваш API почти всегда должен отправлять тело **ответа**. Но клиентам не обязательно всегда отправлять **тело запроса**: иногда они запрашивают только путь, возможно с некоторыми параметрами запроса, но без тела.
Чтобы объявить тело **запроса**, используйте модели [Pydantic](https://docs.pydantic.dev/), со всей их мощью и преимуществами.
/// info | Информация
Чтобы отправить данные, используйте один из методов: `POST` (чаще всего), `PUT`, `DELETE` или `PATCH`.
Отправка тела с запросом `GET` имеет неопределённое поведение в спецификациях, тем не менее это поддерживается FastAPI, но только для очень сложных/крайних случаев использования.
Поскольку это не рекомендуется, интерактивная документация со Swagger UI не будет отображать информацию для тела при использовании `GET`, а промежуточные прокси-серверы могут не поддерживать такой вариант запроса.
///
## Импортируйте `BaseModel` из Pydantic { #import-pydantics-basemodel }
Первое, что нужно сделать, — импортировать `BaseModel` из пакета `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Создайте модель данных { #create-your-data-model }
Затем опишите свою модель данных как класс, наследующийся от `BaseModel`.
Используйте стандартные типы Python для всех атрибутов:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Так же, как при объявлении параметров запроса: когда атрибут модели имеет значение по умолчанию, он не обязателен. Иначе он обязателен. Используйте `None`, чтобы сделать его просто необязательным.
Например, модель выше описывает такой JSON "`object`" (или Python `dict`):
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "`object`" тоже будет корректным:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Объявите её как параметр { #declare-it-as-a-parameter }
Чтобы добавить её в вашу *операцию пути*, объявите её так же, как вы объявляли параметры пути и параметры запроса:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...и укажите тип параметра как созданную вами модель, `Item`.
## Результаты { #results }
Всего лишь с этой аннотацией типов Python **FastAPI**:
* Считает тело запроса как JSON.
* Приведёт данные к соответствующим типам (если потребуется).
* Проведёт валидацию данных.
* Если данные некорректны, вернёт понятную и наглядную ошибку, указывающую, где именно и что было некорректно.
* Передаст полученные данные в параметр `item`.
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т.п.) для всех атрибутов и их типов.
* Сгенерирует определения [JSON Schema](https://json-schema.org) для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией UIs.
## Автоматическая документация { #automatic-docs }
JSON Schema ваших моделей будет частью сгенерированной схемы OpenAPI и будет отображаться в интерактивной документации API:
А также они будут использоваться в документации API внутри каждой *операции пути*, где это требуется:
## Поддержка редактора кода { #editor-support }
В вашем редакторе кода внутри функции вы получите подсказки по типам и автозавершение повсюду (этого бы не было, если бы вы получали `dict` вместо модели Pydantic):
Также вы получите проверку ошибок при некорректных операциях с типами:
Это не случайность — весь фреймворк построен вокруг такого дизайна.
И это было тщательно протестировано ещё на этапе проектирования, до реализации, чтобы убедиться, что всё будет работать со всеми редакторами.
В сам Pydantic даже были внесены некоторые изменения для поддержки этого.
Предыдущие скриншоты сделаны в [Visual Studio Code](https://code.visualstudio.com).
Но вы получите такую же поддержку редактора кода в [PyCharm](https://www.jetbrains.com/pycharm/) и большинстве других редакторов Python:
/// tip | Совет
Если вы используете [PyCharm](https://www.jetbrains.com/pycharm/) в качестве редактора кода, вы можете использовать плагин [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
Он улучшает поддержку моделей Pydantic в редакторе кода, включая:
* автозавершение
* проверки типов
* рефакторинг
* поиск
* инспекции
///
## Использование модели { #use-the-model }
Внутри функции вам доступны все атрибуты объекта модели напрямую:
{* ../../docs_src/body/tutorial002_py310.py *}
## Тело запроса + параметры пути { #request-body-path-parameters }
Вы можете одновременно объявить параметры пути и тело запроса.
**FastAPI** распознает, что параметры функции, соответствующие параметрам пути, должны быть **получены из пути**, а параметры функции, объявленные как модели Pydantic, должны быть **получены из тела запроса**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Тело запроса + параметры пути + параметры запроса { #request-body-path-query-parameters }
Вы также можете одновременно объявить параметры **тела**, **пути** и **запроса**.
**FastAPI** распознает каждый из них и возьмёт данные из правильного источника.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Параметры функции будут распознаны следующим образом:
* Если параметр также объявлен в **пути**, он будет использоваться как path-параметр.
* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т.п.), он будет интерпретирован как параметр **запроса**.
* Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса.
/// note | Заметка
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
Аннотация типов `str | None` не используется FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
///
## Без Pydantic { #without-pydantic }
Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Тело запроса - Несколько параметров: Единичные значения в теле](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/ru/docs/tutorial/cookie-param-models.md
================================================
# Модели параметров cookie { #cookie-parameter-models }
Если у вас есть группа **cookies**, которые связаны между собой, вы можете создать **Pydantic-модель** для их объявления. 🍪
Это позволит вам **переиспользовать модель** в **разных местах**, а также объявить проверки и метаданные сразу для всех параметров. 😎
/// note | Заметка
Этот функционал доступен с версии `0.115.0`. 🤓
///
/// tip | Совет
Такой же подход применяется для `Query`, `Cookie`, и `Header`. 😎
///
## Pydantic-модель для cookies { #cookies-with-a-pydantic-model }
Объявите параметры **cookie**, которые вам нужны, в **Pydantic-модели**, а затем объявите параметр как `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** **извлечёт** данные для **каждого поля** из **cookies**, полученных в запросе, и выдаст вам объявленную Pydantic-модель.
## Проверка сгенерированной документации { #check-the-docs }
Вы можете посмотреть объявленные cookies в графическом интерфейсе Документации по пути `/docs`:
/// info | Дополнительная информация
Имейте в виду, что, поскольку **браузеры обрабатывают cookies** особым образом и под капотом, они **не** позволят **JavaScript** легко получить доступ к ним.
Если вы перейдёте к **графическому интерфейсу документации API** по пути `/docs`, то сможете увидеть **документацию** по cookies для ваших *операций путей*.
Но даже если вы **заполните данные** и нажмёте "Execute", поскольку графический интерфейс Документации работает с **JavaScript**, cookies не будут отправлены, и вы увидите сообщение об **ошибке** как будто не указывали никаких значений.
///
## Запрет дополнительных cookies { #forbid-extra-cookies }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** cookies, которые вы хотите получать.
Теперь у вашего API есть возможность контролировать своё согласие на использование cookie. 🤪🍪
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) любые дополнительные (`extra`) поля:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Если клиент попробует отправить **дополнительные cookies**, то в ответ он получит **ошибку**.
Бедные баннеры cookies, они всеми силами пытаются получить ваше согласие — и всё ради того, чтобы API его отклонил. 🍪
Например, если клиент попытается отправить cookie `santa_tracker` со значением `good-list-please`, то в ответ он получит **ошибку**, сообщающую ему, что `santa_tracker` cookie не разрешён:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Заключение { #summary }
Вы можете использовать **Pydantic-модели** для объявления **cookies** в **FastAPI**. 😎
================================================
FILE: docs/ru/docs/tutorial/cookie-params.md
================================================
# Параметры Cookie { #cookie-parameters }
Вы можете задать параметры Cookie таким же способом, как `Query` и `Path` параметры.
## Импорт `Cookie` { #import-cookie }
Сначала импортируйте `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Объявление параметров `Cookie` { #declare-cookie-parameters }
Затем объявляйте параметры cookie, используя ту же структуру, что и с `Path` и `Query`.
Вы можете задать значение по умолчанию, а также все дополнительные параметры валидации или аннотации:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Технические детали
`Cookie` - это класс, родственный `Path` и `Query`. Он также наследуется от общего класса `Param`.
Но помните, что когда вы импортируете `Query`, `Path`, `Cookie` и другое из `fastapi`, это фактически функции, которые возвращают специальные классы.
///
/// info | Дополнительная информация
Для объявления cookies, вам нужно использовать `Cookie`, иначе параметры будут интерпретированы как параметры запроса.
///
/// info | Дополнительная информация
Имейте в виду, что, поскольку **браузеры обрабатывают cookies** особым образом и «за кулисами», они **не** позволяют **JavaScript** просто так получать к ним доступ.
Если вы откроете **интерфейс документации API** на `/docs`, вы сможете увидеть **документацию** по cookies для ваших *операции пути*.
Но даже если вы **заполните данные** и нажмёте «Execute», поскольку UI документации работает с **JavaScript**, cookies отправлены не будут, и вы увидите сообщение об **ошибке**, как будто вы не указали никаких значений.
///
## Резюме { #recap }
Объявляйте cookies с помощью `Cookie`, используя тот же общий шаблон, что и `Query`, и `Path`.
================================================
FILE: docs/ru/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS или "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) относится к ситуациям, при которых запущенный в браузере фронтенд содержит JavaScript-код, который взаимодействует с бэкендом, находящимся на другом "источнике" ("origin").
## Источник { #origin }
Источник — это совокупность протокола (`http`, `https`), домена (`myapp.com`, `localhost`, `localhost.tiangolo.com`) и порта (`80`, `443`, `8080`).
Поэтому это три разных источника:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Даже если они все расположены в `localhost`, они используют разные протоколы или порты, а значит, являются разными источниками.
## Шаги { #steps }
Допустим, у вас есть фронтенд, запущенный в браузере по адресу `http://localhost:8080`, и его JavaScript-код пытается взаимодействовать с бэкендом, запущенным по адресу `http://localhost` (поскольку мы не указали порт, браузер по умолчанию будет использовать порт `80`).
Затем браузер отправит на бэкенд на `:80` HTTP-запрос `OPTIONS`, и если бэкенд вернёт соответствующие HTTP-заголовки, авторизующие взаимодействие с другим источником (`http://localhost:8080`), то браузер на `:8080` разрешит JavaScript на фронтенде отправить свой запрос на бэкенд на `:80`.
Чтобы это работало, у бэкенда на `:80` должен быть список "разрешённых источников" ("allowed origins").
В таком случае этот список должен содержать `http://localhost:8080`, чтобы фронтенд на `:8080` работал корректно.
## Подстановочный символ "*" { #wildcards }
В качестве списка источников можно указать подстановочный символ `"*"` ("wildcard"), чтобы разрешить любые источники.
Но тогда будут разрешены только некоторые виды взаимодействия, и всё, что связано с учётными данными, будет исключено: куки, HTTP-заголовки Authorization, как при использовании Bearer-токенов, и т.п.
Поэтому, чтобы всё работало корректно, лучше явно указывать список разрешённых источников.
## Использование `CORSMiddleware` { #use-corsmiddleware }
Вы можете настроить это в вашем **FastAPI**-приложении, используя `CORSMiddleware`.
* Импортируйте `CORSMiddleware`.
* Создайте список разрешённых источников (в виде строк).
* Добавьте его как "middleware" (промежуточный слой) к вашему **FastAPI**-приложению.
Вы также можете указать, разрешает ли ваш бэкенд использование:
* Учётных данных (HTTP-заголовки Authorization, куки и т.п.).
* Отдельных HTTP-методов (`POST`, `PUT`) или всех вместе, используя `"*"`.
* Отдельных HTTP-заголовков или всех вместе, используя `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
`CORSMiddleware` использует "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
Поддерживаются следующие аргументы:
* `allow_origins` - Список источников, на которые разрешено выполнять кросс-доменные запросы. Например, `['https://example.org', 'https://www.example.org']`. Можно использовать `['*']`, чтобы разрешить любые источники.
* `allow_origin_regex` - Регулярное выражение для определения источников, на которые разрешено выполнять кросс-доменные запросы. Например, `'https://.*\.example\.org'`.
* `allow_methods` - Список HTTP-методов, которые разрешены для кросс-доменных запросов. По умолчанию `['GET']`. Можно использовать `['*']`, чтобы разрешить все стандартные методы.
* `allow_headers` - Список HTTP-заголовков запроса, которые должны поддерживаться при кросс-доменных запросах. По умолчанию `[]`. Можно использовать `['*']`, чтобы разрешить все заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` и `Content-Type` всегда разрешены для [простых CORS-запросов](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Указывает, что куки разрешены в кросс-доменных запросах. По умолчанию `False`.
Ни один из параметров `allow_origins`, `allow_methods` и `allow_headers` не может быть установлен в `['*']`, если `allow_credentials` имеет значение `True`. Все они должны быть [указаны явно](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Указывает любые заголовки ответа, которые должны быть доступны браузеру. По умолчанию `[]`.
* `max_age` - Устанавливает максимальное время в секундах, в течение которого браузер кэширует CORS-ответы. По умолчанию `600`.
`CORSMiddleware` отвечает на два типа HTTP-запросов...
### CORS-запросы с предварительной проверкой { #cors-preflight-requests }
Это любые `OPTIONS`-запросы с заголовками `Origin` и `Access-Control-Request-Method`.
В этом случае middleware перехватит входящий запрос и отправит соответствующие CORS-заголовки в ответе, а также ответ `200` или `400` в информационных целях.
### Простые запросы { #simple-requests }
Любые запросы с заголовком `Origin`. В этом случае middleware передаст запрос дальше как обычно, но добавит соответствующие CORS-заголовки к ответу.
## Больше информации { #more-info }
Для получения более подробной информации о CORS обратитесь к [документации CORS от Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Технические детали
Вы также можете использовать `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** предоставляет несколько middleware в `fastapi.middleware` только для вашего удобства как разработчика. Но большинство доступных middleware взяты напрямую из Starlette.
///
================================================
FILE: docs/ru/docs/tutorial/debugging.md
================================================
# Отладка { #debugging }
Вы можете подключить отладчик в своем редакторе, например, в Visual Studio Code или PyCharm.
## Вызов `uvicorn` { #call-uvicorn }
В вашем FastAPI приложении, импортируйте и вызовите `uvicorn` напрямую:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Описание `__name__ == "__main__"` { #about-name-main }
Главная цель использования `__name__ == "__main__"` в том, чтобы код выполнялся при запуске файла с помощью:
```console
$ python myapp.py
```
но не вызывался, когда другой файл импортирует это, например:
```Python
from myapp import app
```
#### Больше деталей { #more-details }
Давайте назовём ваш файл `myapp.py`.
Если вы запустите его с помощью:
```console
$ python myapp.py
```
то встроенная переменная `__name__`, автоматически создаваемая Python в вашем файле, будет иметь значение строкового типа `"__main__"`.
Тогда эта часть кода:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
будет запущена.
---
Но этого не произойдет, если вы импортируете этот модуль (файл).
Таким образом, если у вас есть файл `importer.py` с таким импортом:
```Python
from myapp import app
# Еще немного кода
```
то автоматическая создаваемая внутри файла `myapp.py` переменная `__name__` будет иметь значение отличающееся от `"__main__"`.
Следовательно, строка:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
не будет выполнена.
/// info | Информация
Для получения дополнительной информации, ознакомьтесь с [официальной документацией Python](https://docs.python.org/3/library/__main__.html).
///
## Запуск вашего кода с помощью отладчика { #run-your-code-with-your-debugger }
Так как вы запускаете сервер Uvicorn непосредственно из вашего кода, вы можете вызвать Python программу (ваше FastAPI приложение) напрямую из отладчика.
---
Например, в Visual Studio Code вы можете выполнить следующие шаги:
* Перейдите на панель "Debug".
* Выберите "Add configuration...".
* Выберите "Python"
* Запустите отладчик "`Python: Current File (Integrated Terminal)`".
Это запустит сервер с вашим **FastAPI** кодом, остановится на точках останова, и т.д.
Вот как это может выглядеть:
---
Если используете PyCharm, вы можете выполнить следующие шаги:
* Открыть "Run" меню.
* Выбрать опцию "Debug...".
* Затем в появившемся контекстном меню.
* Выбрать файл для отладки (в данном случае, `main.py`).
Это запустит сервер с вашим **FastAPI** кодом, остановится на точках останова, и т.д.
Вот как это может выглядеть:
================================================
FILE: docs/ru/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Классы как зависимости { #classes-as-dependencies }
Прежде чем углубиться в систему **Внедрения Зависимостей**, давайте обновим предыдущий пример.
## `dict` из предыдущего примера { #a-dict-from-the-previous-example }
В предыдущем примере мы возвращали `dict` из нашей зависимости («dependable»):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Но затем мы получаем `dict` в параметре `commons` *функции-обработчика пути*.
И мы знаем, что редакторы кода не могут обеспечить достаточную поддержку (например, автозавершение) для `dict`, поскольку они не могут знать их ключи и типы значений.
Мы можем сделать лучше...
## Что делает зависимость { #what-makes-a-dependency }
До сих пор вы видели зависимости, объявленные как функции.
Но это не единственный способ объявления зависимостей (хотя он, вероятно, более распространенный).
Ключевым фактором является то, что зависимость должна быть «вызываемой».
В Python «**вызываемый**» — это всё, что Python может «вызвать», как функцию.
Так, если у вас есть объект `something` (который может и _не_ быть функцией) и вы можете «вызвать» его (выполнить) так:
```Python
something()
```
или
```Python
something(some_argument, some_keyword_argument="foo")
```
в таком случае он является «вызываемым».
## Классы как зависимости { #classes-as-dependencies_1 }
Вы можете заметить, что для создания экземпляра класса в Python используется тот же синтаксис.
Например:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
В данном случае `fluffy` является экземпляром класса `Cat`.
А чтобы создать `fluffy`, вы «вызываете» `Cat`.
Таким образом, класс в Python также является **вызываемым**.
Тогда в **FastAPI** в качестве зависимости можно использовать класс Python.
На самом деле FastAPI проверяет, что переданный объект является «вызываемым» (функция, класс или что-либо еще) и какие параметры у него определены.
Если вы передаёте «вызываемый» объект в качестве зависимости в **FastAPI**, он проанализирует параметры, необходимые для этого «вызываемого» объекта, и обработает их так же, как параметры *функции-обработчика пути*. Включая подзависимости.
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для *функций-обработчиков пути* без параметров.
Теперь мы можем изменить зависимость («dependable») `common_parameters`, указанную выше, на класс `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Обратите внимание на метод `__init__`, используемый для создания экземпляра класса:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...он имеет те же параметры, что и ранее используемая функция `common_parameters`:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Эти параметры и будут использоваться **FastAPI** для «решения» зависимости.
В обоих случаях она будет иметь:
* Необязательный параметр запроса `q`, представляющий собой `str`.
* Параметр запроса `skip`, представляющий собой `int`, по умолчанию `0`.
* Параметр запроса `limit`, представляющий собой `int`, по умолчанию `100`.
В обоих случаях данные будут конвертированы, валидированы, задокументированы в схеме OpenAPI и т.д.
## Как это использовать { #use-it }
Теперь вы можете объявить свою зависимость, используя этот класс.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** вызывает класс `CommonQueryParams`. При этом создается «экземпляр» этого класса, который будет передан в качестве параметра `commons` в вашу функцию.
## Аннотация типа и `Depends` { #type-annotation-vs-depends }
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Последний `CommonQueryParams`, в:
```Python
... Depends(CommonQueryParams)
```
...это то, что **FastAPI** будет использовать, чтобы узнать, что является зависимостью.
Из него FastAPI извлечёт объявленные параметры, и именно его FastAPI будет вызывать.
---
В этом случае первый `CommonQueryParams`, в:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: CommonQueryParams ...
```
////
...не имеет никакого специального значения для **FastAPI**. FastAPI не будет использовать его для преобразования данных, валидации и т.д. (поскольку для этого используется `Depends(CommonQueryParams)`).
На самом деле можно написать просто:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...как тут:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Но объявление типа приветствуется, так как в этом случае ваш редактор кода будет знать, что будет передано в качестве параметра `commons`, и тогда он сможет помочь вам с автозавершением, проверкой типов и т.д.:
## Сокращение { #shortcut }
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** предоставляет сокращение для таких случаев, когда зависимость — это *конкретный* класс, который **FastAPI** будет «вызывать» для создания экземпляра этого класса.
Для этих конкретных случаев вы можете сделать следующее.
Вместо того чтобы писать:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...следует написать:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с `Annotated`, если возможно.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Вы объявляете зависимость как тип параметра и используете `Depends()` без какого-либо параметра, вместо того чтобы *снова* писать полный класс внутри `Depends(CommonQueryParams)`.
Аналогичный пример будет выглядеть следующим образом:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...и **FastAPI** будет знать, что делать.
/// tip | Подсказка
Если это покажется вам более запутанным, чем полезным, не обращайте внимания — это вам не *нужно*.
Это просто сокращение. Потому что **FastAPI** заботится о том, чтобы помочь вам свести к минимуму повторение кода.
///
================================================
FILE: docs/ru/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Зависимости в декораторах операции пути { #dependencies-in-path-operation-decorators }
В некоторых случаях, возвращаемое значение зависимости не используется внутри *функции операции пути*.
Или же зависимость не возвращает никакого значения.
Но вам всё-таки нужно, чтобы она выполнилась.
Для таких ситуаций, вместо объявления *функции операции пути* с параметром `Depends`, вы можете добавить список зависимостей `dependencies` в *декоратор операции пути*.
## Добавление `dependencies` (зависимостей) в *декоратор операции пути* { #add-dependencies-to-the-path-operation-decorator }
*Декоратор операции пути* получает необязательный аргумент `dependencies`.
Это должен быть `list` состоящий из `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Зависимости из dependencies выполнятся так же, как и обычные зависимости. Но их значения (если они были) не будут переданы в *функцию операции пути*.
/// tip | Подсказка
Некоторые редакторы кода определяют неиспользуемые параметры функций и подсвечивают их как ошибку.
Использование `dependencies` в *декораторе операции пути* гарантирует выполнение зависимостей, избегая при этом предупреждений редактора кода и других инструментов.
Это также должно помочь предотвратить путаницу у начинающих разработчиков, которые видят неиспользуемые параметры в коде и могут подумать что в них нет необходимости.
///
/// info | Примечание
В этом примере мы используем выдуманные пользовательские HTTP-заголовки `X-Key` и `X-Token`.
Но в реальных проектах, при внедрении системы безопасности, вы получите больше пользы используя интегрированные [средства защиты (следующая глава)](../security/index.md).
///
## Ошибки в зависимостях и возвращаемые значения { #dependencies-errors-and-return-values }
Вы можете использовать те же *функции* зависимостей, что и обычно.
### Требования к зависимостям { #dependency-requirements }
Они могут объявлять требования к запросу (например HTTP-заголовки) или другие подзависимости:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Вызов исключений { #raise-exceptions }
Зависимости из dependencies могут вызывать исключения с помощью `raise`, как и обычные зависимости:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Возвращаемые значения { #return-values }
И они могут возвращать значения или нет, эти значения использоваться не будут.
Таким образом, вы можете переиспользовать обычную зависимость (возвращающую значение), которую вы уже используете где-то в другом месте, и хотя значение не будет использоваться, зависимость будет выполнена:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Зависимости для группы *операций путей* { #dependencies-for-a-group-of-path-operations }
Позже, читая о том как структурировать большие приложения ([Большие приложения — несколько файлов](../../tutorial/bigger-applications.md)), возможно, многофайловые, вы узнаете как объявить единый параметр `dependencies` для всей группы *операций путей*.
## Глобальные Зависимости { #global-dependencies }
Далее мы увидим, как можно добавить dependencies для всего `FastAPI` приложения, так чтобы они применялись к каждой *операции пути*.
================================================
FILE: docs/ru/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Зависимости с yield { #dependencies-with-yield }
FastAPI поддерживает зависимости, которые выполняют некоторые дополнительные шаги после завершения.
Для этого используйте `yield` вместо `return`, а дополнительные шаги (код) напишите после него.
/// tip | Подсказка
Убедитесь, что используете `yield` только один раз на одну зависимость.
///
/// note | Технические детали
Любая функция, с которой можно корректно использовать:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) или
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
будет корректной для использования в качестве зависимости **FastAPI**.
На самом деле, FastAPI использует эти два декоратора внутренне.
///
## Зависимость базы данных с помощью `yield` { #a-database-dependency-with-yield }
Например, с его помощью можно создать сессию работы с базой данных и закрыть её после завершения.
Перед созданием ответа будет выполнен только код до и включая оператор `yield`:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
Значение, полученное из `yield`, внедряется в *операции пути* и другие зависимости:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Код, следующий за оператором `yield`, выполняется после ответа:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Подсказка
Можно использовать как `async`, так и обычные функции.
**FastAPI** корректно обработает каждый вариант, так же как и с обычными зависимостями.
///
## Зависимость с `yield` и `try` { #a-dependency-with-yield-and-try }
Если использовать блок `try` в зависимости с `yield`, то вы получите любое исключение, которое было выброшено при использовании зависимости.
Например, если какой-то код в какой-то момент в середине, в другой зависимости или в *операции пути*, сделал "откат" транзакции базы данных или создал любую другую ошибку, то вы получите это исключение в своей зависимости.
Таким образом, можно искать конкретное исключение внутри зависимости с помощью `except SomeException`.
Точно так же можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены независимо от того, было ли исключение или нет.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Подзависимости с `yield` { #sub-dependencies-with-yield }
Вы можете иметь подзависимости и "деревья" подзависимостей любого размера и формы, и любая из них или все они могут использовать `yield`.
**FastAPI** проследит за тем, чтобы «код выхода» в каждой зависимости с `yield` выполнялся в правильном порядке.
Например, `dependency_c` может зависеть от `dependency_b`, а `dependency_b` — от `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
И все они могут использовать `yield`.
В этом случае `dependency_c` для выполнения своего кода выхода нуждается в том, чтобы значение из `dependency_b` (здесь `dep_b`) всё ещё было доступно.
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для её кода выхода.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Точно так же можно иметь часть зависимостей с `yield`, часть — с `return`, и какие-то из них могут зависеть друг от друга.
Либо у вас может быть одна зависимость, которая требует несколько других зависимостей с `yield` и т.д.
Комбинации зависимостей могут быть какими угодно.
**FastAPI** проследит за тем, чтобы всё выполнялось в правильном порядке.
/// note | Технические детали
Это работает благодаря [менеджерам контекста](https://docs.python.org/3/library/contextlib.html) в Python.
**FastAPI** использует их внутренне для достижения этого.
///
## Зависимости с `yield` и `HTTPException` { #dependencies-with-yield-and-httpexception }
Вы видели, что можно использовать зависимости с `yield` и иметь блоки `try`, которые пытаются выполнить некоторый код, а затем запускают код выхода в `finally`.
Также вы можете использовать `except`, чтобы поймать вызванное исключение и что-то с ним сделать.
Например, вы можете вызвать другое исключение, например `HTTPException`.
/// tip | Подсказка
Это довольно продвинутая техника, и в большинстве случаев она вам не понадобится, так как вы можете вызывать исключения (включая `HTTPException`) в остальном коде вашего приложения, например, в *функции-обработчике пути*.
Но если понадобится — возможность есть. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Если вы хотите перехватывать исключения и формировать на их основе пользовательский ответ, создайте [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers).
## Зависимости с `yield` и `except` { #dependencies-with-yield-and-except }
Если вы ловите исключение с помощью `except` в зависимости с `yield` и не вызываете его снова (или не вызываете новое исключение), FastAPI не сможет заметить, что было исключение — так же, как это происходит в обычном Python:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
В этом случае клиент получит *HTTP 500 Internal Server Error*, как и должно быть, поскольку мы не вызываем `HTTPException` или что-то подобное, но на сервере **не будет никаких логов** или других указаний на то, какая была ошибка. 😱
### Всегда делайте `raise` в зависимостях с `yield` и `except` { #always-raise-in-dependencies-with-yield-and-except }
Если вы ловите исключение в зависимости с `yield`, то, если вы не вызываете другой `HTTPException` или что-то подобное, вам следует повторно вызвать исходное исключение.
Вы можете повторно вызвать то же самое исключение с помощью `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Теперь клиент получит тот же *HTTP 500 Internal Server Error*, но на сервере в логах будет наше пользовательское `InternalError`. 😎
## Выполнение зависимостей с `yield` { #execution-of-dependencies-with-yield }
Последовательность выполнения примерно такая, как на этой схеме. Время течёт сверху вниз. А каждый столбец — это одна из частей, взаимодействующих с кодом или выполняющих код.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info | Дополнительная информация
Клиенту будет отправлен только **один ответ**. Это может быть один из ответов об ошибке или ответ от *операции пути*.
После отправки одного из этих ответов никакой другой ответ отправить нельзя.
///
/// tip | Подсказка
Если вы вызовете какое-либо исключение в коде из *функции-обработчика пути*, оно будет передано зависимостям с `yield`, включая `HTTPException`. В большинстве случаев вы захотите повторно вызвать то же самое исключение или новое из зависимости с `yield`, чтобы убедиться, что оно корректно обработано.
///
## Ранний выход и `scope` { #early-exit-and-scope }
Обычно «код выхода» зависимостей с `yield` выполняется **после того, как ответ** отправлен клиенту.
Но если вы знаете, что не будете использовать зависимость после возврата из *функции-обработчика пути*, вы можете использовать `Depends(scope="function")`, чтобы сообщить FastAPI, что он должен закрыть зависимость после возврата из *функции-обработчика пути*, но **до того**, как **ответ будет отправлен**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` принимает параметр `scope`, который может быть:
* `"function"`: начать зависимость до *функции-обработчика пути*, которая обрабатывает запрос, завершить зависимость после окончания *функции-обработчика пути*, но **до того**, как ответ будет отправлен обратно клиенту. То есть функция зависимости будет выполнена **вокруг** *функции-обработчика пути*.
* `"request"`: начать зависимость до *функции-обработчика пути*, которая обрабатывает запрос (как и при использовании `"function"`), но завершить **после** того, как ответ будет отправлен обратно клиенту. То есть функция зависимости будет выполнена **вокруг** цикла запроса (**request**) и ответа.
Если не указано и в зависимости есть `yield`, по умолчанию будет `scope` со значением `"request"`.
### `scope` для подзависимостей { #scope-for-sub-dependencies }
Когда вы объявляете зависимость с `scope="request"` (значение по умолчанию), любая подзависимость также должна иметь `scope` равный `"request"`.
Но зависимость со `scope` равным `"function"` может иметь зависимости со `scope` `"function"` и со `scope` `"request"`.
Это потому, что любая зависимость должна иметь возможность выполнить свой код выхода раньше подзависимостей, так как ей может понадобиться использовать их во время своего кода выхода.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Зависимость scope="request"
participant dep_func as Зависимость scope="function"
participant operation as Функция-обработчик пути
client ->> dep_req: Запрос
Note over dep_req: Выполнить код до yield
dep_req ->> dep_func: Передать значение
Note over dep_func: Выполнить код до yield
dep_func ->> operation: Выполнить функцию-обработчик пути
operation ->> dep_func: Выход из функции-обработчика пути
Note over dep_func: Выполнить код после yield
Note over dep_func: ✅ Зависимость закрыта
dep_func ->> client: Отправить ответ клиенту
Note over client: Ответ отправлен
Note over dep_req: Выполнить код после yield
Note over dep_req: ✅ Зависимость закрыта
```
## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
Зависимости с `yield` со временем эволюционировали, чтобы покрыть разные сценарии и исправить некоторые проблемы.
Если вы хотите посмотреть, что менялось в разных версиях FastAPI, вы можете прочитать об этом подробнее в продвинутом руководстве: [Продвинутые зависимости — зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Контекстные менеджеры { #context-managers }
### Что такое «контекстные менеджеры» { #what-are-context-managers }
«Контекстные менеджеры» — это любые объекты Python, которые можно использовать в операторе `with`.
Например, [можно использовать `with` для чтения файла](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Под капотом вызов `open("./somefile.txt")` создаёт объект, называемый «контекстным менеджером».
Когда блок `with` завершается, он обязательно закрывает файл, даже если были исключения.
Когда вы создаёте зависимость с `yield`, **FastAPI** внутренне создаёт для неё менеджер контекста и сочетает его с некоторыми другими связанными инструментами.
### Использование менеджеров контекста в зависимостях с `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Внимание
Это, более или менее, «продвинутая» идея.
Если вы только начинаете работать с **FastAPI**, то лучше пока пропустить этот пункт.
///
В Python можно создавать менеджеры контекста, [создав класс с двумя методами: `__enter__()` и `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
Их также можно использовать внутри зависимостей **FastAPI** с `yield`, применяя операторы
`with` или `async with` внутри функции зависимости:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Подсказка
Другой способ создания менеджера контекста — с помощью:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) или
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
оформив ими функцию с одним `yield`.
Именно это **FastAPI** использует внутренне для зависимостей с `yield`.
Но использовать эти декораторы для зависимостей FastAPI не обязательно (и не стоит).
FastAPI сделает это за вас на внутреннем уровне.
///
================================================
FILE: docs/ru/docs/tutorial/dependencies/global-dependencies.md
================================================
# Глобальные зависимости { #global-dependencies }
Для некоторых типов приложений может потребоваться добавить зависимости ко всему приложению.
Подобно тому, как вы можете [добавлять `dependencies` (зависимости) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md), вы можете добавлять зависимости сразу ко всему `FastAPI` приложению.
В этом случае они будут применяться ко всем *операциям пути* в приложении:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Все способы [добавления `dependencies` (зависимостей) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md) по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения.
## Зависимости для групп *операций пути* { #dependencies-for-groups-of-path-operations }
Позднее, читая о том, как структурировать более крупные приложения ([Более крупные приложения - несколько файлов](../../tutorial/bigger-applications.md)), возможно, состоящие из нескольких файлов, вы узнаете, как объявить один параметр `dependencies` для целой группы *операций пути*.
================================================
FILE: docs/ru/docs/tutorial/dependencies/index.md
================================================
# Зависимости { #dependencies }
**FastAPI** имеет очень мощную, но интуитивную систему **Инъекция зависимостей**.
Она спроектирована так, чтобы быть очень простой в использовании и облегчать любому разработчику интеграцию других компонентов с **FastAPI**.
## Что такое инъекция зависимостей («Dependency Injection») { #what-is-dependency-injection }
В программировании **«Dependency Injection»** означает, что у вашего кода (в данном случае у ваших *функций обработки пути*) есть способ объявить вещи, которые требуются для его работы и использования: «зависимости».
И затем эта система (в нашем случае **FastAPI**) позаботится о том, чтобы сделать всё необходимое для предоставления вашему коду этих зависимостей (сделать «инъекцию» зависимостей).
Это очень полезно, когда вам нужно:
* Обеспечить общую логику (один и тот же алгоритм снова и снова).
* Разделять соединения с базой данных.
* Обеспечить безопасность, аутентификацию, требования к ролям и т.п.
* И многое другое...
Всё это при минимизации повторения кода.
## Первые шаги { #first-steps }
Давайте рассмотрим очень простой пример. Он настолько простой, что пока не очень полезен.
Но так мы сможем сосредоточиться на том, как работает система **Dependency Injection**.
### Создайте зависимость, или «dependable» (от чего что-то зависит) { #create-a-dependency-or-dependable }
Сначала сосредоточимся на зависимости.
Это просто функция, которая может принимать те же параметры, что и *функция обработки пути*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
И всё.
**2 строки.**
И она имеет ту же форму и структуру, что и все ваши *функции обработки пути*.
Можно думать о ней как о *функции обработки пути* без «декоратора» (без `@app.get("/some-path")`).
И она может возвращать что угодно.
В этом случае эта зависимость ожидает:
* Необязательный query-параметр `q` типа `str`.
* Необязательный query-параметр `skip` типа `int`, по умолчанию `0`.
* Необязательный query-параметр `limit` типа `int`, по умолчанию `100`.
А затем просто возвращает `dict`, содержащий эти значения.
/// info | Информация
FastAPI добавил поддержку `Annotated` (и начал рекомендовать его использование) в версии 0.95.0.
Если у вас более старая версия, вы получите ошибки при попытке использовать `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) как минимум до 0.95.1, прежде чем использовать `Annotated`.
///
### Импорт `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Объявите зависимость в «зависимом» { #declare-the-dependency-in-the-dependant }
Точно так же, как вы используете `Body`, `Query` и т.д. с параметрами вашей *функции обработки пути*, используйте `Depends` с новым параметром:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Хотя вы используете `Depends` в параметрах вашей функции так же, как `Body`, `Query` и т.д., `Depends` работает немного иначе.
В `Depends` вы передаёте только один параметр.
Этот параметр должен быть чем-то вроде функции.
Вы **не вызываете её** напрямую (не добавляйте круглые скобки в конце), просто передаёте её как параметр в `Depends()`.
И эта функция принимает параметры так же, как *функции обработки пути*.
/// tip | Подсказка
В следующей главе вы увидите, какие ещё «вещи», помимо функций, можно использовать в качестве зависимостей.
///
Каждый раз, когда приходит новый запрос, **FastAPI** позаботится о:
* Вызове вашей зависимости («dependable») с корректными параметрами.
* Получении результата из вашей функции.
* Присваивании этого результата параметру в вашей *функции обработки пути*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Таким образом, вы пишете общий код один раз, а **FastAPI** позаботится о его вызове для ваших *операций пути*.
/// check | Проверка
Обратите внимание, что вам не нужно создавать специальный класс и передавать его куда-то в **FastAPI**, чтобы «зарегистрировать» его или что-то подобное.
Вы просто передаёте его в `Depends`, и **FastAPI** знает, что делать дальше.
///
## Использование зависимости с `Annotated` в нескольких местах { #share-annotated-dependencies }
В приведённых выше примерах есть небольшое **повторение кода**.
Когда вам нужно использовать зависимость `common_parameters()`, вы должны написать весь параметр с аннотацией типа и `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Но поскольку мы используем `Annotated`, мы можем сохранить это значение `Annotated` в переменную и использовать его в нескольких местах:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Подсказка
Это стандартный Python, это называется «type alias», и это не особенность **FastAPI**.
Но поскольку **FastAPI** основан на стандартах Python, включая `Annotated`, вы можете использовать этот трюк в своём коде. 😎
///
Зависимости продолжат работать как ожидалось, и **лучшая часть** в том, что **информация о типах будет сохранена**, а значит, ваш редактор кода продолжит предоставлять **автозавершение**, **встроенные ошибки** и т.д. То же относится и к другим инструментам, таким как `mypy`.
Это особенно полезно, когда вы используете это в **большой кодовой базе**, где вы используете **одни и те же зависимости** снова и снова во **многих *операциях пути***.
## Использовать `async` или не `async` { #to-async-or-not-to-async }
Поскольку зависимости также вызываются **FastAPI** (как и ваши *функции обработки пути*), применяются те же правила при определении ваших функций.
Вы можете использовать `async def` или обычное `def`.
И вы можете объявлять зависимости с `async def` внутри обычных *функций обработки пути* `def`, или зависимости `def` внутри *функций обработки пути* `async def` и т.д.
Это не важно. **FastAPI** знает, что делать.
/// note | Примечание
Если вы не уверены, посмотрите раздел [Async: «Нет времени?»](../../async.md#in-a-hurry) о `async` и `await` в документации.
///
## Интеграция с OpenAPI { #integrated-with-openapi }
Все объявления запросов, проверки и требования ваших зависимостей (и подзависимостей) будут интегрированы в ту же схему OpenAPI.
Поэтому в интерактивной документации будет вся информация и из этих зависимостей:
## Простое использование { #simple-usage }
Если посмотреть, *функции обработки пути* объявляются для использования всякий раз, когда *путь* и *операция* совпадают, и тогда **FastAPI** заботится о вызове функции с корректными параметрами, извлекая данные из запроса.
На самом деле все (или большинство) веб-фреймворков работают таким же образом.
Вы никогда не вызываете эти функции напрямую. Их вызывает ваш фреймворк (в нашем случае **FastAPI**).
С системой **Dependency Injection** вы также можете сообщить **FastAPI**, что ваша *функция обработки пути* «зависит» от чего-то, что должно быть выполнено перед вашей *функцией обработки пути*, и **FastAPI** позаботится о его выполнении и «инъекции» результатов.
Другие распространённые термины для описания той же идеи «dependency injection»:
* ресурсы
* провайдеры
* сервисы
* внедряемые зависимости
* компоненты
## Плагины **FastAPI** { #fastapi-plug-ins }
Интеграции и «плагины» могут быть построены с использованием системы **Dependency Injection**. Но на самом деле **нет необходимости создавать «плагины»**, так как, используя зависимости, можно объявить бесконечное количество интеграций и взаимодействий, которые становятся доступными вашим *функциям обработки пути*.
И зависимости можно создавать очень простым и интуитивным способом, который позволяет просто импортировать нужные пакеты Python и интегрировать их с вашими API-функциями в пару строк кода, *буквально*.
Вы увидите примеры этого в следующих главах о реляционных и NoSQL базах данных, безопасности и т.д.
## Совместимость с **FastAPI** { #fastapi-compatibility }
Простота системы **Dependency Injection** делает **FastAPI** совместимым с:
* всеми реляционными базами данных
* NoSQL базами данных
* внешними пакетами
* внешними API
* системами аутентификации и авторизации
* системами мониторинга использования API
* системами инъекции данных в ответы
* и т.д.
## Просто и мощно { #simple-and-powerful }
Хотя иерархическая система dependency injection очень проста для определения и использования, она по-прежнему очень мощная.
Вы можете определять зависимости, которые, в свою очередь, могут иметь собственные зависимости.
В итоге строится иерархическое дерево зависимостей, и система **Dependency Injection** берёт на себя решение всех этих зависимостей (и их подзависимостей) и предоставляет (инъектирует) результаты на каждом шаге.
Например, у вас есть 4 API-эндпоинта (*операции пути*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
тогда вы можете добавить разные требования к правам для каждого из них только с помощью зависимостей и подзависимостей:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Интегрировано с **OpenAPI** { #integrated-with-openapi_1 }
Все эти зависимости, объявляя свои требования, также добавляют параметры, проверки и т.д. к вашим *операциям пути*.
**FastAPI** позаботится о добавлении всего этого в схему OpenAPI, чтобы это отображалось в системах интерактивной документации.
================================================
FILE: docs/ru/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Подзависимости { #sub-dependencies }
Вы можете создавать зависимости, которые имеют **подзависимости**.
Их **вложенность** может быть любой глубины.
**FastAPI** сам займётся их управлением.
## Первая зависимость { #first-dependency-dependable }
Можно создать первую зависимость следующим образом:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Она объявляет необязательный параметр запроса `q` как строку, а затем возвращает его.
Это довольно просто (хотя и не очень полезно), но поможет нам сосредоточиться на том, как работают подзависимости.
## Вторая зависимость, «зависимость» и «зависимая» { #second-dependency-dependable-and-dependant }
Затем можно создать еще одну функцию зависимости, которая одновременно объявляет свою собственную зависимость (таким образом, она тоже является «зависимой»):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Остановимся на объявленных параметрах:
* Несмотря на то, что эта функция сама является зависимостью, она также является зависимой от чего-то другого.
* Она зависит от `query_extractor` и присваивает возвращаемое ей значение параметру `q`.
* Она также объявляет необязательный куки-параметр `last_query` в виде строки.
* Если пользователь не указал параметр `q` в запросе, то мы используем последний использованный запрос, который мы ранее сохранили в куки-параметре `last_query`.
## Использование зависимости { #use-the-dependency }
Затем мы можем использовать зависимость вместе с:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Дополнительная информация
Обратите внимание, что мы объявляем только одну зависимость в *функции операции пути* - `query_or_cookie_extractor`.
Но **FastAPI** будет знать, что сначала он должен выполнить `query_extractor`, чтобы передать результаты этого в `query_or_cookie_extractor` при его вызове.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Использование одной и той же зависимости несколько раз { #using-the-same-dependency-multiple-times }
Если одна из ваших зависимостей объявлена несколько раз для одной и той же *функции операции пути*, например, несколько зависимостей имеют общую подзависимость, **FastAPI** будет знать, что вызывать эту подзависимость нужно только один раз за запрос.
При этом возвращаемое значение будет сохранено в «кэш» и будет передано всем "зависимым" функциям, которые нуждаются в нем внутри этого конкретного запроса, вместо того, чтобы вызывать зависимость несколько раз для одного и того же запроса.
В расширенном сценарии, когда вы знаете, что вам нужно, чтобы зависимость вызывалась на каждом шаге (возможно, несколько раз) в одном и том же запросе, вместо использования "кэшированного" значения, вы можете установить параметр `use_cache=False` при использовании `Depends`:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Предпочтительнее использовать версию с аннотацией, если это возможно.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Резюме { #recap }
Помимо всех этих умных слов, используемых здесь, система внедрения зависимостей довольно проста.
Это просто функции, которые выглядят так же, как *функции операций путей*.
Но, тем не менее, эта система очень мощная и позволяет вам объявлять вложенные графы (деревья) зависимостей сколь угодно глубоко.
/// tip | Подсказка
Все это может показаться не столь полезным на этих простых примерах.
Но вы увидите как это пригодится в главах посвященных безопасности.
И вы также увидите, сколько кода это вам сэкономит.
///
================================================
FILE: docs/ru/docs/tutorial/encoder.md
================================================
# JSON-совместимый кодировщик { #json-compatible-encoder }
В некоторых случаях может потребоваться преобразование типа данных (например, Pydantic-модели) в тип, совместимый с JSON (например, `dict`, `list` и т.д.).
Например, если необходимо хранить его в базе данных.
Для этого **FastAPI** предоставляет функцию `jsonable_encoder()`.
## Использование `jsonable_encoder` { #using-the-jsonable-encoder }
Представим, что у вас есть база данных `fake_db`, которая принимает только JSON-совместимые данные.
Например, она не принимает объекты `datetime`, так как они не совместимы с JSON.
В таком случае объект `datetime` следует преобразовать в `str`, содержащую данные в [формате ISO](https://en.wikipedia.org/wiki/ISO_8601).
Точно так же эта база данных не может принять Pydantic-модель (объект с атрибутами), а только `dict`.
Для этого можно использовать функцию `jsonable_encoder`.
Она принимает объект, например, Pydantic-модель, и возвращает его версию, совместимую с JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
В данном примере она преобразует Pydantic-модель в `dict`, а `datetime` - в `str`.
Результатом её вызова является объект, который может быть закодирован с помощью функции из стандартной библиотеки Python – [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
Функция не возвращает большой `str`, содержащий данные в формате JSON (в виде строки). Она возвращает стандартную структуру данных Python (например, `dict`) со значениями и подзначениями, которые совместимы с JSON.
/// note | Примечание
`jsonable_encoder` фактически используется **FastAPI** внутри системы для преобразования данных. Однако он полезен и во многих других сценариях.
///
================================================
FILE: docs/ru/docs/tutorial/extra-data-types.md
================================================
# Дополнительные типы данных { #extra-data-types }
До сих пор вы использовали простые типы данных, такие как:
* `int`
* `float`
* `str`
* `bool`
Но вы также можете использовать и более сложные типы.
При этом у вас останутся те же возможности, что и до сих пор:
* Отличная поддержка редактора кода.
* Преобразование данных из входящих запросов.
* Преобразование данных для ответа.
* Валидация данных.
* Автоматическая аннотация и документация.
## Другие типы данных { #other-data-types }
Ниже перечислены некоторые из дополнительных типов данных, которые вы можете использовать:
* `UUID`:
* Стандартный "Универсальный уникальный идентификатор", используемый в качестве идентификатора во многих базах данных и системах.
* В запросах и ответах будет представлен как `str`.
* `datetime.datetime`:
* Встроенный в Python `datetime.datetime`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Встроенный в Python `datetime.date`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `2008-09-15`.
* `datetime.time`:
* Встроенный в Python `datetime.time`.
* В запросах и ответах будет представлен как `str` в формате ISO 8601, например: `14:23:55.003`.
* `datetime.timedelta`:
* Встроенный в Python `datetime.timedelta`.
* В запросах и ответах будет представлен в виде общего количества секунд типа `float`.
* Pydantic также позволяет представить его как "Кодировку разницы во времени ISO 8601", [см. документацию для получения дополнительной информации](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* В запросах и ответах обрабатывается так же, как и `set`:
* В запросах будет прочитан список, исключены дубликаты и преобразован в `set`.
* В ответах `set` будет преобразован в `list`.
* В сгенерированной схеме будет указано, что значения `set` уникальны (с помощью JSON-схемы `uniqueItems`).
* `bytes`:
* Встроенный в Python `bytes`.
* В запросах и ответах будет рассматриваться как `str`.
* В сгенерированной схеме будет указано, что это `str` в формате `binary`.
* `Decimal`:
* Встроенный в Python `Decimal`.
* В запросах и ответах обрабатывается так же, как и `float`.
* Вы можете проверить все допустимые типы данных Pydantic здесь: [Типы данных Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
## Пример { #example }
Вот пример *операции пути* с параметрами, который демонстрирует некоторые из вышеперечисленных типов.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Обратите внимание, что параметры внутри функции имеют свой естественный тип данных, и вы, например, можете выполнять обычные манипуляции с датами, такие как:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/ru/docs/tutorial/extra-models.md
================================================
# Дополнительные модели { #extra-models }
В продолжение прошлого примера будет уже обычным делом иметь несколько связанных между собой моделей.
Это особенно применимо в случае моделей пользователя, потому что:
* **Модель для ввода** должна иметь возможность содержать пароль.
* **Модель для вывода** не должна содержать пароль.
* **Модель для базы данных**, возможно, должна содержать хэшированный пароль.
/// danger | Внимание
Никогда не храните пароли пользователей в чистом виде. Всегда храните "безопасный хэш", который вы затем сможете проверить.
Если вам это не знакомо, вы можете узнать про "хэш пароля" в [главах о безопасности](security/simple-oauth2.md#password-hashing).
///
## Множественные модели { #multiple-models }
Ниже изложена основная идея того, как могут выглядеть эти модели с полями для паролей, а также описаны места, где они используются:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Про `**user_in.model_dump()` { #about-user-in-model-dump }
#### `.model_dump()` из Pydantic { #pydantics-model-dump }
`user_in` — это Pydantic-модель класса `UserIn`.
У Pydantic-моделей есть метод `.model_dump()`, который возвращает `dict` с данными модели.
Поэтому, если мы создадим Pydantic-объект `user_in` таким способом:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
и затем вызовем:
```Python
user_dict = user_in.model_dump()
```
то теперь у нас есть `dict` с данными в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели).
И если мы вызовем:
```Python
print(user_dict)
```
мы получим Python `dict` с:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Распаковка `dict` { #unpacking-a-dict }
Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python его "распакует". Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение.
Поэтому, продолжая описанный выше пример с `user_dict`, написание такого кода:
```Python
UserInDB(**user_dict)
```
будет эквивалентно:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Или, более точно, если использовать `user_dict` напрямую, с любым содержимым, которое он может иметь в будущем:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Pydantic-модель из содержимого другой { #a-pydantic-model-from-the-contents-of-another }
Как в примере выше мы получили `user_dict` из `user_in.model_dump()`, этот код:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
будет равнозначен такому:
```Python
UserInDB(**user_in.model_dump())
```
...потому что `user_in.model_dump()` — это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` с префиксом `**`.
Таким образом мы получаем Pydantic-модель на основе данных из другой Pydantic-модели.
#### Распаковка `dict` и дополнительные именованные аргументы { #unpacking-a-dict-and-extra-keywords }
И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password` как здесь:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...то в итоге получится что-то подобное:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Предупреждение
Вспомогательные дополнительные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность.
///
## Сократите дублирование { #reduce-duplication }
Сокращение дублирования кода — это одна из главных идей **FastAPI**.
Поскольку дублирование кода повышает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д.
А все описанные выше модели используют много общих данных и дублируют названия атрибутов и типов.
Мы можем это улучшить.
Мы можем определить модель `UserBase`, которая будет базовой для остальных моделей. И затем мы можем создать подклассы этой модели, которые будут наследовать её атрибуты (объявления типов, валидацию, и т.п.).
Все операции конвертации, валидации, документации, и т.п. будут по-прежнему работать нормально.
В этом случае мы можем определить только различия между моделями (с `password` в чистом виде, с `hashed_password` и без пароля):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` или `anyOf` { #union-or-anyof }
Вы можете объявить HTTP-ответ как `Union` из двух или более типов. Это означает, что HTTP-ответ может быть любым из них.
Он будет определён в OpenAPI как `anyOf`.
Для этого используйте стандартную аннотацию типов в Python [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note | Примечание
При объявлении [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions) сначала указывайте наиболее специфичный тип, затем менее специфичный. В примере ниже более специфичный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` в Python 3.10 { #union-in-python-3-10 }
В этом примере мы передаём `Union[PlaneItem, CarItem]` в качестве значения аргумента `response_model`.
Поскольку мы передаём его как **значение аргумента** вместо того, чтобы поместить его в **аннотацию типа**, нам придётся использовать `Union` даже в Python 3.10.
Если оно было бы указано в аннотации типа, то мы могли бы использовать вертикальную черту как в примере:
```Python
some_variable: PlaneItem | CarItem
```
Но если мы поместим это в присваивание `response_model=PlaneItem | CarItem`, мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа.
## Список моделей { #list-of-models }
Таким же образом вы можете объявлять HTTP-ответы, возвращающие списки объектов.
Для этого используйте стандартный `list` в Python:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Ответ с произвольным `dict` { #response-with-arbitrary-dict }
Вы также можете объявить HTTP-ответ, используя обычный произвольный `dict`, объявив только тип ключей и значений, без использования Pydantic-модели.
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели).
В этом случае вы можете использовать `dict`:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Резюме { #recap }
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждого случая.
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояние, включающее `password`, `password_hash` и отсутствие пароля.
================================================
FILE: docs/ru/docs/tutorial/first-steps.md
================================================
# Первые шаги { #first-steps }
Самый простой файл FastAPI может выглядеть так:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Скопируйте это в файл `main.py`.
Запустите сервер в режиме реального времени:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
В выводе будет строка примерно такого вида:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Эта строка показывает URL, по которому ваше приложение доступно на локальной машине.
### Проверьте { #check-it }
Откройте браузер по адресу: [http://127.0.0.1:8000](http://127.0.0.1:8000).
Вы увидите JSON-ответ вида:
```JSON
{"message": "Hello World"}
```
### Интерактивная документация API { #interactive-api-docs }
Теперь перейдите по адресу: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите автоматически сгенерированную интерактивную документацию по API (предоставлено [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Альтернативная документация API { #alternative-api-docs }
И теперь перейдите по адресу [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Вы увидите альтернативную автоматически сгенерированную документацию (предоставлено [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
**FastAPI** генерирует «схему» всего вашего API, используя стандарт **OpenAPI** для описания API.
#### «Схема» { #schema }
«Схема» — это определение или описание чего-либо. Не код, который это реализует, а только абстрактное описание.
#### «Схема» API { #api-schema }
В данном случае [OpenAPI](https://github.com/OAI/OpenAPI-Specification) — это спецификация, которая определяет, как описывать схему вашего API.
Это определение схемы включает пути вашего API, возможные параметры, которые они принимают, и т.п.
#### «Схема» данных { #data-schema }
Термин «схема» также может относиться к форме некоторых данных, например, к содержимому JSON.
В таком случае это будут атрибуты JSON, их типы данных и т.п.
#### OpenAPI и JSON Schema { #openapi-and-json-schema }
OpenAPI определяет схему API для вашего API. И эта схема включает определения (или «схемы») данных, отправляемых и получаемых вашим API, с использованием стандарта **JSON Schema** для схем данных JSON.
#### Посмотрите `openapi.json` { #check-the-openapi-json }
Если вам интересно, как выглядит исходная схема OpenAPI, FastAPI автоматически генерирует JSON (схему) с описанием всего вашего API.
Вы можете посмотреть её напрямую по адресу: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Вы увидите JSON, начинающийся примерно так:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Для чего нужен OpenAPI { #what-is-openapi-for }
Схема OpenAPI является основой для обеих включённых систем интерактивной документации.
Есть десятки альтернатив, все основаны на OpenAPI. Вы можете легко добавить любую из них в ваше приложение, созданное с **FastAPI**.
Вы также можете использовать её для автоматической генерации кода для клиентов, которые взаимодействуют с вашим API. Например, для фронтенд-, мобильных или IoT-приложений.
### Настройте app `entrypoint` в `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Вы можете указать расположение вашего приложения в файле `pyproject.toml`, например:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Этот `entrypoint` подскажет команде `fastapi`, что приложение нужно импортировать так:
```python
from main import app
```
Если структура вашего кода выглядит так:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Тогда следует указать такой `entrypoint`:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
что будет эквивалентно:
```python
from backend.main import app
```
### `fastapi dev` с путём { #fastapi-dev-with-path }
Вы также можете передать путь к файлу в команду `fastapi dev`, и она попытается определить объект приложения FastAPI для использования:
```console
$ fastapi dev main.py
```
Но в этом случае вам придётся каждый раз помнить о передаче корректного пути при вызове команды `fastapi`.
Кроме того, другие инструменты могут его не найти, например [Расширение VS Code](../editor-support.md) или [FastAPI Cloud](https://fastapicloud.com), поэтому рекомендуется использовать `entrypoint` в `pyproject.toml`.
### Разверните приложение (необязательно) { #deploy-your-app-optional }
При желании вы можете развернуть своё приложение FastAPI в [FastAPI Cloud](https://fastapicloud.com), перейдите и присоединитесь к списку ожидания, если ещё не сделали этого. 🚀
Если у вас уже есть аккаунт **FastAPI Cloud** (мы пригласили вас из списка ожидания 😉), вы можете развернуть приложение одной командой.
Перед развертыванием убедитесь, что вы вошли в систему:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Затем разверните приложение:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Готово! Теперь вы можете открыть своё приложение по этому URL. ✨
## Рассмотрим поэтапно { #recap-step-by-step }
### Шаг 1: импортируйте `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` — это класс на Python, который предоставляет всю функциональность для вашего API.
/// note | Технические детали
`FastAPI` — это класс, который напрямую наследуется от `Starlette`.
Вы можете использовать весь функционал [Starlette](https://www.starlette.dev/) и в `FastAPI`.
///
### Шаг 2: создайте экземпляр `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Здесь переменная `app` будет экземпляром класса `FastAPI`.
Это будет основная точка взаимодействия для создания всего вашего API.
### Шаг 3: создайте *операцию пути (path operation)* { #step-3-create-a-path-operation }
#### Путь (path) { #path }
Здесь «путь» — это последняя часть URL, начиная с первого символа `/`.
Итак, в таком URL:
```
https://example.com/items/foo
```
...путь будет:
```
/items/foo
```
/// info | Информация
«Путь» также часто называют «эндпоинт» или «маршрут».
///
При создании API «путь» — это основной способ разделения «задач» и «ресурсов».
#### Операция (operation) { #operation }
«Операция» здесь — это один из HTTP-«методов».
Один из:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...и более экзотические:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
В протоколе HTTP можно обращаться к каждому пути, используя один (или несколько) из этих «методов».
---
При создании API обычно используют конкретные HTTP-методы для выполнения конкретных действий.
Обычно используют:
* `POST`: создать данные.
* `GET`: прочитать данные.
* `PUT`: обновить данные.
* `DELETE`: удалить данные.
Таким образом, в OpenAPI каждый HTTP-метод называется «операцией».
Мы тоже будем называть их «операциями».
#### Определите *декоратор операции пути (path operation decorator)* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
`@app.get("/")` сообщает **FastAPI**, что функция прямо под ним отвечает за обработку запросов, поступающих:
* по пути `/`
* с использованием get операции
/// info | Информация о `@decorator`
Синтаксис `@something` в Python называется «декоратор».
Его размещают над функцией. Как красивая декоративная шляпа (кажется, отсюда и пошёл термин).
«Декоратор» берёт функцию ниже и делает с ней что-то.
В нашем случае этот декоратор сообщает **FastAPI**, что функция ниже соответствует **пути** `/` с **операцией** `get`.
Это и есть «декоратор операции пути».
///
Можно также использовать другие операции:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
И более экзотические:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | Подсказка
Вы можете использовать каждый метод (HTTP-операцию) так, как считаете нужным.
**FastAPI** не навязывает какого-либо конкретного смысла.
Эта информация дана как рекомендация, а не требование.
Например, при использовании GraphQL обычно все действия выполняются только с помощью POST-операций.
///
### Шаг 4: определите **функцию операции пути** { #step-4-define-the-path-operation-function }
Вот наша «функция операции пути»:
* **путь**: `/`.
* **операция**: `get`.
* **функция**: функция ниже «декоратора» (ниже `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Это функция на Python.
**FastAPI** будет вызывать её каждый раз, когда получает запрос к URL «`/`» с операцией `GET`.
В данном случае это асинхронная (`async`) функция.
---
Вы также можете определить её как обычную функцию вместо `async def`:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Примечание
Если вы не знаете, в чём разница, посмотрите [Асинхронность: *"Нет времени?"*](../async.md#in-a-hurry).
///
### Шаг 5: верните содержимое { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Вы можете вернуть `dict`, `list`, отдельные значения `str`, `int` и т.д.
Также можно вернуть модели Pydantic (подробнее об этом позже).
Многие другие объекты и модели будут автоматически преобразованы в JSON (включая ORM и т.п.). Попробуйте использовать те, что вам привычнее, с высокой вероятностью они уже поддерживаются.
### Шаг 6: разверните приложение { #step-6-deploy-it }
Разверните приложение в **[FastAPI Cloud](https://fastapicloud.com)** одной командой: `fastapi deploy`. 🎉
#### О FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** создан тем же автором и командой, что и **FastAPI**.
Он упрощает процесс **создания образа**, **развертывания** и **доступа** к API с минимальными усилиями.
Он переносит тот же **опыт разработчика** при создании приложений с FastAPI на их **развертывание** в облаке. 🎉
FastAPI Cloud — основной спонсор и источник финансирования для open-source проектов «FastAPI и друзья». ✨
#### Развертывание у других облачных провайдеров { #deploy-to-other-cloud-providers }
FastAPI — open-source и основан на стандартах. Вы можете развернуть приложения FastAPI у любого облачного провайдера по вашему выбору.
Следуйте руководствам вашего облачного провайдера, чтобы развернуть приложения FastAPI у них. 🤓
## Резюме { #recap }
* Импортируйте `FastAPI`.
* Создайте экземпляр `app`.
* Напишите **декоратор операции пути**, например `@app.get("/")`.
* Определите **функцию операции пути**; например, `def root(): ...`.
* Запустите сервер разработки командой `fastapi dev`.
* При желании разверните приложение командой `fastapi deploy`.
================================================
FILE: docs/ru/docs/tutorial/handling-errors.md
================================================
# Обработка ошибок { #handling-errors }
Существует множество ситуаций, когда необходимо сообщить об ошибке клиенту, использующему ваш API.
Таким клиентом может быть браузер с фронтендом, чужой код, IoT-устройство и т.д.
Возможно, вам придется сообщить клиенту о следующем:
* Клиент не имеет достаточных привилегий для выполнения данной операции.
* Клиент не имеет доступа к данному ресурсу.
* Элемент, к которому клиент пытался получить доступ, не существует.
* и т.д.
В таких случаях обычно возвращают **HTTP статус-код** в диапазоне **400** (от 400 до 499).
Они похожи на двухсотые HTTP статус-коды (от 200 до 299), которые означают, что запрос обработан успешно.
Четырёхсотые статус-коды означают, что ошибка произошла по вине клиента.
Помните ли ошибки **"404 Not Found "** (и шутки) ?
## Использование `HTTPException` { #use-httpexception }
Для возврата клиенту HTTP-ответов с ошибками используется `HTTPException`.
### Импортируйте `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Вызовите `HTTPException` в своем коде { #raise-an-httpexception-in-your-code }
`HTTPException` - это обычное исключение Python с дополнительными данными, актуальными для API.
Поскольку это исключение Python, то его не `возвращают`, а `вызывают`.
Это также означает, что если вы находитесь внутри функции, которая вызывается внутри вашей *функции операции пути*, и вы поднимаете `HTTPException` внутри этой функции, то она не будет выполнять остальной код в *функции операции пути*, а сразу завершит запрос и отправит HTTP-ошибку из `HTTPException` клиенту.
О том, насколько выгоднее `вызывать` исключение, чем `возвращать` значение, будет рассказано в разделе, посвященном зависимостям и безопасности.
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Возвращаемый ответ { #the-resulting-response }
Если клиент запросит `http://example.com/items/foo` (`item_id` `"foo"`), то он получит статус-код 200 и ответ в формате JSON:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Но если клиент запросит `http://example.com/items/bar` (несуществующий `item_id` `"bar"`), то он получит статус-код 404 (ошибка "не найдено") и JSON-ответ в виде:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Подсказка
При вызове `HTTPException` в качестве параметра `detail` можно передавать любое значение, которое может быть преобразовано в JSON, а не только `str`.
Вы можете передать `dict`, `list` и т.д.
Они автоматически обрабатываются **FastAPI** и преобразуются в JSON.
///
## Добавление пользовательских заголовков { #add-custom-headers }
В некоторых ситуациях полезно иметь возможность добавлять пользовательские HTTP-заголовки к ошибке HTTP. Например, для некоторых типов безопасности.
Скорее всего, вам не потребуется использовать его непосредственно в коде.
Но в случае, если это необходимо для продвинутого сценария, можно добавить пользовательские заголовки:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
Вы можете добавить пользовательские обработчики исключений с помощью [тех же утилит обработки исключений из Starlette](https://www.starlette.dev/exceptions/).
Допустим, у вас есть пользовательское исключение `UnicornException`, которое вы (или используемая вами библиотека) можете `вызвать`.
И вы хотите обрабатывать это исключение глобально с помощью FastAPI.
Можно добавить собственный обработчик исключений с помощью `@app.exception_handler()`:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Здесь, если запросить `/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
Но оно будет обработано `unicorn_exception_handler`.
Таким образом, вы получите чистую ошибку с кодом состояния HTTP `418` и содержимым JSON:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Технические детали
Также можно использовать `from starlette.requests import Request` и `from starlette.responses import JSONResponse`.
**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette. То же самое касается и `Request`.
///
## Переопределение стандартных обработчиков исключений { #override-the-default-exception-handlers }
**FastAPI** имеет некоторые обработчики исключений по умолчанию.
Эти обработчики отвечают за возврат стандартных JSON-ответов при `вызове` `HTTPException` и при наличии в запросе недопустимых данных.
Вы можете переопределить эти обработчики исключений на свои собственные.
### Переопределение обработчика исключений проверки запроса { #override-request-validation-exceptions }
Когда запрос содержит недопустимые данные, **FastAPI** внутренне вызывает ошибку `RequestValidationError`.
А также включает в себя обработчик исключений по умолчанию.
Чтобы переопределить его, импортируйте `RequestValidationError` и используйте его с `@app.exception_handler(RequestValidationError)` для создания обработчика исключений.
Обработчик исключения получит объект `Request` и исключение.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Теперь, если перейти к `/items/foo`, то вместо стандартной JSON-ошибки с:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
вы получите текстовую версию:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Переопределите обработчик ошибок `HTTPException` { #override-the-httpexception-error-handler }
Аналогичным образом можно переопределить обработчик `HTTPException`.
Например, для этих ошибок можно вернуть обычный текстовый ответ вместо JSON:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Технические детали
Можно также использовать `from starlette.responses import PlainTextResponse`.
**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette.
///
/// warning | Внимание
Имейте в виду, что `RequestValidationError` содержит информацию об имени файла и строке, где произошла ошибка валидации, чтобы вы могли при желании отобразить её в логах с релевантными данными.
Но это означает, что если вы просто преобразуете её в строку и вернёте эту информацию напрямую, вы можете допустить небольшую утечку информации о своей системе, поэтому здесь код извлекает и показывает каждую ошибку отдельно.
///
### Используйте тело `RequestValidationError` { #use-the-requestvalidationerror-body }
Ошибка `RequestValidationError` содержит полученное `тело` с недопустимыми данными.
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Теперь попробуйте отправить недействительный элемент, например:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Вы получите ответ о том, что данные недействительны, содержащий следующее тело:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### `HTTPException` в FastAPI или в Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** имеет собственный `HTTPException`.
Класс ошибок **FastAPI** `HTTPException` наследует от класса ошибок Starlette `HTTPException`.
Единственное отличие состоит в том, что `HTTPException` в **FastAPI** принимает любые данные, пригодные для преобразования в JSON, в поле `detail`, тогда как `HTTPException` в Starlette принимает для него только строки.
Таким образом, вы можете продолжать вызывать `HTTPException` от **FastAPI** как обычно в своем коде.
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для `HTTPException` от Starlette.
Таким образом, если какая-либо часть внутреннего кодa Starlette, расширение или плагин Starlette вызовет исключение Starlette `HTTPException`, ваш обработчик сможет перехватить и обработать его.
В данном примере, чтобы иметь возможность использовать оба `HTTPException` в одном коде, исключения Starlette переименованы в `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Переиспользование обработчиков исключений **FastAPI** { #reuse-fastapis-exception-handlers }
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
================================================
FILE: docs/ru/docs/tutorial/header-param-models.md
================================================
# Модели Header-параметров { #header-parameter-models }
Если у вас есть группа связанных **header-параметров**, то вы можете объединить их в одну **Pydantic-модель**.
Это позволит вам **переиспользовать модель** в **разных местах**, а также задать валидацию и метаданные сразу для всех параметров. 😎
/// note | Заметка
Этот функционал доступен в FastAPI начиная с версии `0.115.0`. 🤓
///
## Header-параметры в виде Pydantic-модели { #header-parameters-with-a-pydantic-model }
Объявите нужные **header-параметры** в **Pydantic-модели** и затем аннотируйте параметр как `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** **извлечёт** данные для **каждого поля** из **заголовков** запроса и выдаст заданную вами Pydantic-модель.
## Проверьте документацию { #check-the-docs }
Вы можете посмотреть нужные header-параметры в графическом интерфейсе сгенерированной документации по пути `/docs`:
## Как запретить дополнительные заголовки { #forbid-extra-headers }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** заголовки, которые вы хотите получать.
Вы можете использовать возможности конфигурации Pydantic-модели для того, чтобы запретить (`forbid`) любые дополнительные (`extra`) поля:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Если клиент попробует отправить **дополнительные заголовки**, то в ответ он получит **ошибку**.
Например, если клиент попытается отправить заголовок `tool` со значением `plumbus`, то в ответ он получит ошибку, сообщающую ему, что header-параметр `tool` не разрешен:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Как отключить автоматическое преобразование подчеркиваний { #disable-convert-underscores }
Как и в случае с обычными заголовками, если у вас в именах параметров имеются символы подчеркивания, они **автоматически преобразовываются в дефис**.
Например, если в коде есть header-параметр `save_data`, то ожидаемый HTTP-заголовок будет `save-data` и именно так он будет отображаться в документации.
Если по каким-то причинам вам нужно отключить данное автоматическое преобразование, это можно сделать и для Pydantic-моделей для header-параметров.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Внимание
Перед тем как устанавливать для параметра `convert_underscores` значение `False`, имейте в виду, что некоторые HTTP-прокси и серверы не разрешают использовать заголовки с символами подчеркивания.
///
## Резюме { #summary }
Вы можете использовать **Pydantic-модели** для объявления **header-параметров** в **FastAPI**. 😎
================================================
FILE: docs/ru/docs/tutorial/header-params.md
================================================
# Header-параметры { #header-parameters }
Вы можете определить параметры заголовка таким же образом, как вы определяете параметры `Query`, `Path` и `Cookie`.
## Импорт `Header` { #import-header }
Сперва импортируйте `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Объявление параметров `Header` { #declare-header-parameters }
Затем объявите параметры заголовка, используя ту же структуру, что и с `Path`, `Query` и `Cookie`.
Первое значение является значением по умолчанию, вы можете передать все дополнительные параметры валидации или аннотации:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Технические детали
`Header` - это "родственный" класс `Path`, `Query` и `Cookie`. Он также наследуется от того же общего класса `Param`.
Но помните, что когда вы импортируете `Query`, `Path`, `Header` и другие из `fastapi`, на самом деле это функции, которые возвращают специальные классы.
///
/// info | Информация
Чтобы объявить заголовки, важно использовать `Header`, иначе параметры интерпретируются как query-параметры.
///
## Автоматическое преобразование { #automatic-conversion }
`Header` обладает небольшой дополнительной функциональностью в дополнение к тому, что предоставляют `Path`, `Query` и `Cookie`.
Большинство стандартных заголовков разделены символом "дефис", также известным как "минус" (`-`).
Но переменная вроде `user-agent` недопустима в Python.
По умолчанию `Header` преобразует символы имен параметров из символа подчеркивания (`_`) в дефис (`-`) для извлечения и документирования заголовков.
Кроме того, HTTP-заголовки не чувствительны к регистру, поэтому вы можете объявить их в стандартном стиле Python (также известном как "snake_case").
Таким образом вы можете использовать `user_agent`, как обычно, в коде Python, вместо того, чтобы вводить заглавные буквы как `User_Agent` или что-то подобное.
Если по какой-либо причине вам необходимо отключить автоматическое преобразование подчеркиваний в дефисы, установите для параметра `convert_underscores` в `Header` значение `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Внимание
Прежде чем установить для `convert_underscores` значение `False`, имейте в виду, что некоторые HTTP-прокси и серверы запрещают использование заголовков с подчеркиванием.
///
## Повторяющиеся заголовки { #duplicate-headers }
Есть возможность получать несколько заголовков с одним и тем же именем, но разными значениями.
Вы можете определить эти случаи, используя список в объявлении типа.
Вы получите все значения из повторяющегося заголовка в виде `list` Python.
Например, чтобы объявить заголовок `X-Token`, который может появляться более одного раза, вы можете написать:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Если вы взаимодействуете с этой *операцией пути*, отправляя два HTTP-заголовка, таких как:
```
X-Token: foo
X-Token: bar
```
Ответ был бы таким:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Резюме { #recap }
Объявляйте заголовки с помощью `Header`, используя тот же общий шаблон, как при `Query`, `Path` и `Cookie`.
И не беспокойтесь о символах подчеркивания в ваших переменных, **FastAPI** позаботится об их преобразовании.
================================================
FILE: docs/ru/docs/tutorial/index.md
================================================
# Учебник - Руководство пользователя { #tutorial-user-guide }
В этом руководстве шаг за шагом показано, как использовать **FastAPI** с большинством его функций.
Каждый раздел постепенно основывается на предыдущих, но структура разделяет темы, так что вы можете сразу перейти к нужной теме для решения ваших конкретных задач по API.
Он также создан как справочник на будущее, чтобы вы могли вернуться и посмотреть именно то, что вам нужно.
## Запустите код { #run-the-code }
Все блоки кода можно копировать и использовать напрямую (это действительно протестированные файлы Python).
Чтобы запустить любой из примеров, скопируйте код в файл `main.py` и запустите `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
**НАСТОЯТЕЛЬНО рекомендуется** написать или скопировать код, отредактировать его и запустить локально.
Использование кода в вашем редакторе кода — это то, что действительно показывает преимущества FastAPI: вы увидите, как мало кода нужно написать, все проверки типов, автозавершение и т.д.
---
## Установка FastAPI { #install-fastapi }
Первый шаг — установить FastAPI.
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, и затем **установите FastAPI**:
/// note | Примечание
При установке с помощью `pip install "fastapi[standard]"` добавляются некоторые стандартные необязательные зависимости по умолчанию, включая `fastapi-cloud-cli`, который позволяет развернуть приложение на [FastAPI Cloud](https://fastapicloud.com).
Если вы не хотите иметь эти необязательные зависимости, установите просто `pip install fastapi`.
Если вы хотите установить стандартные зависимости, но без `fastapi-cloud-cli`, установите `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip | Совет
У FastAPI есть [официальное расширение для VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (и Cursor), которое предоставляет множество функций, включая обзор операций пути, поиск операций пути, навигацию CodeLens в тестах (переход к определению из тестов), а также развертывание в FastAPI Cloud и просмотр логов - всё прямо из вашего редактора кода.
///
## Продвинутое руководство пользователя { #advanced-user-guide }
Существует также **Продвинутое руководство пользователя**, которое вы сможете прочитать после **Учебник - Руководство пользователя**.
**Продвинутое руководство пользователя** основано на этом, использует те же концепции и обучает некоторым дополнительным функциям.
Но сначала вам следует прочитать **Учебник - Руководство пользователя** (то, что вы читаете прямо сейчас).
Оно спроектировано так, что вы можете создать полноценное приложение, используя только **Учебник - Руководство пользователя**, а затем расширить его различными способами, в зависимости от ваших потребностей, используя дополнительные идеи из **Продвинутого руководства пользователя**.
================================================
FILE: docs/ru/docs/tutorial/metadata.md
================================================
# URL-адреса метаданных и документации { #metadata-and-docs-urls }
Вы можете настроить несколько конфигураций метаданных в вашем **FastAPI** приложении.
## Метаданные для API { #metadata-for-api }
Вы можете задать следующие поля, которые используются в спецификации OpenAPI и в UI автоматической документации API:
| Параметр | Тип | Описание |
|------------|------|-------------|
| `title` | `str` | Заголовок API. |
| `summary` | `str` | Краткое резюме API. Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Краткое описание API. Может быть использован Markdown. |
| `version` | `string` | Версия API. Версия вашего собственного приложения, а не OpenAPI. К примеру `2.5.0`. |
| `terms_of_service` | `str` | Ссылка к условиям пользования API. Если указано, то это должен быть URL-адрес. |
| `contact` | `dict` | Контактная информация для открытого API. Может содержать несколько полей. поля contact
Параметр
Тип
Описание
name
str
Идентификационное имя контактного лица/организации.
url
str
URL указывающий на контактную информацию. ДОЛЖЕН быть в формате URL.
email
str
Email адрес контактного лица/организации. ДОЛЖЕН быть в формате email адреса.
|
| `license_info` | `dict` | Информация о лицензии открытого API. Может содержать несколько полей. поля license_info
Параметр
Тип
Описание
name
str
ОБЯЗАТЕЛЬНО (если установлен параметр license_info). Название лицензии, используемой для API.
identifier
str
Выражение лицензии [SPDX](https://spdx.org/licenses/) для API. Поле identifier взаимоисключающее с полем url. Доступно начиная с OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
URL, указывающий на лицензию, используемую для API. ДОЛЖЕН быть в формате URL.
|
Вы можете задать их следующим образом:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Подсказка
Вы можете использовать Markdown в поле `description`, и оно будет отображено в выводе.
///
С этой конфигурацией автоматическая документация API будет выглядеть так:
## Идентификатор лицензии { #license-identifier }
Начиная с OpenAPI 3.1.0 и FastAPI 0.99.0, вы также можете задать `license_info` с помощью `identifier` вместо `url`.
К примеру:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Метаданные для тегов { #metadata-for-tags }
Вы также можете добавить дополнительные метаданные для различных тегов, используемых для группировки ваших операций пути с помощью параметра `openapi_tags`.
Он принимает список, содержащий один словарь для каждого тега.
Каждый словарь может содержать в себе:
* `name` (**обязательно**): `str`-значение с тем же именем тега, которое вы используете в параметре `tags` в ваших *операциях пути* и `APIRouter`ах.
* `description`: `str`-значение с кратким описанием для тега. Может содержать Markdown и будет отображаться в UI документации.
* `externalDocs`: `dict`-значение описывающее внешнюю документацию. Включает в себя:
* `description`: `str`-значение с кратким описанием для внешней документации.
* `url` (**обязательно**): `str`-значение с URL-адресом для внешней документации.
### Создание метаданных для тегов { #create-metadata-for-tags }
Давайте попробуем сделать это на примере с тегами для `users` и `items`.
Создайте метаданные для ваших тегов и передайте их в параметре `openapi_tags`:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Помните, что вы можете использовать Markdown внутри описания, к примеру "login" будет отображен жирным шрифтом (**login**) и "fancy" будет отображаться курсивом (_fancy_).
/// tip | Подсказка
Вам необязательно добавлять метаданные для всех используемых тегов
///
### Используйте собственные теги { #use-your-tags }
Используйте параметр `tags` с вашими *операциями пути* (и `APIRouter`ами), чтобы присвоить им различные теги:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Дополнительная информация
Узнайте больше о тегах в [Конфигурации операции пути](path-operation-configuration.md#tags).
///
### Проверьте документацию { #check-the-docs }
Теперь, если вы проверите документацию, вы увидите всю дополнительную информацию:
### Порядок расположения тегов { #order-of-tags }
Порядок расположения словарей метаданных для каждого тега определяет также порядок, отображаемый в UI документации.
К примеру, несмотря на то, что `users` будут идти после `items` в алфавитном порядке, они отображаются раньше, потому что мы добавляем свои метаданные в качестве первого словаря в списке.
## URL-адрес OpenAPI { #openapi-url }
По умолчанию схема OpenAPI отображена по адресу `/openapi.json`.
Но вы можете изменить это с помощью параметра `openapi_url`.
К примеру, чтобы задать её отображение по адресу `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые её используют.
## URL-адреса документации { #docs-urls }
Вы можете изменить конфигурацию двух пользовательских интерфейсов документации, которые включены:
* **Swagger UI**: отображаемый по адресу `/docs`.
* Вы можете задать его URL с помощью параметра `docs_url`.
* Вы можете отключить это с помощью настройки `docs_url=None`.
* **ReDoc**: отображаемый по адресу `/redoc`.
* Вы можете задать его URL с помощью параметра `redoc_url`.
* Вы можете отключить это с помощью настройки `redoc_url=None`.
К примеру, чтобы задать отображение Swagger UI по адресу `/documentation` и отключить ReDoc:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/ru/docs/tutorial/middleware.md
================================================
# Middleware (Промежуточный слой) { #middleware }
Вы можете добавить middleware (промежуточный слой) в **FastAPI** приложение.
"Middleware" - это функция, которая выполняется с каждым **запросом** до его обработки какой-либо конкретной *операцией пути*. А также с каждым **ответом** перед его возвращением.
* Она принимает каждый поступающий **запрос**.
* Может что-то сделать с этим **запросом** или выполнить любой нужный код.
* Затем передает **запрос** для последующей обработки (какой-либо *операцией пути*).
* Получает **ответ** (от *операции пути*).
* Может что-то сделать с этим **ответом** или выполнить любой нужный код.
* И возвращает **ответ**.
/// note | Технические детали
Если у вас есть зависимости с `yield`, то код выхода (код после `yield`) будет выполняться *после* middleware.
Если были какие‑либо фоновые задачи (рассматриваются в разделе [Фоновые задачи](background-tasks.md), вы увидите это позже), они будут запущены *после* всех middleware.
///
## Создание middleware { #create-a-middleware }
Для создания middleware используйте декоратор `@app.middleware("http")` поверх функции.
Функция middleware получает:
* `request`.
* Функцию `call_next`, которая получает `request` в качестве параметра.
* Эта функция передаёт `request` соответствующей *операции пути*.
* Затем она возвращает `response`, сгенерированный соответствующей *операцией пути*.
* Также имеется возможность видоизменить `response` перед тем как его вернуть.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | Совет
Имейте в виду, что можно добавлять проприетарные HTTP-заголовки [с префиксом `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Но если вы хотите, чтобы клиент в браузере мог видеть ваши пользовательские заголовки, необходимо добавить их в настройки CORS ([CORS (Cross-Origin Resource Sharing)](cors.md)), используя параметр `expose_headers`, описанный в [документации по CORS Starlette](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Технические детали
Вы также можете использовать `from starlette.requests import Request`.
**FastAPI** предоставляет такой доступ для удобства разработчиков. Но, на самом деле, это `Request` из Starlette.
///
### До и после `response` { #before-and-after-the-response }
Вы можете добавить код, использующий `request`, до передачи его какой-либо *операции пути*.
А также после формирования `response`, до того, как вы его вернёте.
Например, вы можете добавить собственный заголовок `X-Process-Time`, содержащий время в секундах, необходимое для обработки запроса и генерации ответа:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | Совет
Мы используем [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) вместо `time.time()` для обеспечения большей точности в таких случаях. 🤓
///
## Порядок выполнения нескольких middleware { #multiple-middleware-execution-order }
Когда вы добавляете несколько middleware с помощью декоратора `@app.middleware()` или метода `app.add_middleware()`, каждое новое middleware оборачивает приложение, формируя стек. Последнее добавленное middleware — самое внешнее (*outermost*), а первое — самое внутреннее (*innermost*).
На пути обработки запроса сначала выполняется самое внешнее middleware.
На пути формирования ответа оно выполняется последним.
Например:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Это приводит к следующему порядку выполнения:
* **Запрос**: MiddlewareB → MiddlewareA → маршрут
* **Ответ**: маршрут → MiddlewareA → MiddlewareB
Такое стековое поведение обеспечивает предсказуемый и управляемый порядок выполнения middleware.
## Другие middleware { #other-middlewares }
О других middleware вы можете узнать больше в разделе [Расширенное руководство пользователя: Продвинутое middleware](../advanced/middleware.md).
В следующем разделе вы можете прочитать, как настроить CORS с помощью middleware.
================================================
FILE: docs/ru/docs/tutorial/path-operation-configuration.md
================================================
# Конфигурация операций пути { #path-operation-configuration }
Существует несколько параметров, которые вы можете передать вашему *декоратору операций пути* для его настройки.
/// warning | Внимание
Помните, что эти параметры передаются непосредственно *декоратору операций пути*, а не вашей *функции-обработчику пути*.
///
## Статус-код ответа { #response-status-code }
Вы можете определить (HTTP) `status_code`, который будет использован в ответах вашей *операции пути*.
Вы можете передать только `int`-значение кода, например `404`.
Но если вы не помните, для чего нужен каждый числовой код, вы можете использовать сокращенные константы в `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Этот статус-код будет использован в ответе и будет добавлен в схему OpenAPI.
/// note | Технические детали
Вы также можете использовать `from starlette import status`.
**FastAPI** предоставляет тот же `starlette.status` под псевдонимом `fastapi.status` для удобства разработчика. Но его источник - это непосредственно Starlette.
///
## Теги { #tags }
Вы можете добавлять теги к вашим *операциям пути*, добавив параметр `tags` с `list` заполненным `str`-значениями (обычно в нём только одна строка):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Они будут добавлены в схему OpenAPI и будут использованы в автоматической документации интерфейса:
### Теги с перечислениями { #tags-with-enums }
Если у вас большое приложение, вы можете прийти к необходимости добавить **несколько тегов**, и возможно, вы захотите убедиться в том, что всегда используете **один и тот же тег** для связанных *операций пути*.
В этих случаях, имеет смысл хранить теги в классе `Enum`.
**FastAPI** поддерживает это так же, как и в случае с обычными строками:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Краткое и развёрнутое содержание { #summary-and-description }
Вы можете добавить параметры `summary` и `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Описание из строк документации { #description-from-docstring }
Так как описания обычно длинные и содержат много строк, вы можете объявить описание *операции пути* в строке документации функции, и **FastAPI** прочитает её оттуда.
Вы можете использовать [Markdown](https://en.wikipedia.org/wiki/Markdown) в строке документации, и он будет интерпретирован и отображён корректно (с учетом отступа в строке документации).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Он будет использован в интерактивной документации:
## Описание ответа { #response-description }
Вы можете указать описание ответа с помощью параметра `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Дополнительная информация
Помните, что `response_description` относится конкретно к ответу, а `description` относится к *операции пути* в целом.
///
/// check | Проверка
OpenAPI указывает, что каждой *операции пути* необходимо описание ответа.
Если вдруг вы не укажете его, то **FastAPI** автоматически сгенерирует это описание с текстом "Successful response".
///
## Обозначение *операции пути* как устаревшей { #deprecate-a-path-operation }
Если вам необходимо пометить *операцию пути* как устаревшую, при этом не удаляя её, передайте параметр `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Он будет четко помечен как устаревший в интерактивной документации:
Проверьте, как будут выглядеть устаревшие и не устаревшие *операции пути*:
## Резюме { #recap }
Вы можете легко конфигурировать и добавлять метаданные в ваши *операции пути*, передавая параметры *декораторам операций пути*.
================================================
FILE: docs/ru/docs/tutorial/path-params-numeric-validations.md
================================================
# Path-параметры и валидация числовых данных { #path-parameters-and-numeric-validations }
Так же, как с помощью `Query` вы можете добавлять валидацию и метаданные для query-параметров, так и с помощью `Path` вы можете добавлять такую же валидацию и метаданные для path-параметров.
## Импорт `Path` { #import-path }
Сначала импортируйте `Path` из `fastapi`, а также импортируйте `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Информация
Поддержка `Annotated` была добавлена в FastAPI начиная с версии 0.95.0 (и с этой версии рекомендуется использовать этот подход).
Если вы используете более старую версию, вы столкнётесь с ошибками при попытке использовать `Annotated`.
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) как минимум до 0.95.1 перед тем, как использовать `Annotated`.
///
## Определите метаданные { #declare-metadata }
Вы можете указать все те же параметры, что и для `Query`.
Например, чтобы указать значение метаданных `title` для path-параметра `item_id`, вы можете написать:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Примечание
Path-параметр всегда является обязательным, поскольку он должен быть частью пути. Даже если вы объявите его как `None` или зададите значение по умолчанию, это ни на что не повлияет — параметр всё равно будет обязательным.
///
## Задайте нужный вам порядок параметров { #order-the-parameters-as-you-need }
/// tip | Подсказка
Это не имеет большого значения, если вы используете `Annotated`.
///
Допустим, вы хотите объявить query-параметр `q` как обязательный параметр типа `str`.
И если вам больше ничего не нужно указывать для этого параметра, то нет необходимости использовать `Query`.
Но вам по-прежнему нужно использовать `Path` для path-параметра `item_id`. И по какой-либо причине вы не хотите использовать `Annotated`.
Если вы поместите параметр со значением по умолчанию перед другим параметром, у которого нет значения по умолчанию, то Python укажет на ошибку.
Но вы можете изменить порядок параметров, чтобы параметр без значения по умолчанию (query-параметр `q`) шёл первым.
Это не имеет значения для **FastAPI**. Он распознает параметры по их названиям, типам и значениям по умолчанию (`Query`, `Path`, и т.д.), ему не важен их порядок.
Поэтому вы можете определить функцию так:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете значения по умолчанию параметров функции для `Query()` или `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Задайте нужный вам порядок параметров, полезные приёмы { #order-the-parameters-as-you-need-tricks }
/// tip | Подсказка
Это не имеет большого значения, если вы используете `Annotated`.
///
Здесь описан **небольшой приём**, который может оказаться удобным, хотя часто он вам не понадобится.
Если вы хотите:
* объявить query-параметр `q` без `Query` и без значения по умолчанию
* объявить path-параметр `item_id` с помощью `Path`
* указать их в другом порядке
* не использовать `Annotated`
...то вы можете использовать специальную возможность синтаксиса Python.
Передайте `*` в качестве первого параметра функции.
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как kwargs, даже если у них нет значений по умолчанию.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Лучше с `Annotated` { #better-with-annotated }
Имейте в виду, что если вы используете `Annotated`, то, поскольку вы не используете значений по умолчанию для параметров функции, у вас не возникнет подобной проблемы и вам, вероятно, не придётся использовать `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Валидация числовых данных: больше или равно { #number-validations-greater-than-or-equal }
С помощью `Query` и `Path` (и других классов, которые мы разберём позже) вы можете добавлять ограничения для числовых данных.
В этом примере при указании `ge=1`, параметр `item_id` должен быть целым числом "`g`reater than or `e`qual" — больше или равно `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Валидация числовых данных: больше и меньше или равно { #number-validations-greater-than-and-less-than-or-equal }
То же самое применимо к:
* `gt`: больше (`g`reater `t`han)
* `le`: меньше или равно (`l`ess than or `e`qual)
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Валидация числовых данных: числа с плавающей точкой, больше и меньше { #number-validations-floats-greater-than-and-less-than }
Валидация также применима к значениям типа `float`.
В этом случае становится важной возможность добавить ограничение gt, вместо ge, поскольку в таком случае вы можете, например, создать ограничение, чтобы значение было больше `0`, даже если оно меньше `1`.
Таким образом, `0.5` будет корректным значением. А `0.0` или `0` — нет.
То же самое справедливо и для lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Резюме { #recap }
С помощью `Query`, `Path` (и других классов, которые мы пока не затронули) вы можете добавлять метаданные и строковую валидацию тем же способом, как и в главе [Query-параметры и валидация строк](query-params-str-validations.md).
А также вы можете добавить валидацию числовых данных:
* `gt`: больше (`g`reater `t`han)
* `ge`: больше или равно (`g`reater than or `e`qual)
* `lt`: меньше (`l`ess `t`han)
* `le`: меньше или равно (`l`ess than or `e`qual)
/// info | Информация
`Query`, `Path` и другие классы, которые вы разберёте позже, являются наследниками общего класса `Param`.
Все они используют те же параметры для дополнительной валидации и метаданных, которые вы видели ранее.
///
/// note | Технические детали
`Query`, `Path` и другие "классы", которые вы импортируете из `fastapi`, на самом деле являются функциями, которые при вызове возвращают экземпляры одноимённых классов.
Объект `Query`, который вы импортируете, является функцией. И при вызове она возвращает экземпляр одноимённого класса `Query`.
Использование функций (вместо использования классов напрямую) нужно для того, чтобы ваш редактор не подсвечивал ошибки, связанные с их типами.
Таким образом вы можете использовать привычный вам редактор и инструменты разработки, не добавляя дополнительных конфигураций для игнорирования подобных ошибок.
///
================================================
FILE: docs/ru/docs/tutorial/path-params.md
================================================
# Path-параметры { #path-parameters }
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
Значение параметра пути `item_id` будет передано в функцию в качестве аргумента `item_id`.
Если запустите этот пример и перейдёте по адресу: [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), то увидите ответ:
```JSON
{"item_id":"foo"}
```
## Параметры пути с типами { #path-parameters-with-types }
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Здесь, `item_id` объявлен типом `int`.
/// check | Заметка
Это обеспечит поддержку редактора кода внутри функции (проверка ошибок, автозавершение и т.п.).
///
## Преобразование данных { #data-conversion }
Если запустите этот пример и перейдёте по адресу: [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), то увидите ответ:
```JSON
{"item_id":3}
```
/// check | Заметка
Обратите внимание на значение `3`, которое получила (и вернула) функция. Это целочисленный Python `int`, а не строка `"3"`.
Используя такое объявление типов, **FastAPI** выполняет автоматический HTTP-запрос "парсинг".
///
## Валидация данных { #data-validation }
Если откроете браузер по адресу [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), то увидите интересную HTTP-ошибку:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
из-за того, что параметр пути `item_id` имеет значение `"foo"`, которое не является типом `int`.
Та же ошибка возникнет, если вместо `int` передать `float`, например: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Заметка
**FastAPI** обеспечивает валидацию данных, используя всё те же определения типов.
Обратите внимание, что в тексте ошибки явно указано место, не прошедшее проверку.
Это очень полезно при разработке и отладке кода, который взаимодействует с API.
///
## Документация { #documentation }
И теперь, когда откроете браузер по адресу: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), то увидите вот такую автоматически сгенерированную документацию API:
/// check | Заметка
Ещё раз, просто используя определения типов, **FastAPI** обеспечивает автоматическую интерактивную документацию (с интеграцией Swagger UI).
Обратите внимание, что параметр пути объявлен целочисленным.
///
## Преимущества стандартизации, альтернативная документация { #standards-based-benefits-alternative-documentation }
Поскольку сгенерированная схема соответствует стандарту [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md), её можно использовать со множеством совместимых инструментов.
Именно поэтому, **FastAPI** сам предоставляет альтернативную документацию API (используя ReDoc), которую можно получить по адресу: [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
По той же причине, есть множество совместимых инструментов, включая инструменты генерации кода для многих языков.
## Pydantic { #pydantic }
Вся проверка данных выполняется под капотом с помощью [Pydantic](https://docs.pydantic.dev/), поэтому вы получаете все его преимущества. И вы можете быть уверены, что находитесь в надёжных руках.
Вы можете использовать в аннотациях как простые типы данных, вроде `str`, `float`, `bool`, так и более сложные типы.
Некоторые из них рассматриваются в следующих главах данного руководства.
## Порядок имеет значение { #order-matters }
При создании *операций пути* можно столкнуться с ситуацией, когда путь является фиксированным.
Например, `/users/me`. Предположим, что это путь для получения данных о текущем пользователе.
У вас также может быть путь `/users/{user_id}`, чтобы получить данные о конкретном пользователе по его ID.
Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.
Аналогично, вы не можете переопределить операцию с путем:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Первый будет выполняться всегда, так как путь совпадает первым.
## Предопределенные значения { #predefined-values }
Что если нам нужно заранее определить допустимые *параметры пути*, которые *операция пути* может принимать? В таком случае можно использовать стандартное перечисление `Enum` Python.
### Создание класса `Enum` { #create-an-enum-class }
Импортируйте `Enum` и создайте подкласс, который наследуется от `str` и `Enum`.
Мы наследуемся от `str`, чтобы документация API могла понять, что значения должны быть типа `string` и отображалась правильно.
Затем создайте атрибуты класса с фиксированными допустимыми значениями:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Подсказка
Если интересно, то "AlexNet", "ResNet" и "LeNet" - это названия моделей Машинного обучения.
///
### Определение *параметра пути* { #declare-a-path-parameter }
Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Проверьте документацию { #check-the-docs }
Поскольку доступные значения *параметра пути* определены заранее, интерактивная документация может наглядно их отображать:
### Работа с *перечислениями* в Python { #working-with-python-enumerations }
Значение *параметра пути* будет *элементом перечисления*.
#### Сравнение *элементов перечисления* { #compare-enumeration-members }
Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Получение *значения перечисления* { #get-the-enumeration-value }
Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Подсказка
Значение `"lenet"` также можно получить с помощью `ModelName.lenet.value`.
///
#### Возврат *элементов перечисления* { #return-enumeration-members }
Из *операции пути* можно вернуть *элементы перечисления*, даже вложенные в тело JSON (например в `dict`).
Они будут преобразованы в соответствующие значения (в данном случае - строки) перед их возвратом клиенту:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
На стороне клиента вы получите такой JSON-ответ:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Path-параметры, содержащие пути { #path-parameters-containing-paths }
Предположим, что есть *операция пути* с путем `/files/{file_path}`.
Но вам нужно, чтобы `file_path` сам содержал *путь*, например, `home/johndoe/myfile.txt`.
Тогда URL для этого файла будет такой: `/files/home/johndoe/myfile.txt`.
### Поддержка OpenAPI { #openapi-support }
OpenAPI не поддерживает способов объявления *параметра пути*, содержащего внутри *путь*, так как это может привести к сценариям, которые сложно определять и тестировать.
Тем не менее это можно сделать в **FastAPI**, используя один из внутренних инструментов Starlette.
Документация по-прежнему будет работать, хотя и не добавит никакой информации о том, что параметр должен содержать путь.
### Конвертер пути { #path-convertor }
Благодаря одной из опций Starlette, можете объявить *параметр пути*, содержащий *путь*, используя URL вроде:
```
/files/{file_path:path}
```
В этом случае `file_path` - это имя параметра, а часть `:path`, указывает, что параметр должен соответствовать любому *пути*.
Можете использовать так:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Подсказка
Возможно, вам понадобится, чтобы параметр содержал `/home/johndoe/myfile.txt` с ведущим слэшем (`/`).
В этом случае URL будет таким: `/files//home/johndoe/myfile.txt`, с двойным слэшем (`//`) между `files` и `home`.
///
## Резюме { #recap }
Используя **FastAPI** вместе со стандартными объявлениями типов Python (короткими и интуитивно понятными), вы получаете:
* Поддержку редактора кода (проверку ошибок, автозавершение и т.п.)
* "Парсинг" данных
* Валидацию данных
* Аннотации API и автоматическую документацию
И объявлять типы достаточно один раз.
Это, вероятно, является главным заметным преимуществом **FastAPI** по сравнению с альтернативными фреймворками (кроме сырой производительности).
================================================
FILE: docs/ru/docs/tutorial/query-param-models.md
================================================
# Модели Query-Параметров { #query-parameter-models }
Если у вас есть группа связанных **query-параметров**, то вы можете объединить их в одну **Pydantic-модель**.
Это позволит вам **переиспользовать модель** в **разных местах**, устанавливать валидаторы и метаданные, в том числе для сразу всех параметров, в одном месте. 😎
/// note | Заметка
Это поддерживается начиная с версии FastAPI `0.115.0`. 🤓
///
## Pydantic-Модель для Query-Параметров { #query-parameters-with-a-pydantic-model }
Объявите нужные **query-параметры** в **Pydantic-модели**, а после аннотируйте параметр как `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI извлечёт** данные соответствующие **каждому полю модели** из **query-параметров** запроса и выдаст вам объявленную Pydantic-модель заполненную ими.
## Проверьте Сгенерированную Документацию { #check-the-docs }
Вы можете посмотреть query-параметры в графическом интерфейсе сгенерированной документации по пути `/docs`:
## Запретить Дополнительные Query-Параметры { #forbid-extra-query-parameters }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** query-параметры, которые вы хотите получить.
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля.
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Если клиент попробует отправить **дополнительные** данные в **query-параметрах**, то в ответ он получит **ошибку**.
Например, если клиент попытается отправить query-параметр `tool` с значением `plumbus`, в виде:
```http
https://example.com/items/?limit=10&tool=plumbus
```
То в ответ он получит **ошибку**, сообщающую ему, что query-параметр `tool` не разрешен:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Заключение { #summary }
Вы можете использовать **Pydantic-модели** для объявления **query-параметров** в **FastAPI**. 😎
/// tip | Совет
Спойлер: вы также можете использовать Pydantic-модели, чтобы объявлять cookies и HTTP-заголовки, но об этом вы прочитаете позже. 🤫
///
================================================
FILE: docs/ru/docs/tutorial/query-params-str-validations.md
================================================
# Query-параметры и валидация строк { #query-parameters-and-string-validations }
**FastAPI** позволяет определять дополнительную информацию и выполнять валидацию для ваших параметров.
Рассмотрим это приложение в качестве примера:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Query-параметр `q` имеет тип `str | None`, это означает, что он имеет тип `str`, но также может быть `None`. Значение по умолчанию действительно `None`, поэтому FastAPI будет знать, что он не обязателен.
/// note | Примечание
FastAPI поймёт, что значение `q` не обязательно, из‑за значения по умолчанию `= None`.
Аннотация `str | None` позволит вашему редактору кода обеспечить лучшую поддержку и находить ошибки.
///
## Дополнительная валидация { #additional-validation }
Мы собираемся добавить ограничение: хотя `q` и необязателен, когда он передан, **его длина не должна превышать 50 символов**.
### Импорт `Query` и `Annotated` { #import-query-and-annotated }
Чтобы сделать это, сначала импортируйте:
* `Query` из `fastapi`
* `Annotated` из `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Дополнительная информация
Поддержка `Annotated` (и рекомендация использовать его) появилась в FastAPI версии 0.95.0.
Если у вас более старая версия, при попытке использовать `Annotated` вы получите ошибки.
Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) как минимум до 0.95.1 перед использованием `Annotated`.
///
## Использовать `Annotated` в типе для параметра `q` { #use-annotated-in-the-type-for-the-q-parameter }
Помните, я уже говорил, что `Annotated` можно использовать для добавления метаданных к параметрам в разделе [Введение в типы Python](../python-types.md#type-hints-with-metadata-annotations)?
Пришло время использовать его с FastAPI. 🚀
У нас была такая аннотация типа:
```Python
q: str | None = None
```
Мы «обернём» это в `Annotated`, и получится:
```Python
q: Annotated[str | None] = None
```
Обе версии означают одно и то же: `q` — параметр, который может быть `str` или `None`, и по умолчанию равен `None`.
А теперь к самому интересному. 🎉
## Добавим `Query` в `Annotated` для параметра `q` { #add-query-to-annotated-in-the-q-parameter }
Теперь, когда у нас есть `Annotated`, куда можно поместить дополнительную информацию (в нашем случае — дополнительные правила валидации), добавим `Query` внутрь `Annotated` и установим параметр `max_length` равным `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Обратите внимание, что значение по умолчанию по‑прежнему `None`, то есть параметр остаётся необязательным.
Но теперь, добавив `Query(max_length=50)` внутрь `Annotated`, мы говорим FastAPI, что этому значению нужна **дополнительная валидация** — максимум 50 символов. 😎
/// tip | Совет
Здесь мы используем `Query()`, потому что это **query-параметр**. Позже мы увидим другие — `Path()`, `Body()`, `Header()` и `Cookie()`, — они также принимают те же аргументы, что и `Query()`.
///
Теперь FastAPI будет:
* **валидировать** данные, удостоверяясь, что максимальная длина — 50 символов;
* показывать **понятную ошибку** клиенту, если данные невалидны;
* **документировать** параметр в *операции пути* схемы OpenAPI (он будет показан в **UI автоматической документации**).
## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value }
В предыдущих версиях FastAPI (до 0.95.0) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
/// tip | Подсказка
Для нового кода и везде, где это возможно, используйте `Annotated`, как описано выше. У этого есть несколько преимуществ (см. ниже) и нет недостатков. 🍰
///
Вот как можно использовать `Query()` как значение по умолчанию для параметра функции, установив `max_length` равным 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Так как в этом случае (без `Annotated`) мы заменяем в функции значение по умолчанию `None` на `Query()`, теперь нужно указать значение по умолчанию через параметр `Query(default=None)`, это служит той же цели — задать значение по умолчанию (по крайней мере для FastAPI).
Итак:
```Python
q: str | None = Query(default=None)
```
...делает параметр необязательным со значением по умолчанию `None`, так же как:
```Python
q: str | None = None
```
Но вариант с `Query` явно объявляет его как query-параметр.
Затем мы можем передать и другие параметры в `Query`. В данном случае — параметр `max_length`, применимый к строкам:
```Python
q: str | None = Query(default=None, max_length=50)
```
Это провалидирует данные, покажет понятную ошибку, если данные невалидны, и задокументирует параметр в *операции пути* схемы OpenAPI.
### `Query` как значение по умолчанию или внутри `Annotated` { #query-as-the-default-value-or-in-annotated }
Помните, что при использовании `Query` внутри `Annotated` нельзя указывать параметр `default` у `Query`.
Вместо этого используйте обычное значение по умолчанию параметра функции. Иначе это будет неоднозначно.
Например, так делать нельзя:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...потому что непонятно, какое значение должно быть по умолчанию: `"rick"` или `"morty"`.
Следовательно, используйте (предпочтительно):
```Python
q: Annotated[str, Query()] = "rick"
```
...или в старой кодовой базе вы увидите:
```Python
q: str = Query(default="rick")
```
### Преимущества `Annotated` { #advantages-of-annotated }
**Рекомендуется использовать `Annotated`** вместо задания значения по умолчанию в параметрах функции — так **лучше** по нескольким причинам. 🤓
**Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри.
Так как `Annotated` может содержать больше одной аннотации метаданных, теперь вы можете использовать ту же функцию и с другими инструментами, например с [Typer](https://typer.tiangolo.com/). 🚀
## Больше валидаций { #add-more-validations }
Можно также добавить параметр `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Регулярные выражения { #add-regular-expressions }
Вы можете определить регулярное выражение `pattern`, которому должен соответствовать параметр:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Данный шаблон регулярного выражения проверяет, что полученное значение параметра:
* `^`: начинается с следующих символов, до них нет символов.
* `fixedquery`: имеет точное значение `fixedquery`.
* `$`: заканчивается здесь, после `fixedquery` нет никаких символов.
Если вы теряетесь во всех этих идеях про **«регулярные выражения»**, не переживайте. Это сложная тема для многих. Многое можно сделать и без них.
Теперь вы знаете, что когда они понадобятся, вы сможете использовать их в **FastAPI**.
## Значения по умолчанию { #default-values }
Конечно, можно использовать и другие значения по умолчанию, не только `None`.
Допустим, вы хотите объявить, что query-параметр `q` должен иметь `min_length` равный `3` и значение по умолчанию `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Примечание
Наличие значения по умолчанию любого типа, включая `None`, делает параметр необязательным.
///
## Обязательные параметры { #required-parameters }
Когда не требуется объявлять дополнительные проверки или метаданные, можно сделать query-параметр `q` обязательным, просто не указывая значение по умолчанию, например:
```Python
q: str
```
вместо:
```Python
q: str | None = None
```
Но сейчас мы объявляем его через `Query`, например так:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Поэтому, когда вам нужно объявить значение как обязательное при использовании `Query`, просто не указывайте значение по умолчанию:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Обязательный, но может быть `None` { #required-can-be-none }
Можно объявить, что параметр может принимать `None`, но при этом остаётся обязательным. Это заставит клиентов отправлять значение, даже если это значение — `None`.
Для этого объявите, что `None` — валидный тип, но просто не задавайте значение по умолчанию:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Query-параметр - список / несколько значений { #query-parameter-list-multiple-values }
Когда вы явно объявляете query-параметр через `Query`, можно также указать, что он принимает список значений, иначе говоря — несколько значений.
Например, чтобы объявить query-параметр `q`, который может встречаться в URL несколько раз, можно написать:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Тогда при таком URL:
```
http://localhost:8000/items/?q=foo&q=bar
```
вы получите множественные значения *query-параметров* `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции-обработчика пути*, в *параметре функции* `q`.
Таким образом, ответ на этот URL будет:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Совет
Чтобы объявить query-параметр типа `list`, как в примере выше, нужно явно использовать `Query`, иначе он будет интерпретирован как тело запроса.
///
Интерактивная документация API обновится соответствующим образом и позволит передавать несколько значений:
### Query-параметр - список / несколько значений со значением по умолчанию { #query-parameter-list-multiple-values-with-defaults }
Можно также определить значение по умолчанию как `list`, если ничего не передано:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Если вы перейдёте по адресу:
```
http://localhost:8000/items/
```
значение по умолчанию для `q` будет: `["foo", "bar"]`, и ответом будет:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Просто `list` { #using-just-list }
Можно использовать `list` напрямую вместо `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Примечание
Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка.
Например, `list[int]` проверит (и задокументирует), что элементы списка — целые числа. А просто `list` — нет.
///
## Больше метаданных { #declare-more-metadata }
Можно добавить больше информации о параметре.
Эта информация будет включена в сгенерированную OpenAPI-схему и использована интерфейсами документации и внешними инструментами.
/// note | Примечание
Помните, что разные инструменты могут иметь разный уровень поддержки OpenAPI.
Некоторые из них пока могут не показывать всю дополнительную информацию, хотя в большинстве случаев недостающая возможность уже запланирована к разработке.
///
Можно задать `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
И `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Псевдонимы параметров { #alias-parameters }
Представьте, что вы хотите, чтобы параметр назывался `item-query`.
Например:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Но `item-query` — недопустимое имя переменной в Python.
Ближайший вариант — `item_query`.
Но вам всё равно нужно именно `item-query`...
Тогда можно объявить `alias`, и этот псевдоним будет использован для поиска значения параметра:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Маркировка параметров как устаревших { #deprecating-parameters }
Предположим, этот параметр вам больше не нравится.
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как устаревший.
Тогда передайте параметр `deprecated=True` в `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
В документации это будет показано так:
## Исключить параметры из OpenAPI { #exclude-parameters-from-openapi }
Чтобы исключить query-параметр из генерируемой OpenAPI-схемы (и, следовательно, из систем автоматической документации), укажите у `Query` параметр `include_in_schema=False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Кастомная валидация { #custom-validation }
Бывают случаи, когда нужна **кастомная валидация**, которую нельзя выразить параметрами выше.
В таких случаях можно использовать **кастомную функцию-валидатор**, которая применяется после обычной валидации (например, после проверки, что значение — это `str`).
Этого можно добиться, используя [`AfterValidator` Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) внутри `Annotated`.
/// tip | Совет
В Pydantic также есть [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) и другие. 🤓
///
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги ISBN или с `imdb-` для ID URL фильма на IMDB:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Дополнительная информация
Это доступно в Pydantic версии 2 и выше. 😎
///
/// tip | Совет
Если вам нужна валидация, требующая общения с каким‑либо **внешним компонентом** — базой данных или другим API — вместо этого используйте **Зависимости FastAPI**, вы познакомитесь с ними позже.
Эти кастомные валидаторы предназначены для проверок, которые можно выполнить, имея **только** те же **данные**, что пришли в запросе.
///
### Понимание этого кода { #understand-that-code }
Важный момент — это использовать **`AfterValidator` с функцией внутри `Annotated`**. Смело пропускайте эту часть. 🤸
---
Но если вам любопытен именно этот пример и всё ещё интересно, вот немного подробностей.
#### Строка и `value.startswith()` { #string-with-value-startswith }
Заметили? Метод строки `value.startswith()` может принимать кортеж — тогда будет проверено каждое значение из кортежа:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Случайный элемент { #a-random-item }
С помощью `data.items()` мы получаем итерируемый объект с кортежами, содержащими ключ и значение для каждого элемента словаря.
Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`.
Затем с `random.choice()` можно получить **случайное значение** из списка — то есть кортеж вида `(id, name)`. Это будет что‑то вроде `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
После этого мы **присваиваем эти два значения** кортежа переменным `id` и `name`.
Так что, если пользователь не передал ID элемента, он всё равно получит случайную рекомендацию.
...и всё это в **одной простой строке**. 🤯 Разве не прекрасен Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Резюме { #recap }
Вы можете объявлять дополнительные проверки и метаданные для параметров.
Общие метаданные и настройки:
* `alias`
* `title`
* `description`
* `deprecated`
Проверки, специфичные для строк:
* `min_length`
* `max_length`
* `pattern`
Кастомные проверки с использованием `AfterValidator`.
В этих примерах вы видели, как объявлять проверки для значений типа `str`.
Смотрите следующие главы, чтобы узнать, как объявлять проверки для других типов, например чисел.
================================================
FILE: docs/ru/docs/tutorial/query-params.md
================================================
# Query-параметры { #query-parameters }
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
Query-параметры представляют из себя набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
Например, в этом URL-адресе:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...параметры запроса такие:
* `skip`: со значением `0`
* `limit`: со значением `10`
Будучи частью URL-адреса, они "по умолчанию" являются строками.
Но когда вы объявляете их с использованием типов Python (в примере выше, как `int`), они конвертируются в указанный тип данных и проходят проверку на соответствие ему.
Все те же правила, которые применяются к path-параметрам, также применяются и query-параметрам:
* Поддержка от редактора кода (очевидно)
* "Парсинг" данных
* Проверка на соответствие данных (Валидация)
* Автоматическая документация
## Значения по умолчанию { #defaults }
Поскольку query-параметры не являются фиксированной частью пути, они могут быть не обязательными и иметь значения по умолчанию.
В примере выше значения по умолчанию равны `skip=0` и `limit=10`.
Таким образом, результат перехода по URL-адресу:
```
http://127.0.0.1:8000/items/
```
будет таким же, как если перейти используя параметры по умолчанию:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Но если вы введёте, например:
```
http://127.0.0.1:8000/items/?skip=20
```
Значения параметров в вашей функции будут:
* `skip=20`: потому что вы установили это в URL-адресе
* `limit=10`: т.к это было значение по умолчанию
## Необязательные параметры { #optional-parameters }
Аналогично, вы можете объявлять необязательные query-параметры, установив их значение по умолчанию, равное `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
В этом случае, параметр `q` будет не обязательным и будет иметь значение `None` по умолчанию.
/// check | Важно
Также обратите внимание, что **FastAPI** достаточно умён чтобы заметить, что параметр `item_id` является path-параметром, а `q` нет, поэтому, это параметр запроса.
///
## Преобразование типа параметра запроса { #query-parameter-type-conversion }
Вы также можете объявлять параметры с типом `bool`, которые будут преобразованы соответственно:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
В этом случае, если вы сделаете запрос:
```
http://127.0.0.1:8000/items/foo?short=1
```
или
```
http://127.0.0.1:8000/items/foo?short=True
```
или
```
http://127.0.0.1:8000/items/foo?short=true
```
или
```
http://127.0.0.1:8000/items/foo?short=on
```
или
```
http://127.0.0.1:8000/items/foo?short=yes
```
или в любом другом варианте написания (в верхнем регистре, с заглавной буквой, и т.п), внутри вашей функции параметр `short` будет иметь значение `True` типа данных `bool` . В противном случае - `False`.
## Смешивание query-параметров и path-параметров { #multiple-path-and-query-parameters }
Вы можете объявлять несколько query-параметров и path-параметров одновременно, **FastAPI** сам разберётся, что чем является.
И вы не обязаны объявлять их в каком-либо определенном порядке.
Они будут обнаружены по именам:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Обязательные query-параметры { #required-query-parameters }
Когда вы объявляете значение по умолчанию для параметра, который не является path-параметром (в этом разделе мы пока что рассмотрели только query-параметры), то он не является обязательным.
Если вы не хотите задавать конкретное значение, но хотите сделать параметр необязательным, вы можете установить значение по умолчанию равным `None`.
Но если вы хотите сделать query-параметр обязательным, вы можете просто не указывать значение по умолчанию:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Здесь параметр запроса `needy` является обязательным параметром с типом данных `str`.
Если вы откроете в браузере URL-адрес, например:
```
http://127.0.0.1:8000/items/foo-item
```
...без добавления обязательного параметра `needy`, вы увидите подобного рода ошибку:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Поскольку `needy` является обязательным параметром, вам необходимо указать его в URL-адресе:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...это будет работать:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
Конечно, вы можете определить некоторые параметры как обязательные, некоторые — со значением по умолчанию, а некоторые — полностью необязательные:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
В этом примере, у нас есть 3 параметра запроса:
* `needy`, обязательный `str`.
* `skip`, типа `int` и со значением по умолчанию `0`.
* `limit`, необязательный `int`.
/// tip | Подсказка
Вы можете использовать класс `Enum` также, как ранее применяли его с [Path-параметрами](path-params.md#predefined-values).
///
================================================
FILE: docs/ru/docs/tutorial/request-files.md
================================================
# Загрузка файлов { #request-files }
Используя класс `File`, мы можем позволить клиентам загружать файлы.
/// info | Дополнительная информация
Чтобы получать загруженные файлы, сначала установите [`python-multipart`](https://github.com/Kludex/python-multipart).
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет, например:
```console
$ pip install python-multipart
```
Это связано с тем, что загружаемые файлы передаются как "данные формы".
///
## Импорт `File` { #import-file }
Импортируйте `File` и `UploadFile` из модуля `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Определите параметры `File` { #define-file-parameters }
Создайте параметры `File` так же, как вы это делаете для `Body` или `Form`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Дополнительная информация
`File` - это класс, который наследуется непосредственно от `Form`.
Но помните, что когда вы импортируете `Query`, `Path`, `File` и другие из `fastapi`, на самом деле это функции, которые возвращают специальные классы.
///
/// tip | Подсказка
Для объявления тела файла необходимо использовать `File`, поскольку в противном случае параметры будут интерпретироваться как параметры запроса или параметры тела (JSON).
///
Файлы будут загружены как данные формы.
Если вы объявите тип параметра у *функции операции пути* как `bytes`, то **FastAPI** прочитает файл за вас, и вы получите его содержимое в виде `bytes`.
Следует иметь в виду, что все содержимое будет храниться в памяти. Это хорошо подходит для небольших файлов.
Однако возможны случаи, когда использование `UploadFile` может оказаться полезным.
## Параметры файла с `UploadFile` { #file-parameters-with-uploadfile }
Определите параметр файла с типом `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Использование `UploadFile` имеет ряд преимуществ перед `bytes`:
* Использовать `File()` в значении параметра по умолчанию не обязательно.
* При этом используется "буферный" файл:
* Файл, хранящийся в памяти до максимального предела размера, после преодоления которого он будет храниться на диске.
* Это означает, что он будет хорошо работать с большими файлами, такими как изображения, видео, большие бинарные файлы и т.д., не потребляя при этом всю память.
* Из загруженного файла можно получить метаданные.
* Он реализует [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` интерфейс.
* Он предоставляет реальный объект Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile), который вы можете передать непосредственно другим библиотекам, которые ожидают файл в качестве объекта.
### `UploadFile` { #uploadfile }
`UploadFile` имеет следующие атрибуты:
* `filename`: Строка `str` с исходным именем файла, который был загружен (например, `myimage.jpg`).
* `content_type`: Строка `str` с типом содержимого (MIME type / media type) (например, `image/jpeg`).
* `file`: [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (a [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) объект). Это фактический файл Python, который можно передавать непосредственно другим функциям или библиотекам, ожидающим файл в качестве объекта.
`UploadFile` имеет следующие методы `async`. Все они вызывают соответствующие файловые методы (используя внутренний `SpooledTemporaryFile`).
* `write(data)`: Записать данные `data` (`str` или `bytes`) в файл.
* `read(size)`: Прочитать количество `size` (`int`) байт/символов из файла.
* `seek(offset)`: Перейти к байту на позиции `offset` (`int`) в файле.
* Например, `await myfile.seek(0)` перейдет к началу файла.
* Это особенно удобно, если вы один раз выполнили команду `await myfile.read()`, а затем вам нужно прочитать содержимое файла еще раз.
* `close()`: Закрыть файл.
Поскольку все эти методы являются `async` методами, вам следует использовать "await" вместе с ними.
Например, внутри `async` *функции операции пути* можно получить содержимое с помощью:
```Python
contents = await myfile.read()
```
Если вы находитесь внутри обычной `def` *функции операции пути*, можно получить прямой доступ к файлу `UploadFile.file`, например:
```Python
contents = myfile.file.read()
```
/// note | Технические детали `async`
При использовании методов `async` **FastAPI** запускает файловые методы в пуле потоков и ожидает их.
///
/// note | Технические детали Starlette
**FastAPI** наследует `UploadFile` непосредственно из **Starlette**, но добавляет некоторые детали для совместимости с **Pydantic** и другими частями FastAPI.
///
## Что такое «данные формы» { #what-is-form-data }
Способ, которым HTML-формы (``) отправляют данные на сервер, обычно использует "специальную" кодировку для этих данных, отличную от JSON.
**FastAPI** позаботится о том, чтобы считать эти данные из нужного места, а не из JSON.
/// note | Технические детали
Данные из форм обычно кодируются с использованием "media type" `application/x-www-form-urlencoded` когда он не включает файлы.
Но когда форма включает файлы, она кодируется как `multipart/form-data`. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела.
Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке [MDN web docs for `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Внимание
В операции *функции операции пути* можно объявить несколько параметров `File` и `Form`, но нельзя также объявлять поля `Body`, которые предполагается получить в виде JSON, поскольку тело запроса будет закодировано с помощью `multipart/form-data`, а не `application/json`.
Это не является ограничением **FastAPI**, это часть протокола HTTP.
///
## Необязательная загрузка файлов { #optional-file-upload }
Вы можете сделать загрузку файла необязательной, используя стандартные аннотации типов и установив значение по умолчанию `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` с дополнительными метаданными { #uploadfile-with-additional-metadata }
Вы также можете использовать `File()` вместе с `UploadFile`, например, для установки дополнительных метаданных:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Загрузка нескольких файлов { #multiple-file-uploads }
Можно одновременно загружать несколько файлов.
Они будут связаны с одним и тем же "полем формы", отправляемым с помощью данных формы.
Для этого необходимо объявить список `bytes` или `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Вы получите, как и было объявлено, список `list` из `bytes` или `UploadFile`.
/// note | Технические детали
Можно также использовать `from starlette.responses import HTMLResponse`.
**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette.
///
### Загрузка нескольких файлов с дополнительными метаданными { #multiple-file-uploads-with-additional-metadata }
Так же, как и раньше, вы можете использовать `File()` для задания дополнительных параметров, даже для `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Резюме { #recap }
Используйте `File`, `bytes` и `UploadFile` для работы с файлами, которые будут загружаться и передаваться в виде данных формы.
================================================
FILE: docs/ru/docs/tutorial/request-form-models.md
================================================
# Модели форм { #form-models }
Вы можете использовать **Pydantic-модели** для объявления **полей формы** в FastAPI.
/// info | Дополнительная информация
Чтобы использовать формы, сначала установите [`python-multipart`](https://github.com/Kludex/python-multipart).
Убедитесь, что вы создали и активировали [виртуальное окружение](../virtual-environments.md), а затем установите пакет, например:
```console
$ pip install python-multipart
```
///
/// note | Заметка
Этот функционал доступен начиная с версии FastAPI `0.113.0`. 🤓
///
## Pydantic-модели для форм { #pydantic-models-for-forms }
Вам просто нужно объявить **Pydantic-модель** с полями, которые вы хотите получить как **поля формы**, а затем объявить параметр как `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** **извлечёт** данные для **каждого поля** из **данных формы** в запросе и выдаст вам объявленную Pydantic-модель.
## Проверьте документацию { #check-the-docs }
Вы можете проверить это в интерфейсе документации по адресу `/docs`:
## Запрет дополнительных полей формы { #forbid-extra-form-fields }
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** поля формы только теми, которые объявлены в Pydantic-модели. И **запретить** любые **дополнительные** поля.
/// note | Заметка
Этот функционал доступен начиная с версии FastAPI `0.114.0`. 🤓
///
Вы можете сконфигурировать Pydantic-модель так, чтобы запретить (`forbid`) все дополнительные (`extra`) поля:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Если клиент попробует отправить дополнительные данные, то в ответ он получит **ошибку**.
Например, если клиент попытается отправить поля формы:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
То в ответ он получит **ошибку**, сообщающую ему, что поле `extra` не разрешено:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Итоги { #summary }
Вы можете использовать Pydantic-модели для объявления полей форм в FastAPI. 😎
================================================
FILE: docs/ru/docs/tutorial/request-forms-and-files.md
================================================
# Файлы и формы в запросе { #request-forms-and-files }
Вы можете определять файлы и поля формы одновременно, используя `File` и `Form`.
/// info | Информация
Чтобы получать загруженные файлы и/или данные форм, сначала установите [`python-multipart`](https://github.com/Kludex/python-multipart).
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет, например:
```console
$ pip install python-multipart
```
///
## Импортируйте `File` и `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Определите параметры `File` и `Form` { #define-file-and-form-parameters }
Создайте параметры файла и формы таким же образом, как для `Body` или `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Файлы и поля формы будут загружены в виде данных формы, и вы получите файлы и поля формы.
Вы можете объявить некоторые файлы как `bytes`, а некоторые — как `UploadFile`.
/// warning | Внимание
Вы можете объявить несколько параметров `File` и `Form` в операции пути, но вы не можете также объявить поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с помощью `multipart/form-data` вместо `application/json`.
Это не ограничение **FastAPI**, это часть протокола HTTP.
///
## Резюме { #recap }
Используйте `File` и `Form` вместе, когда необходимо получить данные и файлы в одном запросе.
================================================
FILE: docs/ru/docs/tutorial/request-forms.md
================================================
# Данные формы { #form-data }
Когда вам нужно получить поля формы вместо JSON, вы можете использовать `Form`.
/// info | Дополнительная информация
Чтобы использовать формы, сначала установите [`python-multipart`](https://github.com/Kludex/python-multipart).
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет, например:
```console
$ pip install python-multipart
```
///
## Импорт `Form` { #import-form }
Импортируйте `Form` из `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Определение параметров `Form` { #define-form-parameters }
Создайте параметры формы так же, как это делается для `Body` или `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Например, в одном из способов использования спецификации OAuth2 (называемом «password flow» - аутентификация по паролю) требуется отправить `username` и `password` в виде полей формы.
спецификация требует, чтобы поля были строго названы `username` и `password` и отправлялись как поля формы, а не JSON.
С помощью `Form` вы можете объявить те же настройки, что и с `Body` (и `Query`, `Path`, `Cookie`), включая валидацию, примеры, псевдоним (например, `user-name` вместо `username`) и т.д.
/// info | Дополнительная информация
`Form` — это класс, который наследуется непосредственно от `Body`.
///
/// tip | Подсказка
Чтобы объявлять данные формы, вам нужно явно использовать `Form`, иначе параметры будут интерпретированы как параметры запроса или параметры тела (JSON).
///
## О «полях формы» { #about-form-fields }
Обычно способ, которым HTML-формы (``) отправляют данные на сервер, использует «специальное» кодирование для этих данных, отличное от JSON.
**FastAPI** гарантирует, что эти данные будут прочитаны из нужного места, а не из JSON.
/// note | Технические детали
Данные из форм обычно кодируются с использованием «типа содержимого» `application/x-www-form-urlencoded`.
Но когда форма содержит файлы, она кодируется как `multipart/form-data`. О работе с файлами вы прочтёте в следующей главе.
Если вы хотите узнать больше про эти кодировки и поля формы, обратитесь к [MDN веб-документации для `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Предупреждение
Вы можете объявлять несколько параметров `Form` в *операции пути*, но вы не можете одновременно объявлять поля `Body`, которые вы ожидаете получить в виде JSON, так как запрос будет иметь тело, закодированное с использованием `application/x-www-form-urlencoded`, а не `application/json`.
Это не ограничение **FastAPI**, это часть протокола HTTP.
///
## Резюме { #recap }
Используйте `Form` для объявления входных параметров данных формы.
================================================
FILE: docs/ru/docs/tutorial/response-model.md
================================================
# Модель ответа — Возвращаемый тип { #response-model-return-type }
Вы можете объявить тип, используемый для ответа, указав аннотацию **возвращаемого значения** для *функции-обработчика пути*.
Вы можете использовать **аннотации типов** так же, как и для входных данных в **параметрах** функции: Pydantic-модели, списки, словари, скалярные значения (целые числа, булевы и т.д.).
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI будет использовать этот возвращаемый тип, чтобы:
* **Валидировать** возвращаемые данные.
* Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру данных.
* Добавить **JSON Schema** для ответа в OpenAPI *операции пути*.
* Это будет использовано **автоматической документацией**.
* Это также будет использовано инструментами автоматической генерации клиентского кода.
* **Сериализовать** возвращаемые данные в JSON с помощью Pydantic, который написан на **Rust**, поэтому это будет **намного быстрее**.
Но самое главное:
* Выходные данные будут **ограничены и отфильтрованы** в соответствии с тем, что определено в возвращаемом типе.
* Это особенно важно для **безопасности**, ниже мы рассмотрим это подробнее.
## Параметр `response_model` { #response-model-parameter }
Бывают случаи, когда вам нужно или хочется возвращать данные, которые не в точности соответствуют объявленному типу.
Например, вы можете хотеть **возвращать словарь** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных).
Если вы добавите аннотацию возвращаемого типа, инструменты и редакторы кода начнут жаловаться (и будут правы), что функция возвращает тип (например, dict), отличный от объявленного (например, Pydantic-модель).
В таких случаях вместо аннотации возвращаемого типа можно использовать параметр `response_model` у *декоратора операции пути*.
Параметр `response_model` можно указать у любой *операции пути*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* и т.д.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Примечание
Обратите внимание, что `response_model` — это параметр метода «декоратора» (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в которой указываются параметры и тело запроса.
///
`response_model` принимает тот же тип, что вы бы объявили для поля Pydantic-модели, то есть это может быть одна Pydantic-модель, а может быть, например, `list` Pydantic-моделей, как `List[Item]`.
FastAPI будет использовать этот `response_model` для документирования, валидации данных и т.п., а также для **конвертации и фильтрации выходных данных** к объявленному типу.
/// tip | Совет
Если у вас в редакторе кода, mypy и т.п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`.
Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документирование, валидацию, фильтрацию данных и т.д. с помощью `response_model`.
///
### Приоритет `response_model` { #response-model-priority }
Если вы объявите и возвращаемый тип, и `response_model`, приоритет будет у `response_model`, именно его использует FastAPI.
Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор кода и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`.
Вы также можете указать `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для вещей, не являющихся валидными полями Pydantic. Пример вы увидите ниже.
## Вернуть те же входные данные { #return-the-same-input-data }
Здесь мы объявляем модель `UserIn`, она будет содержать пароль в открытом виде:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Информация
Чтобы использовать `EmailStr`, сначала установите [`email-validator`](https://github.com/JoshData/python-email-validator).
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установите пакет, например:
```console
$ pip install email-validator
```
или так:
```console
$ pip install "pydantic[email]"
```
///
И мы используем эту модель для объявления входных данных, и ту же модель — для объявления выходных данных:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Теперь, когда браузер создаёт пользователя с паролем, API вернёт тот же пароль в ответе.
В этом случае это может быть не проблемой, так как пароль отправляет тот же пользователь.
Но если мы используем ту же модель в другой *операции пути*, мы можем начать отправлять пароли пользователей каждому клиенту.
/// danger | Осторожно
Никогда не храните пароль пользователя в открытом виде и не отправляйте его в ответе подобным образом, если только вы не понимаете всех рисков и точно знаете, что делаете.
///
## Добавить выходную модель { #add-an-output-model }
Вместо этого мы можем создать входную модель с паролем в открытом виде и выходную модель без него:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Здесь, хотя *функция-обработчик пути* возвращает тот же входной объект пользователя, содержащий пароль:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...мы объявили `response_model` как модель `UserOut`, в которой нет пароля:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Таким образом, **FastAPI** позаботится о том, чтобы отфильтровать все данные, не объявленные в выходной модели (используя Pydantic).
### `response_model` или возвращаемый тип { #response-model-or-return-type }
В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор кода и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы.
Поэтому в этом примере мы должны объявить тип ответа в параметре `response_model`.
...но читайте дальше, чтобы узнать, как это обойти.
## Возвращаемый тип и фильтрация данных { #return-type-and-data-filtering }
Продолжим предыдущий пример. Мы хотели **аннотировать функцию одним типом**, но при этом иметь возможность вернуть из функции что-то, что фактически включает **больше данных**.
Мы хотим, чтобы FastAPI продолжал **фильтровать** данные с помощью модели ответа. Так что, даже если функция возвращает больше данных, в ответ будут включены только поля, объявленные в модели ответа.
В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора кода и инструментов, проверяющих возвращаемый тип функции.
Однако в большинстве таких случаев нам нужно лишь **отфильтровать/убрать** некоторые данные, как в этом примере.
И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе кода и инструментах и при этом получить **фильтрацию данных** от FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Так мы получаем поддержку инструментов — редакторов кода и mypy, так как этот код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI.
Как это работает? Давайте разберёмся. 🤓
### Аннотации типов и инструменты { #type-annotations-and-tooling }
Сначала посмотрим, как это увидят редактор кода, mypy и другие инструменты.
`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он будет включать все поля обеих моделей.
Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически возвращаем экземпляр `UserIn`.
Редактор кода, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`.
### Фильтрация данных FastAPI { #fastapi-data-filtering }
Теперь для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе.
FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы в итоге вернуть намного больше данных, чем ожидали.
Таким образом вы получаете лучшее из обоих миров: аннотации типов с **поддержкой инструментов** и **фильтрацию данных**.
## Посмотреть в документации { #see-it-in-the-docs }
В автоматической документации вы увидите, что у входной и выходной моделей есть свои JSON Schema:
И обе модели будут использоваться в интерактивной документации API:
## Другие аннотации возвращаемых типов { #other-return-type-annotations }
Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор кода, mypy и т.д.).
### Возврат Response напрямую { #return-a-response-directly }
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах документации для продвинутых](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`.
И инструменты тоже будут довольны, потому что и `RedirectResponse`, и `JSONResponse` являются подклассами `Response`, так что аннотация типа корректна.
### Аннотировать подкласс Response { #annotate-a-response-subclass }
Вы также можете использовать подкласс `Response` в аннотации типа:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай.
### Некорректные аннотации возвращаемых типов { #invalid-return-type-annotations }
Но когда вы возвращаете произвольный объект, не являющийся валидным типом Pydantic (например, объект базы данных), и аннотируете его таким образом в функции, FastAPI попытается создать модель ответа Pydantic из этой аннотации типа и потерпит неудачу.
То же произойдёт, если у вас будет что-то вроде объединение разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...это не сработает, потому что аннотация типа не является типом Pydantic и это не единственный класс `Response` или его подкласс, а объединение (`union`) из `Response` и `dict`.
### Отключить модель ответа { #disable-response-model }
Продолжая пример выше, вы можете не хотеть использовать стандартные валидацию данных, документирование, фильтрацию и т.п., выполняемые FastAPI.
Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов вроде редакторов кода и инструментов проверки типов (например, mypy).
В этом случае вы можете отключить генерацию модели ответа, установив `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, которые вам нужны, без влияния на ваше приложение FastAPI. 🤓
## Параметры кодирования модели ответа { #response-model-encoding-parameters }
У вашей модели ответа могут быть значения по умолчанию, например:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (или `str | None = None` в Python 3.10) имеет значение по умолчанию `None`.
* `tax: float = 10.5` имеет значение по умолчанию `10.5`.
* `tags: List[str] = []` имеет значение по умолчанию пустого списка: `[]`.
но вы можете захотеть опустить их в результате, если они фактически не были сохранены.
Например, если у вас есть модели с множеством необязательных атрибутов в NoSQL-базе данных, но вы не хотите отправлять очень длинные JSON-ответы, заполненные значениями по умолчанию.
### Используйте параметр `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Вы можете установить у *декоратора операции пути* параметр `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
и эти значения по умолчанию не будут включены в ответ — только те значения, которые действительно были установлены.
Итак, если вы отправите запрос к этой *операции пути* для элемента с ID `foo`, ответ (без значений по умолчанию) будет таким:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Информация
Вы также можете использовать:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
как описано в [документации Pydantic](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) для `exclude_defaults` и `exclude_none`.
///
#### Данные со значениями для полей, имеющих значения по умолчанию { #data-with-values-for-fields-with-defaults }
Но если в ваших данных есть значения для полей модели, для которых указаны значения по умолчанию, как у элемента с ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
они будут включены в ответ.
#### Данные с такими же значениями, как значения по умолчанию { #data-with-the-same-values-as-the-defaults }
Если данные имеют те же значения, что и значения по умолчанию, как у элемента с ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI достаточно умен (на самом деле, это Pydantic), чтобы понять, что хотя `description`, `tax` и `tags` совпадают со значениями по умолчанию, они были установлены явно (а не взяты из значений по умолчанию).
Поэтому они тоже будут включены в JSON-ответ.
/// tip | Совет
Обратите внимание, что значения по умолчанию могут быть любыми, не только `None`.
Это может быть список (`[]`), число с плавающей точкой `10.5` и т.д.
///
### `response_model_include` и `response_model_exclude` { #response-model-include-and-response-model-exclude }
Вы также можете использовать параметры *декоратора операции пути* `response_model_include` и `response_model_exclude`.
Они принимают `set` из `str` с именами атрибутов, которые нужно включить (исключив остальные) или исключить (оставив остальные).
Это можно использовать как быстрый способ, если у вас только одна Pydantic-модель и вы хотите убрать часть данных из ответа.
/// tip | Совет
Но всё же рекомендуется использовать подходы выше — несколько классов — вместо этих параметров.
Потому что JSON Schema, генерируемая в OpenAPI вашего приложения (и документации), всё равно будет соответствовать полной модели, даже если вы используете `response_model_include` или `response_model_exclude`, чтобы опустить некоторые атрибуты.
То же относится к `response_model_by_alias`, который работает аналогично.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Совет
Синтаксис `{"name", "description"}` создаёт `set` с этими двумя значениями.
Это эквивалентно `set(["name", "description"])`.
///
#### Использование `list` вместо `set` { #using-lists-instead-of-sets }
Если вы забыли использовать `set` и применили `list` или `tuple` вместо него, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Резюме { #recap }
Используйте параметр `response_model` у *декоратора операции пути*, чтобы задавать модели ответа, и особенно — чтобы приватные данные отфильтровывались.
Используйте `response_model_exclude_unset`, чтобы возвращать только те значения, которые были установлены явно.
================================================
FILE: docs/ru/docs/tutorial/response-status-code.md
================================================
# Статус-код ответа { #response-status-code }
Подобно тому, как вы можете задать модель/схему ответа, вы можете объявить HTTP статус-код, используемый для ответа, с помощью параметра `status_code` в любой из *операций пути*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* и других.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Примечание
Обратите внимание, что `status_code` — это параметр метода-декоратора (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в отличие от всех остальных параметров и тела запроса.
///
Параметр `status_code` принимает число, обозначающее HTTP статус-код.
/// info | Информация
В качестве значения параметра `status_code` также может использоваться `IntEnum`, например, из библиотеки [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus) в Python.
///
Это позволит:
* Возвращать указанный код статуса в ответе.
* Документировать его как код статуса ответа в OpenAPI схеме (а значит, и в пользовательских интерфейсах):
/// note | Примечание
Некоторые коды статуса ответа (см. следующий раздел) указывают на то, что ответ не имеет тела.
FastAPI знает об этом и создаст документацию OpenAPI, в которой будет указано, что тело ответа отсутствует.
///
## Об HTTP статус-кодах { #about-http-status-codes }
/// note | Примечание
Если вы уже знаете, что представляют собой HTTP статус-коды, можете перейти к следующему разделу.
///
В протоколе HTTP числовой код состояния из 3 цифр отправляется как часть ответа.
У кодов статуса есть названия, чтобы упростить их распознавание, но важны именно числовые значения.
Кратко:
* `100 - 199` – статус-коды информационного типа. Они редко используются разработчиками напрямую. Ответы с этими кодами не могут иметь тела.
* **`200 - 299`** – статус-коды, сообщающие об успешной обработке запроса. Они используются чаще всего.
* `200` – это код статуса ответа по умолчанию, который означает, что все прошло "OK".
* Другим примером может быть статус `201`, "Created". Он обычно используется после создания новой записи в базе данных.
* Особый случай – `204`, "No Content". Этот статус ответа используется, когда нет содержимого для возврата клиенту, и поэтому ответ не должен иметь тела.
* **`300 - 399`** – статус-коды, сообщающие о перенаправлениях. Ответы с этими кодами статуса могут иметь или не иметь тело, за исключением ответов со статусом `304`, "Not Modified", у которых не должно быть тела.
* **`400 - 499`** – статус-коды, сообщающие о клиентской ошибке. Это ещё одна наиболее часто используемая категория.
* Пример – код `404` для статуса "Not Found".
* Для общих ошибок со стороны клиента можно просто использовать код `400`.
* `500 - 599` – статус-коды, сообщающие о серверной ошибке. Они почти никогда не используются разработчиками напрямую. Когда что-то идет не так в какой-то части кода вашего приложения или на сервере, он автоматически вернёт один из этих кодов статуса.
/// tip | Подсказка
Чтобы узнать больше о HTTP кодах статуса и о том, для чего каждый из них предназначен, ознакомьтесь с [MDN документацией об HTTP статус-кодах](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Краткие обозначения для запоминания названий кодов { #shortcut-to-remember-the-names }
Рассмотрим предыдущий пример еще раз:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` – это код статуса "Создано".
Но вам не обязательно запоминать, что означает каждый из этих кодов.
Для удобства вы можете использовать переменные из `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Они содержат те же числовые значения, но позволяют использовать автозавершение редактора кода для выбора кода статуса:
/// note | Технические детали
Вы также можете использовать `from starlette import status`.
**FastAPI** позволяет использовать как `starlette.status`, так и `fastapi.status` исключительно для удобства разработчиков. Но поставляется fastapi.status непосредственно из Starlette.
///
## Изменение кода статуса по умолчанию { #changing-the-default }
Позже, в [Руководстве для продвинутых пользователей](../advanced/response-change-status-code.md), вы узнаете, как возвращать HTTP статус-код, отличный от значения по умолчанию, которое вы объявляете здесь.
================================================
FILE: docs/ru/docs/tutorial/schema-extra-example.md
================================================
# Объявление примеров данных запроса { #declare-request-example-data }
Вы можете объявлять примеры данных, которые ваше приложение может получать.
Вот несколько способов, как это сделать.
## Дополнительные данные JSON Schema в моделях Pydantic { #extra-json-schema-data-in-pydantic-models }
Вы можете объявить `examples` для модели Pydantic, которые будут добавлены в сгенерированную JSON Schema.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Эта дополнительная информация будет добавлена как есть в выходную **JSON Schema** этой модели и будет использоваться в документации API.
Вы можете использовать атрибут `model_config`, который принимает `dict`, как описано в [Документация Pydantic: Конфигурация](https://docs.pydantic.dev/latest/api/config/).
Вы можете задать `"json_schema_extra"` с `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
/// tip | Подсказка
Вы можете использовать тот же приём, чтобы расширить JSON Schema и добавить свою собственную дополнительную информацию.
Например, вы можете использовать это, чтобы добавить метаданные для фронтенд‑пользовательского интерфейса и т.д.
///
/// info | Информация
OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) добавил поддержку `examples`, который является частью стандарта **JSON Schema**.
До этого поддерживалось только ключевое слово `example` с одним примером. Оно всё ещё поддерживается в OpenAPI 3.1.0, но помечено как устаревшее и не является частью стандарта JSON Schema. Поэтому рекомендуется мигрировать `example` на `examples`. 🤓
Подробнее — в конце этой страницы.
///
## Дополнительные аргументы `Field` { #field-additional-arguments }
При использовании `Field()` с моделями Pydantic вы также можете объявлять дополнительные `examples`:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` в JSON Schema — OpenAPI { #examples-in-json-schema-openapi }
При использовании любой из функций:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
вы также можете объявить набор `examples` с дополнительной информацией, которая будет добавлена в их **JSON Schema** внутри **OpenAPI**.
### `Body` с `examples` { #body-with-examples }
Здесь мы передаём `examples`, содержащий один пример данных, ожидаемых в `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Пример в UI документации { #example-in-the-docs-ui }
С любым из перечисленных выше методов это будет выглядеть так в `/docs`:
### `Body` с несколькими `examples` { #body-with-multiple-examples }
Конечно, вы можете передать и несколько `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Когда вы делаете это, примеры становятся частью внутренней **JSON Schema** для данных тела запроса.
Тем не менее, на момент написания этого Swagger UI, инструмент, отвечающий за отображение UI документации, не поддерживает показ нескольких примеров для данных в **JSON Schema**. Но ниже есть обходной путь.
### Специфические для OpenAPI `examples` { #openapi-specific-examples }
Ещё до того как **JSON Schema** поддержала `examples`, в OpenAPI была поддержка другого поля, также называемого `examples`.
Эти **специфические для OpenAPI** `examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях для каждой операции пути (обработчика пути)**, а не внутри каждого объекта Schema.
И Swagger UI уже какое‑то время поддерживает именно это поле `examples`. Поэтому вы можете использовать его, чтобы **отобразить** разные **примеры в UI документации**.
Структура этого специфичного для OpenAPI поля `examples` — это `dict` с **несколькими примерами** (вместо `list`), каждый с дополнительной информацией, которая также будет добавлена в **OpenAPI**.
Это не помещается внутрь каждого объекта Schema в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
### Использование параметра `openapi_examples` { #using-the-openapi-examples-parameter }
Вы можете объявлять специфические для OpenAPI `examples` в FastAPI с помощью параметра `openapi_examples` для:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
Ключи `dict` идентифицируют каждый пример, а каждое значение — это ещё один `dict`.
Каждый конкретный пример `dict` в `examples` может содержать:
* `summary`: Краткое описание примера.
* `description`: Подробное описание, которое может содержать текст в Markdown.
* `value`: Это фактический пример, который отображается, например, `dict`.
* `externalValue`: альтернатива `value`, URL, указывающий на пример. Хотя это может поддерживаться не так многими инструментами, как `value`.
Использовать это можно так:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### OpenAPI-примеры в UI документации { #openapi-examples-in-the-docs-ui }
С `openapi_examples`, добавленным в `Body()`, страница `/docs` будет выглядеть так:
## Технические детали { #technical-details }
/// tip | Подсказка
Если вы уже используете **FastAPI** версии **0.99.0 или выше**, вы, вероятно, можете **пропустить** эти подробности.
Они более актуальны для старых версий, до того как стала доступна OpenAPI 3.1.0.
Считайте это кратким **уроком истории** про OpenAPI и JSON Schema. 🤓
///
/// warning | Внимание
Далее идут очень технические подробности о стандартах **JSON Schema** и **OpenAPI**.
Если идеи выше уже работают для вас, этого может быть достаточно, и, вероятно, вам не нужны эти детали — смело пропускайте их.
///
До OpenAPI 3.1.0 OpenAPI использовала более старую и модифицированную версию **JSON Schema**.
В JSON Schema не было `examples`, поэтому OpenAPI добавила собственное поле `example` в свою модифицированную версию.
OpenAPI также добавила поля `example` и `examples` в другие части спецификации:
* [`Parameter Object` (в спецификации)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object), которое использовалось в FastAPI:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, в поле `content`, в `Media Type Object` (в спецификации)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object), которое использовалось в FastAPI:
* `Body()`
* `File()`
* `Form()`
/// info | Информация
Этот старый специфичный для OpenAPI параметр `examples` теперь называется `openapi_examples`, начиная с FastAPI `0.103.0`.
///
### Поле `examples` в JSON Schema { #json-schemas-examples-field }
Позже в новой версии спецификации JSON Schema было добавлено поле [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5).
А затем новый OpenAPI 3.1.0 был основан на последней версии (JSON Schema 2020-12), которая включала это новое поле `examples`.
И теперь это новое поле `examples` имеет приоритет над старым одиночным (и кастомным) полем `example`, которое теперь устарело.
Это новое поле `examples` в JSON Schema — это **просто `list`** примеров, а не dict с дополнительными метаданными, как в других местах OpenAPI (описанных выше).
/// info | Информация
Даже после того как OpenAPI 3.1.0 была выпущена с этой новой, более простой интеграцией с JSON Schema, какое‑то время Swagger UI, инструмент, предоставляющий автоматическую документацию, не поддерживал OpenAPI 3.1.0 (поддержка появилась начиная с версии 5.0.0 🎉).
Из‑за этого версии FastAPI до 0.99.0 всё ещё использовали версии OpenAPI ниже 3.1.0.
///
### `examples` в Pydantic и FastAPI { #pydantic-and-fastapi-examples }
Когда вы добавляете `examples` внутри модели Pydantic, используя `schema_extra` или `Field(examples=["something"])`, этот пример добавляется в **JSON Schema** для этой модели Pydantic.
И эта **JSON Schema** модели Pydantic включается в **OpenAPI** вашего API, а затем используется в UI документации.
В версиях FastAPI до 0.99.0 (0.99.0 и выше используют новый OpenAPI 3.1.0), когда вы использовали `example` или `examples` с любыми другими утилитами (`Query()`, `Body()`, и т.д.), эти примеры не добавлялись в JSON Schema, описывающую эти данные (даже в собственную версию JSON Schema OpenAPI), они добавлялись непосредственно в объявление *операции пути* в OpenAPI (вне частей OpenAPI, использующих JSON Schema).
Но теперь, когда FastAPI 0.99.0 и выше используют OpenAPI 3.1.0, который использует JSON Schema 2020-12, а также Swagger UI 5.0.0 и выше, всё стало более последовательным, и примеры включаются в JSON Schema.
### Swagger UI и специфичные для OpenAPI `examples` { #swagger-ui-and-openapi-specific-examples }
Теперь, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации.
Чтобы решить это, FastAPI `0.103.0` **добавил поддержку** объявления того же старого, **специфичного для OpenAPI**, поля `examples` с новым параметром `openapi_examples`. 🤓
### Итог { #summary }
Раньше я говорил, что не очень люблю историю... а теперь вот рассказываю «уроки технической истории». 😅
Коротко: **обновитесь до FastAPI 0.99.0 или выше** — так всё будет значительно **проще, последовательнее и интуитивнее**, и вам не придётся знать все эти исторические подробности. 😎
================================================
FILE: docs/ru/docs/tutorial/security/first-steps.md
================================================
# Безопасность — первые шаги { #security-first-steps }
Представим, что у вас есть **бэкенд** API на некотором домене.
И у вас есть **фронтенд** на другом домене или на другом пути того же домена (или в мобильном приложении).
И вы хотите, чтобы фронтенд мог аутентифицироваться на бэкенде, используя **имя пользователя** и **пароль**.
Мы можем использовать **OAuth2**, чтобы построить это с **FastAPI**.
Но давайте сэкономим вам время на чтение всей длинной спецификации в поисках тех небольших фрагментов информации, которые вам нужны.
Воспользуемся инструментами, предоставленными **FastAPI**, чтобы работать с безопасностью.
## Как это выглядит { #how-it-looks }
Сначала просто воспользуемся кодом и посмотрим, как он работает, а затем вернемся и разберемся, что происходит.
## Создание `main.py` { #create-main-py }
Скопируйте пример в файл `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Запуск { #run-it }
/// info | Дополнительная информация
Пакет [`python-multipart`](https://github.com/Kludex/python-multipart) автоматически устанавливается вместе с **FastAPI**, если вы запускаете команду `pip install "fastapi[standard]"`.
Однако, если вы используете команду `pip install fastapi`, пакет `python-multipart` по умолчанию не включается.
Чтобы установить его вручную, убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md), активировали его и затем установили пакет:
```console
$ pip install python-multipart
```
Это связано с тем, что **OAuth2** использует «данные формы» для отправки `username` и `password`.
///
Запустите пример командой:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Проверка { #check-it }
Перейдите к интерактивной документации по адресу: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите примерно следующее:
/// check | Кнопка авторизации!
У вас уже появилась новая кнопка «Authorize».
А у вашей *операции пути* в правом верхнем углу есть маленький замочек, на который можно нажать.
///
Если нажать на нее, появится небольшая форма авторизации, в которую нужно ввести `username` и `password` (и другие необязательные поля):
/// note | Примечание
Неважно, что вы введете в форму — пока это не будет работать. Но мы скоро до этого дойдем.
///
Конечно, это не фронтенд для конечных пользователей, но это отличный автоматический инструмент для интерактивного документирования всего вашего API.
Им может пользоваться команда фронтенда (которой можете быть и вы сами).
Им могут пользоваться сторонние приложения и системы.
И им также можете пользоваться вы сами, чтобы отлаживать, проверять и тестировать то же самое приложение.
## «`password` flow» (аутентификация по паролю) { #the-password-flow }
Теперь давайте немного вернемся и разберемся, что это все такое.
«`password` flow» — это один из способов («flows»), определенных в OAuth2, для обеспечения безопасности и аутентификации.
OAuth2 был спроектирован так, чтобы бэкенд или API были независимы от сервера, который аутентифицирует пользователя.
Но в нашем случае одно и то же приложение **FastAPI** будет работать и с API, и с аутентификацией.
Итак, рассмотрим это с упрощенной точки зрения:
* Пользователь вводит на фронтенде `username` и `password` и нажимает `Enter`.
* Фронтенд (работающий в браузере пользователя) отправляет эти `username` и `password` на конкретный URL в нашем API (объявленный с `tokenUrl="token"`).
* API проверяет этот `username` и `password` и отвечает «токеном» (мы еще ничего из этого не реализовали).
* «Токен» — это просто строка с некоторым содержимым, которое мы сможем позже использовать для проверки этого пользователя.
* Обычно у токена установлен срок действия: он истекает через некоторое время.
* Поэтому пользователю придется снова войти в систему в какой‑то момент.
* И если токен украдут, риск меньше: это не постоянный ключ, который будет работать вечно (в большинстве случаев).
* Фронтенд временно где‑то хранит этот токен.
* Пользователь кликает во фронтенде, чтобы перейти в другой раздел веб‑приложения.
* Фронтенду нужно получить дополнительные данные из API.
* Но для этого для конкретной конечной точки нужна аутентификация.
* Поэтому, чтобы аутентифицироваться в нашем API, он отправляет HTTP-заголовок `Authorization` со значением `Bearer ` плюс сам токен.
* Если токен содержит `foobar`, то содержимое заголовка `Authorization` будет: `Bearer foobar`.
## Класс `OAuth2PasswordBearer` в **FastAPI** { #fastapis-oauth2passwordbearer }
**FastAPI** предоставляет несколько средств на разных уровнях абстракции для реализации этих функций безопасности.
В этом примере мы будем использовать **OAuth2**, с потоком **Password**, используя токен **Bearer**. Для этого мы используем класс `OAuth2PasswordBearer`.
/// info | Дополнительная информация
Токен «bearer» — не единственный вариант.
Но для нашего случая он — лучший.
И он может быть лучшим для большинства случаев использования, если только вы не являетесь экспертом по OAuth2 и точно знаете, почему другой вариант лучше подходит под ваши нужды.
В этом случае **FastAPI** также предоставляет инструменты, чтобы его реализовать.
///
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `username` и `password`, чтобы получить токен.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Подсказка
Здесь `tokenUrl="token"` ссылается на относительный URL `token`, который мы еще не создали. Поскольку это относительный URL, он эквивалентен `./token`.
Поскольку мы используем относительный URL, если ваш API расположен по адресу `https://example.com/`, то он будет ссылаться на `https://example.com/token`. А если ваш API расположен по адресу `https://example.com/api/v1/`, то он будет ссылаться на `https://example.com/api/v1/token`.
Использование относительного URL важно для того, чтобы ваше приложение продолжало работать даже в таком продвинутом случае, как [За прокси-сервером](../../advanced/behind-a-proxy.md).
///
Этот параметр не создает конечную точку / *операцию пути*, а объявляет, что URL `/token` — это тот, который клиент должен использовать для получения токена. Эта информация используется в OpenAPI, а затем в интерактивных системах документации по API.
Скоро мы также создадим и саму операцию пути.
/// info | Дополнительная информация
Если вы очень строгий «питонист», вам может не понравиться стиль имени параметра `tokenUrl` вместо `token_url`.
Это потому, что используется то же имя, что и в спецификации OpenAPI. Так, если вам нужно разобраться подробнее в какой‑то из этих схем безопасности, вы можете просто скопировать и вставить это имя, чтобы найти больше информации.
///
Переменная `oauth2_scheme` — это экземпляр `OAuth2PasswordBearer`, но она также «вызываемая».
Ее можно вызвать так:
```Python
oauth2_scheme(some, parameters)
```
Поэтому ее можно использовать вместе с `Depends`.
### Использование { #use-it }
Теперь вы можете передать `oauth2_scheme` как зависимость с `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Эта зависимость предоставит `str`, который будет присвоен параметру `token` *функции-обработчика пути*.
**FastAPI** будет знать, что может использовать эту зависимость для определения «схемы безопасности» в схеме OpenAPI (и в автоматической документации по API).
/// info | Технические детали
**FastAPI** будет знать, что может использовать класс `OAuth2PasswordBearer` (объявленный в зависимости) для определения схемы безопасности в OpenAPI, потому что он наследуется от `fastapi.security.oauth2.OAuth2`, который, в свою очередь, наследуется от `fastapi.security.base.SecurityBase`.
Все утилиты безопасности, интегрируемые с OpenAPI (и автоматической документацией по API), наследуются от `SecurityBase`, — так **FastAPI** понимает, как интегрировать их в OpenAPI.
///
## Что он делает { #what-it-does }
Он будет искать в запросе заголовок `Authorization`, проверять, что его значение — это `Bearer ` плюс некоторый токен, и вернет токен как `str`.
Если заголовок `Authorization` отсутствует или его значение не содержит токен `Bearer `, он сразу ответит ошибкой со статус-кодом 401 (`UNAUTHORIZED`).
Вам даже не нужно проверять наличие токена, чтобы вернуть ошибку. Вы можете быть уверены: если ваша функция была выполнена, в этом токене будет `str`.
Это уже можно попробовать в интерактивной документации:
Мы пока не проверяем валидность токена, но для начала это уже неплохо.
## Резюме { #recap }
Таким образом, всего за 3–4 дополнительные строки у вас уже есть некая примитивная форма защиты.
================================================
FILE: docs/ru/docs/tutorial/security/get-current-user.md
================================================
# Получить текущего пользователя { #get-current-user }
В предыдущей главе система безопасности (основанная на системе внедрения зависимостей) передавала *функции-обработчику пути* `token` типа `str`:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Но это всё ещё не слишком полезно.
Сделаем так, чтобы она возвращала текущего пользователя.
## Создать модель пользователя { #create-a-user-model }
Сначала создадим Pydantic-модель пользователя.
Точно так же, как мы используем Pydantic для объявления тел запросов, мы можем использовать его где угодно:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Создать зависимость `get_current_user` { #create-a-get-current-user-dependency }
Давайте создадим зависимость `get_current_user`.
Помните, что у зависимостей могут быть подзависимости?
`get_current_user` будет иметь зависимость от того же `oauth2_scheme`, который мы создали ранее.
Аналогично тому, как мы делали ранее прямо в *операции пути*, новая зависимость `get_current_user` получит `token` типа `str` от подзависимости `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Получить пользователя { #get-the-user }
`get_current_user` будет использовать созданную нами (ненастоящую) служебную функцию, которая принимает токен типа `str` и возвращает нашу Pydantic-модель `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Внедрить текущего пользователя { #inject-the-current-user }
Теперь мы можем использовать тот же `Depends` с нашей `get_current_user` в *операции пути*:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Обратите внимание, что мы объявляем тип `current_user` как Pydantic-модель `User`.
Это поможет внутри функции с автозавершением и проверками типов.
/// tip | Подсказка
Возможно, вы помните, что тела запросов также объявляются с помощью Pydantic-моделей.
Здесь **FastAPI** не запутается, потому что вы используете `Depends`.
///
/// check | Заметка
То, как устроена эта система зависимостей, позволяет иметь разные зависимости, которые возвращают модель `User`.
Мы не ограничены наличием только одной зависимости, которая может возвращать такой тип данных.
///
## Другие модели { #other-models }
Теперь вы можете получать текущего пользователя напрямую в *функциях-обработчиках пути* и работать с механизмами безопасности на уровне **внедрения зависимостей**, используя `Depends`.
И вы можете использовать любую модель или данные для требований безопасности (в данном случае Pydantic-модель `User`).
Но вы не ограничены использованием какой-то конкретной модели данных, класса или типа.
Хотите, чтобы в модели были `id` и `email`, но не было `username`? Пожалуйста. Можно использовать те же инструменты.
Хотите просто `str`? Или просто `dict`? Или напрямую экземпляр класса модели базы данных? Всё работает одинаково.
У вас вообще нет пользователей, которые входят в приложение, а есть роботы, боты или другие системы, у которых есть только токен доступа? Снова — всё работает так же.
Просто используйте любую модель, любой класс, любую базу данных, которые нужны вашему приложению. Система внедрения зависимостей **FastAPI** поможет вам в этом.
## Размер кода { #code-size }
Этот пример может показаться многословным. Имейте в виду, что в одном файле мы смешиваем безопасность, модели данных, служебные функции и *операции пути*.
Но вот ключевой момент.
Всё, что касается безопасности и внедрения зависимостей, пишется один раз.
И вы можете сделать это настолько сложным, насколько захотите. И всё равно это будет написано только один раз, в одном месте. Со всей гибкостью.
При этом у вас могут быть тысячи эндпоинтов (*операций пути*), использующих одну и ту же систему безопасности.
И все они (или любая их часть по вашему желанию) могут воспользоваться преимуществами повторного использования этих зависимостей или любых других зависимостей, которые вы создадите.
И все эти тысячи *операций пути* могут состоять всего из 3 строк:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Резюме { #recap }
Теперь вы можете получать текущего пользователя прямо в своей *функции-обработчике пути*.
Мы уже на полпути.
Нужно лишь добавить *операцию пути*, чтобы пользователь/клиент мог отправить `username` и `password`.
Это будет дальше.
================================================
FILE: docs/ru/docs/tutorial/security/index.md
================================================
# Безопасность { #security }
Существует множество способов обеспечения безопасности, аутентификации и авторизации.
Обычно эта тема является достаточно сложной и «трудной».
Во многих фреймворках и системах только работа с безопасностью и аутентификацией требует значительных затрат усилий и написания множества кода (во многих случаях его объём может составлять 50% или более от всего написанного кода).
**FastAPI** предоставляет несколько инструментов, которые помогут вам работать с **безопасностью** легко, быстро, стандартным способом, без необходимости изучать и разбираться во всех спецификациях по безопасности.
Но сначала давайте рассмотрим несколько небольших концепций.
## Нет времени? { #in-a-hurry }
Если вам не важны какие-либо из этих терминов и вам просто нужно добавить защиту с аутентификацией на основе имени пользователя и пароля прямо сейчас, переходите к следующим главам.
## OAuth2 { #oauth2 }
OAuth2 - это спецификация, которая определяет несколько способов обработки аутентификации и авторизации.
Это довольно обширная спецификация, охватывающая несколько сложных вариантов использования.
Она включает способы аутентификации с использованием «третьей стороны».
Именно это используется во всех системах с кнопками «войти с помощью Facebook, Google, X (Twitter), GitHub».
### OAuth 1 { #oauth-1 }
Ранее использовался OAuth 1, который сильно отличается от OAuth2 и является более сложным, поскольку он включал прямые спецификации того, как шифровать обмен данными.
В настоящее время он не очень популярен и не используется.
OAuth2 не указывает, как шифровать обмен данными, он ожидает, что ваше приложение будет обслуживаться по протоколу HTTPS.
/// tip | Подсказка
В разделе о **развертывании** вы увидите, как настроить HTTPS бесплатно, используя Traefik и Let's Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect — это ещё одна спецификация, основанная на **OAuth2**.
Она просто расширяет OAuth2, уточняя некоторые вещи, которые относительно неоднозначны в OAuth2, стараясь сделать его более совместимым.
Например, для входа в Google используется OpenID Connect (который под капотом использует OAuth2).
Но вход в Facebook не поддерживает OpenID Connect. У него есть собственная вариация OAuth2.
### OpenID (не «OpenID Connect») { #openid-not-openid-connect }
Также ранее использовалась спецификация «OpenID», которая пыталась решить ту же задачу, что и **OpenID Connect**, но не была основана на OAuth2.
Таким образом, это была полноценная дополнительная система.
В настоящее время не очень популярен и не используется.
## OpenAPI { #openapi }
OpenAPI (ранее известный как Swagger) - это открытая спецификация для создания API (в настоящее время является частью Linux Foundation).
**FastAPI** основан на **OpenAPI**.
Это то, что делает возможными несколько автоматических интерактивных интерфейсов документации, генерацию кода и т.д.
В OpenAPI есть способ определить несколько «схем» безопасности.
Используя их, вы можете воспользоваться преимуществами всех этих инструментов, основанных на стандартах, включая интерактивные системы документирования.
OpenAPI определяет следующие схемы безопасности:
* `apiKey`: специфичный для приложения ключ, который может поступать из:
* параметра запроса.
* HTTP-заголовка.
* cookie.
* `http`: стандартные системы аутентификации по HTTP, включая:
* `bearer`: HTTP-заголовок `Authorization` со значением `Bearer ` плюс токен. Это унаследовано от OAuth2.
* Базовая аутентификация по HTTP.
* HTTP Digest и т.д.
* `oauth2`: все способы OAuth2 для обеспечения безопасности (называются «потоками»).
* Несколько из этих «потоков» подходят для построения провайдера аутентификации OAuth 2.0 (например, Google, Facebook, X (Twitter), GitHub и т.д.):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Но есть один конкретный «поток», который можно идеально использовать для обработки аутентификации непосредственно в этом же приложении:
* `password`: в некоторых следующих главах будут приведены примеры.
* `openIdConnect`: имеет способ определить, как автоматически обнаруживать данные аутентификации OAuth2.
* Именно это автоматическое обнаружение определено в спецификации OpenID Connect.
/// tip | Подсказка
Интеграция сторонних провайдеров аутентификации/авторизации, таких как Google, Facebook, X (Twitter), GitHub и т.д., также возможна и относительно проста.
Самой сложной задачей является создание такого провайдера аутентификации/авторизации, но **FastAPI** предоставляет вам инструменты, позволяющие легко это сделать, выполняя при этом всю тяжёлую работу за вас.
///
## Инструменты **FastAPI** { #fastapi-utilities }
FastAPI предоставляет несколько инструментов для каждой из этих схем безопасности в модуле `fastapi.security`, которые упрощают использование этих механизмов безопасности.
В следующих главах вы увидите, как добавить безопасность в ваш API, используя инструменты, предоставляемые **FastAPI**.
И вы также увидите, как это автоматически интегрируется в систему интерактивной документации.
================================================
FILE: docs/ru/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 с паролем (и хешированием), Bearer с JWT-токенами { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Теперь, когда у нас определен процесс обеспечения безопасности, давайте сделаем приложение действительно безопасным, используя токены JWT и безопасное хеширование паролей.
Этот код можно реально использовать в своем приложении, сохранять хэши паролей в базе данных и т.д.
Мы продолжим разбираться, начиная с того места, на котором остановились в предыдущей главе, и расширим его.
## Про JWT { #about-jwt }
JWT означает "JSON Web Tokens".
Это стандарт для кодирования JSON-объекта в виде длинной строки без пробелов. Выглядит это следующим образом:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Он не зашифрован, поэтому любой человек может восстановить информацию из его содержимого.
Но он подписан. Следовательно, когда вы получаете токен, который вы эмитировали (выдавали), вы можете убедиться, что это именно вы его эмитировали.
Таким образом, можно создать токен со сроком действия, скажем, 1 неделя. А когда пользователь вернется на следующий день с тем же токеном, вы будете знать, что он все еще авторизован в вашей системе.
Через неделю срок действия токена истечет, пользователь не будет авторизован и ему придется заново входить в систему, чтобы получить новый токен. А если пользователь (или третье лицо) попытается модифицировать токен, чтобы изменить срок действия, вы сможете это обнаружить, поскольку подписи не будут совпадать.
Если вы хотите поиграть с JWT-токенами и посмотреть, как они работают, посмотрите [https://jwt.io](https://jwt.io/).
## Установка `PyJWT` { #install-pyjwt }
Нам необходимо установить `pyjwt` для генерации и проверки JWT-токенов на языке Python.
Убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md), активируйте его, а затем установите `pyjwt`:
```console
$ pip install pyjwt
---> 100%
```
/// info | Дополнительная информация
Если вы планируете использовать алгоритмы цифровой подписи, такие как RSA или ECDSA, вам следует установить зависимость библиотеки криптографии `pyjwt[crypto]`.
Подробнее об этом можно прочитать в [документации по установке PyJWT](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Хеширование паролей { #password-hashing }
"Хеширование" означает преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
Каждый раз, когда вы передаете точно такое же содержимое (точно такой же пароль), вы получаете точно такую же тарабарщину.
Но преобразовать тарабарщину обратно в пароль невозможно.
### Для чего нужно хеширование паролей { #why-use-password-hashing }
Если ваша база данных будет украдена, то вор не получит пароли пользователей в открытом виде, а только их хэши.
Таким образом, вор не сможет использовать этот пароль в другой системе (поскольку многие пользователи везде используют один и тот же пароль, это было бы опасно).
## Установка `pwdlib` { #install-pwdlib }
pwdlib — это отличный пакет Python для работы с хэшами паролей.
Он поддерживает множество безопасных алгоритмов хеширования и утилит для работы с ними.
Рекомендуемый алгоритм — "Argon2".
Убедитесь, что вы создали [виртуальное окружение](../../virtual-environments.md), активируйте его, и затем установите pwdlib вместе с Argon2:
/// tip | Подсказка
С помощью `pwdlib` можно даже настроить его на чтение паролей, созданных **Django**, плагином безопасности **Flask** или многими другими библиотеками.
Таким образом, вы сможете, например, совместно использовать одни и те же данные из приложения Django в базе данных с приложением FastAPI. Или постепенно мигрировать Django-приложение, используя ту же базу данных.
При этом пользователи смогут одновременно входить в систему как из приложения Django, так и из приложения **FastAPI**.
///
## Хеширование и проверка паролей { #hash-and-verify-the-passwords }
Импортируйте необходимые инструменты из `pwdlib`.
Создайте экземпляр PasswordHash с рекомендованными настройками — он будет использоваться для хэширования и проверки паролей.
/// tip | Подсказка
pwdlib также поддерживает алгоритм хеширования bcrypt, но не включает устаревшие алгоритмы — для работы с устаревшими хэшами рекомендуется использовать библиотеку passlib.
Например, вы можете использовать ее для чтения и проверки паролей, сгенерированных другой системой (например, Django), но хэшировать все новые пароли другим алгоритмом, например Argon2 или Bcrypt.
И при этом быть совместимым со всеми этими системами.
///
Создайте служебную функцию для хэширования пароля, поступающего от пользователя.
А затем создайте другую — для проверки соответствия полученного пароля и хранимого хэша.
И еще одну — для аутентификации и возврата пользователя.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Когда `authenticate_user` вызывается с именем пользователя, которого нет в базе данных, мы все равно запускаем `verify_password` с использованием фиктивного хэша.
Это гарантирует, что эндпоинт отвечает примерно за одно и то же время вне зависимости от того, существует имя пользователя или нет, предотвращая тайминговые атаки (атака по времени), с помощью которых можно было бы перечислять существующие имена пользователей.
/// note | Технические детали
Если проверить новую (фальшивую) базу данных `fake_users_db`, то можно увидеть, как теперь выглядит хэшированный пароль: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Работа с JWT токенами { #handle-jwt-tokens }
Импортируйте установленные модули.
Создайте случайный секретный ключ, который будет использоваться для подписи JWT-токенов.
Для генерации безопасного случайного секретного ключа используйте команду:
И скопируйте полученный результат в переменную `SECRET_KEY` (не используйте тот, что в примере).
Создайте переменную `ALGORITHM` с алгоритмом, используемым для подписи JWT-токена, и установите для нее значение `"HS256"`.
Создайте переменную для срока действия токена.
Определите Pydantic-модель, которая будет использоваться для формирования ответа на запрос на получение токена.
Создайте служебную функцию для генерации нового токена доступа.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Обновление зависимостей { #update-the-dependencies }
Обновите `get_current_user` для получения того же токена, что и раньше, но на этот раз с использованием JWT-токенов.
Декодируйте полученный токен, проверьте его и верните текущего пользователя.
Если токен недействителен, то сразу же верните HTTP-ошибку.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Обновление *операции пути* `/token` { #update-the-token-path-operation }
Создайте `timedelta` со временем истечения срока действия токена.
Создайте реальный токен доступа JWT и верните его
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Технические подробности о JWT ключе `sub` { #technical-details-about-the-jwt-subject-sub }
В спецификации JWT говорится, что существует ключ `sub`, содержащий субъект токена.
Его использование необязательно, но это именно то место, куда вы должны поместить идентификатор пользователя, и поэтому мы здесь его и используем.
JWT может использоваться и для других целей, помимо идентификации пользователя и предоставления ему возможности выполнять операции непосредственно в вашем API.
Например, вы могли бы определить "автомобиль" или "запись в блоге".
Затем вы могли бы добавить права доступа к этой сущности, например "управлять" (для автомобиля) или "редактировать" (для блога).
Затем вы могли бы передать этот JWT-токен пользователю (или боту), и они использовали бы его для выполнения определенных действий (управление автомобилем или редактирование запись в блоге), даже не имея учетной записи, просто используя JWT-токен, сгенерированный вашим API.
Используя эти идеи, JWT можно применять для гораздо более сложных сценариев.
В отдельных случаях несколько сущностей могут иметь один и тот же идентификатор, скажем, `foo` (пользователь `foo`, автомобиль `foo` и запись в блоге `foo`).
Поэтому, чтобы избежать коллизий идентификаторов, при создании JWT-токена для пользователя можно добавить префикс `username` к значению ключа `sub`. Таким образом, в данном примере значение `sub` было бы `username:johndoe`.
Важно помнить, что ключ `sub` должен иметь уникальный идентификатор для всего приложения и представлять собой строку.
## Проверка в действии { #check-it }
Запустите сервер и перейдите к документации: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Вы увидите пользовательский интерфейс вида:
Пройдите авторизацию так же, как делали раньше.
Используя учетные данные пользователя:
Username: `johndoe`
Password: `secret`
/// check | Проверка
Обратите внимание, что нигде в коде не используется открытый текст пароля "`secret`", мы используем только его хэшированную версию.
///
Вызвав эндпоинт `/users/me/`, вы получите ответ в виде:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Если открыть инструменты разработчика, то можно увидеть, что передаваемые данные включают только токен, пароль передается только в первом запросе для аутентификации пользователя и получения токена доступа, но не в последующих:
/// note | Техническая информация
Обратите внимание на HTTP-заголовок `Authorization`, значение которого начинается с `Bearer `.
///
## Продвинутое использование `scopes` { #advanced-usage-with-scopes }
В OAuth2 существует понятие "диапазоны" ("`scopes`").
С их помощью можно добавить определенный набор разрешений к JWT-токену.
Затем вы можете передать этот токен непосредственно пользователю или третьей стороне для взаимодействия с вашим API с определенным набором ограничений.
О том, как их использовать и как они интегрированы в **FastAPI**, читайте далее в **Расширенном руководстве пользователя**.
## Резюме { #recap }
С учетом того, что вы видели до сих пор, вы можете создать безопасное приложение **FastAPI**, используя такие стандарты, как OAuth2 и JWT.
Практически в любом фреймворке работа с безопасностью довольно быстро превращается в сложную тему.
Многие пакеты, сильно упрощающие эту задачу, вынуждены идти на многочисленные компромиссы с моделью данных, с базой данных и с доступным функционалом. Некоторые из этих пакетов, которые пытаются уж слишком все упростить, имеют даже "дыры" в системе безопасности.
---
**FastAPI** не делает уступок ни одной базе данных, модели данных или инструментарию.
Он предоставляет вам полную свободу действий, позволяя выбирать то, что лучше всего подходит для вашего проекта.
Вы можете напрямую использовать многие хорошо поддерживаемые и широко распространенные пакеты, такие как `pwdlib` и `PyJWT`, поскольку **FastAPI** не требует сложных механизмов для интеграции внешних пакетов.
Напротив, он предоставляет инструменты, позволяющие максимально упростить этот процесс без ущерба для гибкости, надежности и безопасности.
При этом вы можете использовать и реализовывать безопасные стандартные протоколы, такие как OAuth2, относительно простым способом.
В **Расширенном руководстве пользователя** вы можете узнать больше о том, как использовать "диапазоны" ("`scopes`") OAuth2 для создания более точно настроенной системы разрешений в соответствии с теми же стандартами. OAuth2 с диапазонами — это механизм, используемый многими крупными провайдерами сервиса аутентификации, такими как Facebook, Google, GitHub, Microsoft, X (Twitter) и др., для авторизации сторонних приложений на взаимодействие с их API от имени их пользователей.
================================================
FILE: docs/ru/docs/tutorial/security/simple-oauth2.md
================================================
# Простая авторизация OAuth2 с паролем и «Bearer» { #simple-oauth2-with-password-and-bearer }
Теперь, отталкиваясь от предыдущей главы, добавим недостающие части, чтобы получить полный поток безопасности.
## Получение `username` и `password` { #get-the-username-and-password }
Для получения `username` и `password` мы будем использовать утилиты безопасности **FastAPI**.
OAuth2 определяет, что при использовании "password flow" (аутентификация по паролю - именно его мы используем) клиент/пользователь должен передавать поля `username` и `password` в полях формы.
В спецификации сказано, что поля должны быть названы именно так. Поэтому `user-name` или `email` работать не будут.
Но не волнуйтесь, вы можете показать это конечным пользователям во фронтенде в том виде, в котором хотите.
А ваши модели баз данных могут использовать любые другие имена.
Но для логин-операции пути нам нужно использовать именно эти имена, чтобы быть совместимыми со спецификацией (и иметь возможность, например, использовать встроенную систему документации API).
В спецификации также указано, что `username` и `password` должны передаваться в виде данных формы (так что никакого JSON здесь нет).
### `scope` { #scope }
В спецификации также говорится, что клиент может передать еще одно поле формы — `scope`.
Имя поля формы — `scope` (в единственном числе), но на самом деле это длинная строка, состоящая из отдельных "scopes", разделенных пробелами.
Каждый "scope" — это просто строка (без пробелов).
Обычно они используются для указания уровней доступа, например:
* `users:read` или `users:write` — распространенные примеры.
* `instagram_basic` используется Facebook / Instagram.
* `https://www.googleapis.com/auth/drive` используется Google.
/// info | Дополнительная информация
В OAuth2 "scope" — это просто строка, которая указывает требуемое конкретное разрешение.
Не имеет значения, содержит ли она другие символы, например `:`, или является ли это URL.
Эти детали зависят от реализации.
Для OAuth2 это просто строки.
///
## Код для получения `username` и `password` { #code-to-get-the-username-and-password }
Теперь воспользуемся утилитами, предоставляемыми **FastAPI**, чтобы обработать это.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Сначала импортируйте `OAuth2PasswordRequestForm` и затем используйте её как зависимость с `Depends` в операции пути для `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` — это зависимость-класс, которая объявляет тело формы со следующими полями:
* `username`.
* `password`.
* Необязательное поле `scope` в виде большой строки, состоящей из строк, разделенных пробелами.
* Необязательное поле `grant_type`.
/// tip | Подсказка
По спецификации OAuth2 поле `grant_type` обязательно и содержит фиксированное значение `password`, но `OAuth2PasswordRequestForm` это не проверяет строго.
Если вам нужно это строгое требование, используйте `OAuth2PasswordRequestFormStrict` вместо `OAuth2PasswordRequestForm`.
///
* Необязательное поле `client_id` (в нашем примере оно не нужно).
* Необязательное поле `client_secret` (в нашем примере оно не нужно).
/// info | Дополнительная информация
`OAuth2PasswordRequestForm` — это не специальный класс для **FastAPI**, как `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` сообщает **FastAPI**, что это схема безопасности. Поэтому она добавляется в OpenAPI соответствующим образом.
А `OAuth2PasswordRequestForm` — это просто зависимость-класс, которую вы могли бы написать сами, или вы могли бы объявить параметры `Form` напрямую.
Но так как это распространённый вариант использования, он предоставлен **FastAPI** напрямую, чтобы упростить задачу.
///
### Использование данных формы { #use-the-form-data }
/// tip | Подсказка
У экземпляра зависимости `OAuth2PasswordRequestForm` не будет атрибута `scope` с длинной строкой, разделенной пробелами. Вместо этого будет атрибут `scopes` со списком отдельных строк — по одной для каждого переданного scope.
В данном примере мы не используем `scopes`, но если вам это необходимо, функциональность есть.
///
Теперь получим данные о пользователе из (ненастоящей) базы данных, используя `username` из поля формы.
Если такого пользователя нет, то мы возвращаем ошибку "Incorrect username or password" (неверное имя пользователя или пароль).
Для ошибки используем исключение `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Проверка пароля { #check-the-password }
На данный момент у нас есть данные о пользователе из нашей базы данных, но мы еще не проверили пароль.
Давайте сначала поместим эти данные в Pydantic-модель `UserInDB`.
Никогда нельзя сохранять пароли в открытом виде, поэтому мы будем использовать (пока что ненастоящую) систему хеширования паролей.
Если пароли не совпадают, мы возвращаем ту же ошибку.
#### Хеширование паролей { #password-hashing }
"Хеширование" означает: преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
Каждый раз, когда вы передаете точно такое же содержимое (точно такой же пароль), вы получаете точно такую же тарабарщину.
Но преобразовать тарабарщину обратно в пароль невозможно.
##### Зачем использовать хеширование паролей { #why-use-password-hashing }
Если вашу базу данных украдут, у злоумышленника не будет паролей пользователей в открытом виде, только хэши.
Таким образом, он не сможет попробовать использовать эти же пароли в другой системе (поскольку многие пользователи используют один и тот же пароль повсеместно, это было бы опасно).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Про `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` означает:
*Передать ключи и значения `user_dict` непосредственно как аргументы ключ-значение, эквивалентно:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Дополнительная информация
Более полное объяснение `**user_dict` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user-in-dict).
///
## Возврат токена { #return-the-token }
Ответ операции пути `/token` должен быть объектом JSON.
В нём должен быть `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть `bearer`.
И в нём должен быть `access_token` — строка, содержащая наш токен доступа.
В этом простом примере мы намеренно поступим небезопасно и вернём тот же `username` в качестве токена.
/// tip | Подсказка
В следующей главе вы увидите реальную защищённую реализацию с хешированием паролей и токенами JWT.
Но пока давайте сосредоточимся на необходимых нам деталях.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Подсказка
Согласно спецификации, вы должны возвращать JSON с `access_token` и `token_type`, как в данном примере.
Это то, что вы должны сделать сами в своём коде и убедиться, что вы используете именно эти JSON-ключи.
Это практически единственное, о чём нужно не забыть, чтобы соответствовать спецификациям.
Остальное за вас сделает **FastAPI**.
///
## Обновление зависимостей { #update-the-dependencies }
Теперь мы обновим наши зависимости.
Мы хотим получить `current_user` только если этот пользователь активен.
Поэтому мы создаём дополнительную зависимость `get_current_active_user`, которая, в свою очередь, использует в качестве зависимости `get_current_user`.
Обе эти зависимости просто вернут HTTP-ошибку, если пользователь не существует или неактивен.
Таким образом, в нашей операции пути мы получим пользователя только в том случае, если он существует, корректно аутентифицирован и активен:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Дополнительная информация
Дополнительный HTTP-заголовок `WWW-Authenticate` со значением `Bearer`, который мы здесь возвращаем, также является частью спецификации.
Любой HTTP статус-код 401 "UNAUTHORIZED" должен также возвращать заголовок `WWW-Authenticate`.
В случае с bearer-токенами (наш случай) значение этого заголовка должно быть `Bearer`.
Фактически, этот дополнительный заголовок можно опустить, и всё будет работать.
Но он приведён здесь для соответствия спецификациям.
Кроме того, могут существовать инструменты, которые ожидают его и могут использовать, и это может быть полезно для вас или ваших пользователей — сейчас или в будущем.
В этом и заключается преимущество стандартов...
///
## Посмотрим, как это работает { #see-it-in-action }
Откройте интерактивную документацию: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Аутентификация { #authenticate }
Нажмите кнопку "Authorize".
Используйте учётные данные:
Пользователь: `johndoe`
Пароль: `secret`
После аутентификации вы увидите следующее:
### Получение собственных пользовательских данных { #get-your-own-user-data }
Теперь используйте операцию `GET` с путём `/users/me`.
Вы получите свои пользовательские данные, например:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Если щёлкнуть на значке замка и выйти из системы, а затем попробовать выполнить ту же операцию ещё раз, будет выдана ошибка HTTP 401:
```JSON
{
"detail": "Not authenticated"
}
```
### Неактивный пользователь { #inactive-user }
Теперь попробуйте с неактивным пользователем, аутентифицируйтесь с:
Пользователь: `alice`
Пароль: `secret2`
И попробуйте использовать операцию `GET` с путём `/users/me`.
Вы получите ошибку "Inactive user", как здесь:
```JSON
{
"detail": "Inactive user"
}
```
## Резюме { #recap }
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `username` и `password` для вашего API.
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных и с любой пользовательской или моделью данных.
Единственная деталь, которой не хватает, — система пока ещё не "защищена" по-настоящему.
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены JWT.
================================================
FILE: docs/ru/docs/tutorial/server-sent-events.md
================================================
# События, отправляемые сервером (SSE) { #server-sent-events-sse }
Вы можете передавать данные потоком клиенту, используя Server-Sent Events (SSE).
Это похоже на [Стриминг JSON Lines](stream-json-lines.md), но использует формат `text/event-stream`, который нативно поддерживается браузерами через [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info | Информация
Добавлено в FastAPI 0.135.0.
///
## Что такое Server-Sent Events? { #what-are-server-sent-events }
SSE — это стандарт для потоковой передачи данных с сервера на клиента по HTTP.
Каждое событие — это небольшой текстовый блок с «полями», такими как `data`, `event`, `id` и `retry`, разделёнными пустыми строками.
Это выглядит так:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE часто используют для стриминга ответов ИИ в чатах, живых уведомлений, логов и наблюдаемости, а также в других случаях, когда сервер «проталкивает» обновления клиенту.
/// tip | Совет
Если вам нужно стримить бинарные данные, например видео или аудио, посмотрите расширенное руководство: [Stream Data](../advanced/stream-data.md).
///
## Стриминг SSE с FastAPI { #stream-sse-with-fastapi }
Чтобы стримить SSE с FastAPI, используйте `yield` в своей функции-обработчике пути и укажите `response_class=EventSourceResponse`.
Импортируйте `EventSourceResponse` из `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Каждый возвращаемый через `yield` элемент кодируется как JSON и отправляется в поле `data:` события SSE.
Если вы объявите тип возврата как `AsyncIterable[Item]`, FastAPI будет использовать его, чтобы выполнить **валидацию**, добавить **документацию** и **сериализовать** данные с помощью Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Совет
Так как Pydantic будет сериализовать это на стороне **Rust**, вы получите значительно более высокую **производительность**, чем если не объявите тип возврата.
///
### Несинхронные функции-обработчики пути { #non-async-path-operation-functions }
Вы также можете использовать обычные функции `def` (без `async`) и применять `yield` тем же образом.
FastAPI проследит, чтобы выполнение прошло корректно и не блокировало цикл событий.
Так как в этом случае функция не async, правильным типом возврата будет `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Без объявленного типа возврата { #no-return-type }
Вы также можете опустить тип возврата. FastAPI использует [`jsonable_encoder`](./encoder.md) для преобразования данных и их отправки.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Если вам нужно задать поля SSE, такие как `event`, `id`, `retry` или `comment`, вы можете возвращать через `yield` объекты `ServerSentEvent` вместо обычных данных.
Импортируйте `ServerSentEvent` из `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
Поле `data` всегда кодируется как JSON. Вы можете передавать любое значение, сериализуемое в JSON, включая Pydantic-модели.
## Необработанные данные { #raw-data }
Если нужно отправлять данные без JSON-кодирования, используйте `raw_data` вместо `data`.
Это полезно для отправки заранее отформатированного текста, строк логов или специальных значений «сентинель», например `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Примечание
`data` и `raw_data` взаимно исключают друг друга. В каждом `ServerSentEvent` можно задать только одно из них.
///
## Возобновление с `Last-Event-ID` { #resuming-with-last-event-id }
Когда браузер переподключается после обрыва соединения, он отправляет последний полученный `id` в HTTP-заголовке `Last-Event-ID`.
Вы можете прочитать его как параметр заголовка и использовать, чтобы возобновить поток с того места, где клиент остановился:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE с POST { #sse-with-post }
SSE работает с любым HTTP-методом, не только с `GET`.
Это полезно для таких протоколов, как [MCP](https://modelcontextprotocol.io), которые стримят SSE по `POST`:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Технические детали { #technical-details }
FastAPI из коробки реализует некоторые лучшие практики для SSE.
- Отправлять комментарий «ping» для поддержания соединения («keep alive») каждые 15 секунд, когда нет сообщений, чтобы предотвратить закрытие соединения некоторыми прокси, как рекомендовано в [HTML specification: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
- Устанавливать заголовок `Cache-Control: no-cache`, чтобы предотвратить кэширование потока.
- Устанавливать специальный заголовок `X-Accel-Buffering: no`, чтобы предотвратить буферизацию в некоторых прокси, например Nginx.
Вам не нужно ничего настраивать, это работает из коробки. 🤓
================================================
FILE: docs/ru/docs/tutorial/sql-databases.md
================================================
# SQL (реляционные) базы данных { #sql-relational-databases }
**FastAPI** не требует использовать SQL (реляционную) базу данных. Но вы можете использовать любую базу данных, которую хотите.
Здесь мы рассмотрим пример с использованием [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** построен поверх [SQLAlchemy](https://www.sqlalchemy.org/) и Pydantic. Его создал тот же автор, что и **FastAPI**, чтобы он идеально подходил для приложений FastAPI, которым нужны **SQL базы данных**.
/// tip | Подсказка
Вы можете использовать любую другую библиотеку для работы с SQL или NoSQL базами данных (иногда их называют "ORMs"), FastAPI ничего не навязывает. 😎
///
Так как SQLModel основан на SQLAlchemy, вы можете легко использовать **любую поддерживаемую** SQLAlchemy базу данных (а значит, и поддерживаемую SQLModel), например:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, и т.д.
В этом примере мы будем использовать **SQLite**, потому что она использует один файл и имеет встроенную поддержку в Python. Так что вы можете скопировать этот пример и запустить его как есть.
Позже, для продакшн-приложения, возможно, вы захотите использовать серверную базу данных, например **PostgreSQL**.
/// tip | Подсказка
Существует официальный генератор проектов на **FastAPI** и **PostgreSQL**, включающий frontend и другие инструменты: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Это очень простое и короткое руководство. Если вы хотите узнать больше о базах данных в целом, об SQL или о более продвинутых возможностях, обратитесь к [документации SQLModel](https://sqlmodel.tiangolo.com/).
## Установка `SQLModel` { #install-sqlmodel }
Сначала убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md), активировали его и затем установили `sqlmodel`:
```console
$ pip install sqlmodel
---> 100%
```
## Создание приложения с единственной моделью { #create-the-app-with-a-single-model }
Сначала мы создадим самую простую первую версию приложения с одной моделью **SQLModel**.
Позже мы улучшим его, повысив безопасность и универсальность, добавив **несколько моделей**. 🤓
### Создание моделей { #create-models }
Импортируйте `SQLModel` и создайте модель базы данных:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Класс `Hero` очень похож на модель Pydantic (фактически, под капотом, это и есть модель Pydantic).
Есть несколько отличий:
* `table=True` сообщает SQLModel, что это *модель-таблица*, она должна представлять **таблицу** в SQL базе данных, это не просто *модель данных* (как обычный класс Pydantic).
* `Field(primary_key=True)` сообщает SQLModel, что `id` — это **первичный ключ** в SQL базе данных (подробнее о первичных ключах SQL можно узнать в документации SQLModel).
**Примечание:** Мы используем `int | None` для поля первичного ключа, чтобы в Python-коде можно было *создать объект без `id`* (`id=None`), предполагая, что база данных *сгенерирует его при сохранении*. SQLModel понимает, что база данных предоставит `id`, и *определяет столбец как `INTEGER` (не `NULL`)* в схеме базы данных. См. [документацию SQLModel о первичных ключах](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) для подробностей.
* `Field(index=True)` сообщает SQLModel, что нужно создать **SQL индекс** для этого столбца, что позволит быстрее выполнять выборки при чтении данных, отфильтрованных по этому столбцу.
SQLModel будет знать, что объявленное как `str` станет SQL-столбцом типа `TEXT` (или `VARCHAR`, в зависимости от базы данных).
### Создание Engine { #create-an-engine }
Объект `engine` в SQLModel (под капотом это `engine` из SQLAlchemy) **удерживает соединения** с базой данных.
У вас должен быть **один объект `engine`** для всей кодовой базы, чтобы подключаться к одной и той же базе данных.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Параметр `check_same_thread=False` позволяет FastAPI использовать одну и ту же базу данных SQLite в разных потоках. Это необходимо, так как **один запрос** может использовать **больше одного потока** (например, в зависимостях).
Не волнуйтесь, с такой структурой кода мы позже обеспечим использование **одной сессии SQLModel на запрос**, по сути именно этого и добивается `check_same_thread`.
### Создание таблиц { #create-the-tables }
Далее мы добавим функцию, которая использует `SQLModel.metadata.create_all(engine)`, чтобы **создать таблицы** для всех *моделей-таблиц*.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Создание зависимости Session { #create-a-session-dependency }
**`Session`** хранит **объекты в памяти** и отслеживает необходимые изменения в данных, затем **использует `engine`** для общения с базой данных.
Мы создадим **зависимость** FastAPI с `yield`, которая будет предоставлять новую `Session` для каждого запроса. Это и обеспечивает использование одной сессии на запрос. 🤓
Затем мы создадим объявленную (`Annotated`) зависимость `SessionDep`, чтобы упростить остальной код, который будет использовать эту зависимость.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Создание таблиц базы данных при старте { #create-database-tables-on-startup }
Мы создадим таблицы базы данных при запуске приложения.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Здесь мы создаём таблицы в обработчике события запуска приложения.
Для продакшн вы, вероятно, будете использовать скрипт миграций, который выполняется до запуска приложения. 🤓
/// tip | Подсказка
В SQLModel появятся утилиты миграций — обёртки над Alembic, но пока вы можете использовать [Alembic](https://alembic.sqlalchemy.org/en/latest/) напрямую.
///
### Создание героя (Hero) { #create-a-hero }
Так как каждая модель SQLModel также является моделью Pydantic, вы можете использовать её в тех же **аннотациях типов**, в которых используете модели Pydantic.
Например, если вы объявите параметр типа `Hero`, он будет прочитан из **JSON body (тела запроса)**.
Аналогично вы можете объявить её как **тип возвращаемого значения** функции, и тогда форма данных отобразится в автоматически сгенерированном UI документации API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Здесь мы используем зависимость `SessionDep` (это `Session`), чтобы добавить нового `Hero` в экземпляр `Session`, зафиксировать изменения в базе данных, обновить данные в `hero` и затем вернуть его.
### Чтение героев { #read-heroes }
Мы можем **читать** записи `Hero` из базы данных с помощью `select()`. Можно добавить `limit` и `offset` для постраничного вывода результатов.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Чтение одного героя { #read-one-hero }
Мы можем **прочитать** одного `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Удаление героя { #delete-a-hero }
Мы также можем **удалить** `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Запуск приложения { #run-the-app }
Вы можете запустить приложение:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Затем перейдите в UI `/docs`. Вы увидите, что **FastAPI** использует эти **модели** для **документирования** API, а также для **сериализации** и **валидации** данных.
## Обновление приложения с несколькими моделями { #update-the-app-with-multiple-models }
Теперь давайте немного **отрефакторим** приложение, чтобы повысить **безопасность** и **универсальность**.
Если вы посмотрите на предыдущую версию, в UI видно, что до сих пор клиент мог сам задавать `id` создаваемого `Hero`. 😱
Так делать нельзя, иначе они могли бы перезаписать `id`, который уже присвоен в БД. Решение по `id` должно приниматься **бэкендом** или **базой данных**, а **не клиентом**.
Кроме того, мы создаём для героя `secret_name`, но пока что возвращаем его повсюду — это не очень **секретно**... 😅
Мы исправим это, добавив несколько **дополнительных моделей**. Здесь SQLModel раскроется во всей красе. ✨
### Создание нескольких моделей { #create-multiple-models }
В **SQLModel** любая модель с `table=True` — это **модель-таблица**.
Любая модель без `table=True` — это **модель данных**, по сути обычная модель Pydantic (с парой небольших дополнений). 🤓
С SQLModel мы можем использовать **наследование**, чтобы **избежать дублирования** полей.
#### `HeroBase` — базовый класс { #herobase-the-base-class }
Начнём с модели `HeroBase`, которая содержит **общие поля** для всех моделей:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` — *модель-таблица* { #hero-the-table-model }
Далее создадим `Hero`, фактическую *модель-таблицу*, с **дополнительными полями**, которых может не быть в других моделях:
* `id`
* `secret_name`
Так как `Hero` наследуется от `HeroBase`, он **также** имеет **поля**, объявленные в `HeroBase`, поэтому все поля `Hero`:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` — публичная *модель данных* { #heropublic-the-public-data-model }
Далее мы создадим модель `HeroPublic`, именно она будет **возвращаться** клиентам API.
У неё те же поля, что и у `HeroBase`, поэтому она не включает `secret_name`.
Наконец-то личность наших героев защищена! 🥷
Также здесь заново объявляется `id: int`. Тем самым мы заключаем **контракт** с клиентами API: они всегда могут рассчитывать, что поле `id` присутствует и это `int` (никогда не `None`).
/// tip | Подсказка
Гарантия того, что в модели ответа значение всегда присутствует и это `int` (не `None`), очень полезна для клиентов API — так можно писать гораздо более простой код.
Кроме того, **автоматически сгенерированные клиенты** будут иметь более простые интерфейсы, и разработчикам, взаимодействующим с вашим API, будет работать значительно комфортнее. 😎
///
Все поля `HeroPublic` такие же, как в `HeroBase`, а `id` объявлен как `int` (не `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` — *модель данных* для создания героя { #herocreate-the-data-model-to-create-a-hero }
Теперь создадим модель `HeroCreate`, она будет **валидировать** данные от клиентов.
У неё те же поля, что и у `HeroBase`, а также есть `secret_name`.
Теперь, когда клиенты **создают нового героя**, они будут отправлять `secret_name`, он сохранится в базе данных, но не будет возвращаться клиентам в API.
/// tip | Подсказка
Так следует обрабатывать **пароли**: принимать их, но не возвращать в API.
Также перед сохранением значения паролей нужно **хэшировать**, **никогда не храните их в открытом виде**.
///
Поля `HeroCreate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` — *модель данных* для обновления героя { #heroupdate-the-data-model-to-update-a-hero }
В предыдущей версии приложения у нас не было способа **обновлять героя**, но теперь, с **несколькими моделями**, мы можем это сделать. 🎉
*Модель данных* `HeroUpdate` особенная: у неё **те же поля**, что и для создания нового героя, но все поля **необязательные** (у всех есть значение по умолчанию). Таким образом, при обновлении героя можно отправлять только те поля, которые нужно изменить.
Поскольку **фактически меняются все поля** (их тип теперь включает `None`, и по умолчанию они равны `None`), нам нужно **переобъявить** их.
Наследоваться от `HeroBase` не обязательно, так как мы заново объявляем все поля. Я оставлю наследование для единообразия, но это не необходимо. Скорее дело вкуса. 🤷
Поля `HeroUpdate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Создание с `HeroCreate` и возврат `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Теперь, когда у нас есть **несколько моделей**, мы можем обновить части приложения, которые их используют.
Мы получаем в запросе *модель данных* `HeroCreate` и на её основе создаём *модель-таблицу* `Hero`.
Новая *модель-таблица* `Hero` будет иметь поля, отправленные клиентом, а также `id`, сгенерированный базой данных.
Затем возвращаем из функции ту же *модель-таблицу* `Hero` как есть. Но так как мы объявили `response_model` с *моделью данных* `HeroPublic`, **FastAPI** использует `HeroPublic` для валидации и сериализации данных.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Подсказка
Теперь мы используем `response_model=HeroPublic` вместо **аннотации типа возвращаемого значения** `-> HeroPublic`, потому что фактически возвращаемое значение — это *не* `HeroPublic`.
Если бы мы объявили `-> HeroPublic`, ваш редактор кода и линтер справедливо пожаловались бы, что вы возвращаете `Hero`, а не `HeroPublic`.
Объявляя модель в `response_model`, мы говорим **FastAPI** сделать своё дело, не вмешиваясь в аннотации типов и работу редактора кода и других инструментов.
///
### Чтение героев с `HeroPublic` { #read-heroes-with-heropublic }
Аналогично мы можем **читать** `Hero` — снова используем `response_model=list[HeroPublic]`, чтобы данные валидировались и сериализовались корректно.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Чтение одного героя с `HeroPublic` { #read-one-hero-with-heropublic }
Мы можем **прочитать** одного героя:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Обновление героя с `HeroUpdate` { #update-a-hero-with-heroupdate }
Мы можем **обновить героя**. Для этого используем HTTP операцию `PATCH`.
В коде мы получаем `dict` со всеми данными, отправленными клиентом — **только с данными, отправленными клиентом**, исключая любые значения, которые были бы там лишь как значения по умолчанию. Для этого мы используем `exclude_unset=True`. Это главный трюк. 🪄
Затем мы используем `hero_db.sqlmodel_update(hero_data)`, чтобы обновить `hero_db` данными из `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Снова удаление героя { #delete-a-hero-again }
Операция **удаления** героя остаётся практически прежней.
Желание *«отрефакторить всё»* на этот раз останется неудовлетворённым. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Снова запустим приложение { #run-the-app-again }
Вы можете снова запустить приложение:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Если вы перейдёте в UI API `/docs`, вы увидите, что он обновился: теперь при создании героя он не ожидает получить `id` от клиента и т.д.
## Резюме { #recap }
Вы можете использовать [**SQLModel**](https://sqlmodel.tiangolo.com/) для взаимодействия с SQL базой данных и упростить код с помощью *моделей данных* и *моделей-таблиц*.
Гораздо больше вы можете узнать в документации **SQLModel**, там есть более подробное мини-[руководство по использованию SQLModel с **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/ru/docs/tutorial/static-files.md
================================================
# Статические Файлы { #static-files }
Вы можете предоставлять статические файлы автоматически из директории, используя `StaticFiles`.
## Использование `StaticFiles` { #use-staticfiles }
* Импортируйте `StaticFiles`.
* "Примонтируйте" экземпляр `StaticFiles()` к определённому пути.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Технические детали
Вы также можете использовать `from starlette.staticfiles import StaticFiles`.
**FastAPI** предоставляет `starlette.staticfiles` под псевдонимом `fastapi.staticfiles`, просто для вашего удобства, как разработчика. Но на самом деле это берётся напрямую из библиотеки Starlette.
///
### Что такое "Монтирование" { #what-is-mounting }
"Монтирование" означает добавление полноценного "независимого" приложения на определённый путь, которое затем обрабатывает все подпути.
Это отличается от использования `APIRouter`, так как примонтированное приложение является полностью независимым.
OpenAPI и документация из вашего главного приложения не будут содержать ничего из примонтированного приложения, и т.д.
Вы можете прочитать больше об этом в [Расширенном руководстве пользователя](../advanced/index.md).
## Детали { #details }
Первый параметр `"/static"` относится к подпути, по которому это "подприложение" будет "примонтировано". Таким образом, любой путь начинающийся со `"/static"` будет обработан этим приложением.
Параметр `directory="static"` относится к имени директории, которая содержит ваши статические файлы.
`name="static"` даёт имя маршруту, которое может быть использовано внутри **FastAPI**.
Все эти параметры могут отличаться от "`static`", настройте их в соответствии с вашими нуждами и конкретными деталями вашего собственного приложения.
## Больше информации { #more-info }
Для получения дополнительной информации о деталях и настройках ознакомьтесь с [документацией Starlette о статических файлах](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/ru/docs/tutorial/stream-json-lines.md
================================================
# Стриминг JSON Lines { #stream-json-lines }
У вас может быть последовательность данных, которую вы хотите отправлять в «**потоке**». Это можно сделать с помощью **JSON Lines**.
/// info | Информация
Добавлено в FastAPI 0.134.0.
///
## Что такое поток? { #what-is-a-stream }
«**Стриминг**» данных означает, что ваше приложение начнет отправлять элементы данных клиенту, не дожидаясь готовности всей последовательности.
То есть оно отправит первый элемент, клиент его получит и начнет обрабатывать, а вы в это время можете все еще генерировать следующий элемент.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Это может быть даже бесконечный поток, когда вы продолжаете отправлять данные.
## JSON Lines { #json-lines }
В таких случаях часто отправляют «**JSON Lines**», это формат, в котором отправляется по одному JSON-объекту на строку.
Ответ будет иметь тип содержимого `application/jsonl` (вместо `application/json`), а тело ответа будет примерно таким:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
Это очень похоже на JSON-массив (эквивалент списка Python), но вместо того чтобы быть обернутым в `[]` и иметь `,` между элементами, здесь **один JSON-объект на строку**, они разделены символом новой строки.
/// info | Информация
Важный момент в том, что ваше приложение сможет по очереди производить каждую строку, пока клиент потребляет предыдущие строки.
///
/// note | Технические детали
Так как каждый JSON-объект будет разделен новой строкой, в их содержимом не могут быть буквальные символы новой строки, но могут быть экранированные переводы строк (`\n`), что входит в стандарт JSON.
Однако обычно об этом не нужно беспокоиться — всё делается автоматически, читайте дальше. 🤓
///
## Варианты использования { #use-cases }
Вы можете использовать это для стриминга данных из сервиса **AI LLM**, из **логов** или **телеметрии**, или из других типов данных, которые можно структурировать в элементы **JSON**.
/// tip | Совет
Если вы хотите стримить бинарные данные, например видео или аудио, посмотрите расширенное руководство: [Потоковая передача данных](../advanced/stream-data.md).
///
## Стриминг JSON Lines с FastAPI { #stream-json-lines-with-fastapi }
Чтобы стримить JSON Lines с FastAPI, вместо использования `return` в вашей *функции-обработчике пути* используйте `yield`, чтобы по очереди выдавать каждый элемент.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Если каждый JSON-элемент, который вы хотите отправить обратно, имеет тип `Item` (Pydantic-модель), и это асинхронная функция, вы можете объявить тип возвращаемого значения как `AsyncIterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Если вы объявите тип возвращаемого значения, FastAPI будет использовать его, чтобы **валидировать** данные, **документировать** их в OpenAPI, **фильтровать** и **сериализовать** с помощью Pydantic.
/// tip | Совет
Так как Pydantic будет сериализовывать это на стороне **Rust**, вы получите значительно более высокую **производительность**, чем если бы вы не указывали тип возвращаемого значения.
///
### Неасинхронные функции-обработчики пути { #non-async-path-operation-functions }
Вы также можете использовать обычные функции `def` (без `async`) и использовать `yield` таким же образом.
FastAPI обеспечит корректное выполнение так, чтобы это не блокировало цикл событий.
Поскольку в этом случае функция не асинхронная, подходящим типом возвращаемого значения будет `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Без возвращаемого типа { #no-return-type }
Вы также можете опустить тип возвращаемого значения. Тогда FastAPI использует [`jsonable_encoder`](./encoder.md), чтобы преобразовать данные к виду, который можно сериализовать в JSON, и затем отправит их как JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## События, отправляемые сервером (SSE) { #server-sent-events-sse }
FastAPI также имеет полноценную поддержку Server-Sent Events (SSE), которые довольно похожи, но с парой дополнительных деталей. Вы можете узнать о них в следующей главе: [События, отправляемые сервером (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/ru/docs/tutorial/testing.md
================================================
# Тестирование { #testing }
Благодаря [Starlette](https://www.starlette.dev/testclient/), тестировать приложения **FastAPI** легко и приятно.
Тестирование основано на библиотеке [HTTPX](https://www.python-httpx.org), которая в свою очередь основана на библиотеке Requests, так что все действия знакомы и интуитивно понятны.
Используя эти инструменты, Вы можете напрямую задействовать [pytest](https://docs.pytest.org/) с **FastAPI**.
## Использование класса `TestClient` { #using-testclient }
/// info | Информация
Для использования класса `TestClient` сначала установите [`httpx`](https://www.python-httpx.org).
Убедитесь, что Вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет, например:
```console
$ pip install httpx
```
///
Импортируйте `TestClient`.
Создайте объект `TestClient`, передав ему в качестве параметра Ваше приложение **FastAPI**.
Создайте функцию, название которой должно начинаться с `test_` (это стандарт из соглашений `pytest`).
Используйте объект `TestClient` так же, как Вы используете `httpx`.
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Подсказка
Обратите внимание, что тестирующая функция является обычной `def`, а не асинхронной `async def`.
И вызов клиента также осуществляется без `await`.
Это позволяет вам использовать `pytest` без лишних усложнений.
///
/// note | Технические детали
Также можно написать `from starlette.testclient import TestClient`.
**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика. Но он берётся напрямую из Starlette.
///
/// tip | Подсказка
Если для тестирования Вам, помимо запросов к приложению FastAPI, необходимо вызывать асинхронные функции (например, для подключения к базе данных с помощью асинхронного драйвера), то ознакомьтесь со страницей [Асинхронное тестирование](../advanced/async-tests.md) в расширенном руководстве.
///
## Разделение тестов { #separating-tests }
В реальном приложении Вы, вероятно, разместите тесты в отдельном файле.
Кроме того, Ваше приложение **FastAPI** может состоять из нескольких файлов, модулей и т.п.
### Файл приложения **FastAPI** { #fastapi-app-file }
Допустим, структура файлов Вашего приложения похожа на ту, что описана на странице [Более крупные приложения](bigger-applications.md):
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
В файле `main.py` находится Ваше приложение **FastAPI**:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Файл тестов { #testing-file }
Также у Вас может быть файл `test_main.py` содержащий тесты. Он может находиться в том же Python-пакете (в той же директории с файлом `__init__.py`):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...и писать дальше тесты, как и раньше.
## Тестирование: расширенный пример { #testing-extended-example }
Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения.
### Расширенный файл приложения **FastAPI** { #extended-fastapi-app-file }
Мы продолжим работу с той же файловой структурой, что и ранее:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Предположим, что в файле `main.py` с приложением **FastAPI** есть несколько **операций пути**.
В нём описана операция `GET`, которая может вернуть ошибку.
Ещё есть операция `POST`, и она может вернуть несколько ошибок.
Обе *операции пути* требуют наличия в запросе HTTP-заголовка `X-Token`.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Расширенный файл тестов { #extended-testing-file }
Теперь обновим файл `test_main.py`, добавив в него тестов:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поисковиком (погуглить) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests.
Затем Вы просто применяете найденные ответы в тестах.
Например:
* Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL.
* Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`.
* Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`.
* Для передачи *HTTP-заголовков*, передайте объект `dict` через параметр `headers`.
* Для передачи *cookies* также передайте `dict`, но через параметр `cookies`.
Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с [документацией HTTPX](https://www.python-httpx.org).
/// info | Информация
Обратите внимание, что `TestClient` принимает данные, которые можно конвертировать в JSON, но не модели Pydantic.
Если в Ваших тестах есть модели Pydantic и Вы хотите отправить их в тестируемое приложение, то можете использовать функцию `jsonable_encoder`, описанную на странице [Кодировщик совместимый с JSON](encoder.md).
///
## Запуск { #run-it }
Далее Вам нужно установить `pytest`.
Убедитесь, что Вы создали [виртуальное окружение](../virtual-environments.md), активировали его, а затем установили пакет, например:
```console
$ pip install pytest
---> 100%
```
Он автоматически найдёт все файлы и тесты, выполнит их и предоставит Вам отчёт о результатах тестирования.
Запустите тесты:
================================================
FILE: docs/ru/docs/virtual-environments.md
================================================
# Виртуальные окружения { #virtual-environments }
При работе с проектами на Python рекомендуется использовать **виртуальное окружение** (или похожий механизм), чтобы изолировать пакеты, которые вы устанавливаете для каждого проекта.
/// info | Дополнительная информация
Если вы уже знакомы с виртуальными окружениями, знаете, как их создавать и использовать, вы можете пропустить этот раздел. 🤓
///
/// tip | Подсказка
**Виртуальное окружение** — это не то же самое, что **переменная окружения**.
**Переменная окружения** — это переменная в системе, которую могут использовать программы.
**Виртуальное окружение** — это директория с файлами внутри.
///
/// info | Дополнительная информация
На этой странице вы узнаете, как пользоваться **виртуальными окружениями** и как они работают.
Если вы готовы начать использовать **инструмент, который управляет всем** за вас (включая установку Python), попробуйте [uv](https://github.com/astral-sh/uv).
///
## Создание проекта { #create-a-project }
Сначала создайте директорию для вашего проекта.
Обычно я создаю папку с именем `code` в моем домашнем каталоге.
А внутри неё создаю отдельную директорию для каждого проекта.
```console
// Перейдите в домашний каталог
$ cd
// Создайте директорию для всех ваших проектов с кодом
$ mkdir code
// Перейдите в эту директорию code
$ cd code
// Создайте директорию для этого проекта
$ mkdir awesome-project
// Перейдите в директорию проекта
$ cd awesome-project
```
## Создание виртуального окружения { #create-a-virtual-environment }
Когда вы начинаете работать над Python‑проектом **впервые**, создайте виртуальное окружение **внутри вашего проекта**.
/// tip | Подсказка
Делать это нужно **один раз на проект**, не каждый раз, когда вы работаете.
///
//// tab | `venv`
Для создания виртуального окружения вы можете использовать модуль `venv`, который поставляется вместе с Python.
```console
$ python -m venv .venv
```
/// details | Что делает эта команда?
* `python`: использовать программу под названием `python`
* `-m`: вызвать модуль как скрипт, далее мы укажем, какой модуль вызвать
* `venv`: использовать модуль `venv`, который обычно устанавливается вместе с Python
* `.venv`: создать виртуальное окружение в новой директории `.venv`
///
////
//// tab | `uv`
Если у вас установлен [`uv`](https://github.com/astral-sh/uv), вы можете использовать его для создания виртуального окружения.
```console
$ uv venv
```
/// tip | Подсказка
По умолчанию `uv` создаст виртуальное окружение в директории с именем `.venv`.
Но вы можете переопределить это, передав дополнительный аргумент с именем директории.
///
////
Эта команда создаст новое виртуальное окружение в директории `.venv`.
/// details | `.venv` или другое имя?
Вы можете создать виртуальное окружение в другой директории, но по соглашению его называют `.venv`.
///
## Активация виртуального окружения { #activate-the-virtual-environment }
Активируйте новое виртуальное окружение, чтобы все команды Python и устанавливаемые пакеты использовали именно его.
/// tip | Подсказка
Делайте это **каждый раз**, когда вы начинаете **новую сессию терминала** для работы над проектом.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Или если вы используете Bash для Windows (например, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | Подсказка
Каждый раз, когда вы устанавливаете **новый пакет** в это окружение, **активируйте** окружение снова.
Это гарантирует, что если вы используете **программу терминала (CLI)**, установленную этим пакетом, вы будете использовать именно ту, что из вашего виртуального окружения, а не какую‑то глобально установленную, возможно другой версии, чем вам нужна.
///
## Проверка, что виртуальное окружение активно { #check-the-virtual-environment-is-active }
Проверьте, что виртуальное окружение активно (предыдущая команда сработала).
/// tip | Подсказка
Это **необязательно**, но это хороший способ **проверить**, что всё работает как ожидается и вы используете запланированное виртуальное окружение.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Если отображается исполняемый файл `python` по пути `.venv/bin/python` внутри вашего проекта (в нашем случае `awesome-project`), значит всё сработало. 🎉
////
//// tab | Windows PowerShell
Если отображается исполняемый файл `python` по пути `.venv\Scripts\python` внутри вашего проекта (в нашем случае `awesome-project`), значит всё сработало. 🎉
////
## Обновление `pip` { #upgrade-pip }
/// tip | Подсказка
Если вы используете [`uv`](https://github.com/astral-sh/uv), то для установки вы будете использовать его вместо `pip`, поэтому обновлять `pip` не нужно. 😎
///
Если для установки пакетов вы используете `pip` (он идёт по умолчанию вместе с Python), вам стоит **обновить** его до последней версии.
Многие экзотические ошибки при установке пакетов решаются простым предварительным обновлением `pip`.
/// tip | Подсказка
Обычно это делается **один раз**, сразу после создания виртуального окружения.
///
Убедитесь, что виртуальное окружение активно (см. команду выше) и запустите:
/// tip | Подсказка
Иногда при попытке обновить pip вы можете получить ошибку **`No module named pip`**.
Если это произошло, установите и обновите pip с помощью команды ниже:
Эта команда установит pip, если он ещё не установлен, а также гарантирует, что установленная версия pip будет не старее, чем версия, доступная в `ensurepip`.
///
## Добавление `.gitignore` { #add-gitignore }
Если вы используете **Git** (а вам стоит его использовать), добавьте файл `.gitignore`, чтобы исключить из Git всё, что находится в вашей `.venv`.
/// tip | Подсказка
Если вы использовали [`uv`](https://github.com/astral-sh/uv) для создания виртуального окружения, он уже сделал это за вас — можно пропустить этот шаг. 😎
///
/// tip | Подсказка
Сделайте это **один раз**, сразу после создания виртуального окружения.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Что делает эта команда?
* `echo "*"`: «напечатать» в терминале текст `*` (следующая часть немного меняет поведение)
* `>`: всё, что команда слева от `>` выводит в терминал, вместо печати нужно записать в файл, указанный справа от `>`
* `.gitignore`: имя файла, в который нужно записать текст
А `*` в Git означает «всё». То есть будет игнорироваться всё в директории `.venv`.
Эта команда создаст файл `.gitignore` со следующим содержимым:
```gitignore
*
```
///
## Установка пакетов { #install-packages }
После активации окружения вы можете устанавливать в него пакеты.
/// tip | Подсказка
Сделайте это **один раз** при установке или обновлении пакетов, необходимых вашему проекту.
Если вам нужно обновить версию или добавить новый пакет, вы **сделаете это снова**.
///
### Установка пакетов напрямую { #install-packages-directly }
Если вы торопитесь и не хотите объявлять зависимости проекта в отдельном файле, вы можете установить их напрямую.
/// tip | Подсказка
Очень хорошая идея — указать используемые вашим проектом пакеты и их версии в файле (например, `requirements.txt` или `pyproject.toml`).
///
//// tab | `pip`
////
### Установка из `requirements.txt` { #install-from-requirements-txt }
Если у вас есть `requirements.txt`, вы можете использовать его для установки пакетов.
//// tab | `pip`
////
/// details | `requirements.txt`
`requirements.txt` с некоторыми пакетами может выглядеть так:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Запуск вашей программы { #run-your-program }
После активации виртуального окружения вы можете запустить свою программу, и она будет использовать Python из вашего виртуального окружения вместе с установленными там пакетами.
```console
$ python main.py
Hello World
```
## Настройка вашего редактора кода { #configure-your-editor }
Скорее всего, вы будете использовать редактор кода. Убедитесь, что вы настроили его на использование того же виртуального окружения, которое вы создали (обычно он определяет его автоматически), чтобы получить автозавершение и подсветку ошибок.
Например:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Подсказка
Обычно это нужно сделать только **один раз**, при создании виртуального окружения.
///
## Деактивация виртуального окружения { #deactivate-the-virtual-environment }
Когда закончите работу над проектом, вы можете **деактивировать** виртуальное окружение.
```console
$ deactivate
```
Таким образом, при запуске `python` он не будет пытаться запускаться из этого виртуального окружения с установленными там пакетами.
## Готово к работе { #ready-to-work }
Теперь вы готовы начать работать над своим проектом.
/// tip | Подсказка
Хотите понять, что это всё было выше?
Продолжайте читать. 👇🤓
///
## Зачем нужны виртуальные окружения { #why-virtual-environments }
Чтобы работать с FastAPI, вам нужно установить [Python](https://www.python.org/).
После этого вам нужно будет **установить** FastAPI и другие **пакеты**, которые вы хотите использовать.
Для установки пакетов обычно используют команду `pip`, которая идет вместе с Python (или альтернативные инструменты).
Тем не менее, если просто использовать `pip` напрямую, пакеты будут установлены в **глобальное окружение Python** (глобально установленный Python).
### Проблема { #the-problem }
Так в чём проблема установки пакетов в глобальное окружение Python?
Со временем вы, вероятно, будете писать много разных программ, зависящих от **разных пакетов**. И некоторые из ваших проектов будут зависеть от **разных версий** одного и того же пакета. 😱
Например, вы можете создать проект `philosophers-stone`, который зависит от пакета **`harry` версии `1`**. Значит, нужно установить `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Затем вы создаёте другой проект `prisoner-of-azkaban`, который тоже зависит от `harry`, но ему нужен **`harry` версии `3`**.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Проблема в том, что если устанавливать пакеты глобально (в глобальное окружение), а не в локальное **виртуальное окружение**, вам придётся выбирать, какую версию `harry` установить.
Если вы хотите запустить `philosophers-stone`, сначала нужно установить `harry` версии `1`, например так:
```console
$ pip install "harry==1"
```
Тогда у вас в глобальном окружении Python будет установлен `harry` версии `1`:
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Но если затем вы захотите запустить `prisoner-of-azkaban`, вам нужно будет удалить `harry` версии `1` и установить `harry` версии `3` (или просто установка версии `3` автоматически удалит версию `1`).
```console
$ pip install "harry==3"
```
В итоге у вас будет установлен `harry` версии `3` в глобальном окружении Python.
А если вы снова попробуете запустить `philosophers-stone`, есть шанс, что он **не будет работать**, так как ему нужен `harry` версии `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Подсказка
В Python-пакетах часто стараются изо всех сил **избегать ломающих изменений** в **новых версиях**, но лучше действовать осторожно: устанавливать новые версии осознанно и тогда, когда вы можете прогнать тесты и убедиться, что всё работает корректно.
///
Теперь представьте то же самое с **многими** другими **пакетами**, от которых зависят все ваши **проекты**. Этим очень сложно управлять. И вы, скорее всего, в какой‑то момент будете запускать проекты с **несовместимыми версиями** пакетов и не понимать, почему что‑то не работает.
Кроме того, в зависимости от ОС (например, Linux, Windows, macOS), она может поставляться с уже установленным Python. И тогда, вероятно, в системе уже есть предустановленные пакеты определённых версий, **нужные вашей системе**. Если вы устанавливаете пакеты в глобальное окружение Python, вы можете в итоге **сломать** некоторые системные программы.
## Куда устанавливаются пакеты { #where-are-packages-installed }
Когда вы устанавливаете Python, на вашем компьютере создаются некоторые директории с файлами.
Часть этих директорий отвечает за хранение всех устанавливаемых вами пакетов.
Когда вы запускаете:
```console
// Не запускайте это сейчас, это просто пример 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Будет загружен сжатый файл с кодом FastAPI, обычно с [PyPI](https://pypi.org/project/fastapi/).
Также будут **загружены** файлы для других пакетов, от которых зависит FastAPI.
Затем все эти файлы будут **распакованы** и помещены в директорию на вашем компьютере.
По умолчанию они попадут в директорию из вашей установки Python — это **глобальное окружение**.
## Что такое виртуальные окружения { #what-are-virtual-environments }
Решение проблемы с пакетами в глобальном окружении — использовать **виртуальное окружение для каждого проекта**, над которым вы работаете.
Виртуальное окружение — это **директория**, очень похожая на глобальную, куда вы можете устанавливать пакеты для конкретного проекта.
Таким образом, у каждого проекта будет своё виртуальное окружение (директория `.venv`) со своими пакетами.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Что означает активация виртуального окружения { #what-does-activating-a-virtual-environment-mean }
Когда вы активируете виртуальное окружение, например так:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Или если вы используете Bash для Windows (например, [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Эта команда создаст или изменит некоторые [переменные окружения](environment-variables.md), которые будут доступны для следующих команд.
Одна из таких переменных — `PATH`.
/// tip | Подсказка
Вы можете узнать больше о переменной окружения `PATH` в разделе [Переменные окружения](environment-variables.md#path-environment-variable).
///
Активация виртуального окружения добавляет его путь `.venv/bin` (на Linux и macOS) или `.venv\Scripts` (на Windows) в переменную окружения `PATH`.
Предположим, что до активации окружения переменная `PATH` выглядела так:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что система будет искать программы в:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Это означает, что система будет искать программы в:
* `C:\Windows\System32`
////
После активации виртуального окружения переменная `PATH` будет выглядеть примерно так:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Это означает, что теперь система в первую очередь будет искать программы в:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
прежде чем искать в других директориях.
Поэтому, когда вы введёте в терминале `python`, система найдёт программу Python по пути
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
и использует именно её.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Это означает, что теперь система в первую очередь будет искать программы в:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
прежде чем искать в других директориях.
Поэтому, когда вы введёте в терминале `python`, система найдёт программу Python по пути
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
и использует именно её.
////
Важная деталь: путь к виртуальному окружению будет добавлен в самое **начало** переменной `PATH`. Система найдёт его **раньше**, чем любой другой установленный Python. Таким образом, при запуске `python` будет использоваться Python **из виртуального окружения**, а не какой‑то другой `python` (например, из глобального окружения).
Активация виртуального окружения также меняет ещё несколько вещей, но это — одна из важнейших.
## Проверка виртуального окружения { #checking-a-virtual-environment }
Когда вы проверяете, активно ли виртуальное окружение, например, так:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Это означает, что будет использоваться программа `python` **из виртуального окружения**.
На Linux и macOS используется `which`, а в Windows PowerShell — `Get-Command`.
Как работает эта команда: она проходит по переменной окружения `PATH`, идя **по каждому пути по порядку**, и ищет программу с именем `python`. Как только находит — **показывает путь** к этой программе.
Самое важное — при вызове `python` именно этот «`python`» и будет выполняться.
Так вы можете подтвердить, что находитесь в правильном виртуальном окружении.
/// tip | Подсказка
Легко активировать одно виртуальное окружение, получить один Python, а затем **перейти к другому проекту**.
И второй проект **не будет работать**, потому что вы используете **не тот Python**, из виртуального окружения другого проекта.
Полезно уметь проверить, какой именно `python` используется. 🤓
///
## Зачем деактивировать виртуальное окружение { #why-deactivate-a-virtual-environment }
Например, вы работаете над проектом `philosophers-stone`, **активируете виртуальное окружение**, устанавливаете пакеты и работаете с ним.
Затем вы хотите поработать над **другим проектом** `prisoner-of-azkaban`.
Вы переходите в этот проект:
```console
$ cd ~/code/prisoner-of-azkaban
```
Если вы не деактивируете виртуальное окружение `philosophers-stone`, при запуске `python` в терминале он попытается использовать Python из `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Но если вы деактивируете виртуальное окружение и активируете новое для `prisoner-of-askaban`, тогда при запуске `python` он будет использовать Python из виртуального окружения `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// Вам не нужно находиться в старой директории, чтобы деактивировать окружение, вы можете сделать это где угодно, даже после перехода в другой проект 😎
$ deactivate
// Активируйте виртуальное окружение в prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Теперь при запуске python он найдёт пакет sirius, установленный в этом виртуальном окружении ✨
$ python main.py
I solemnly swear 🐺
```
## Альтернативы { #alternatives }
Это простое руководство, чтобы вы начали и поняли, как всё работает **под капотом**.
Существует много **альтернатив** для управления виртуальными окружениями, зависимостями (requirements), проектами.
Когда вы будете готовы и захотите использовать инструмент для **управления всем проектом** — зависимостями пакетов, виртуальными окружениями и т.п., я бы предложил попробовать [uv](https://github.com/astral-sh/uv).
`uv` может многое:
* **Устанавливать Python**, включая разные версии
* Управлять **виртуальным окружением** ваших проектов
* Устанавливать **пакеты**
* Управлять **зависимостями и версиями** пакетов вашего проекта
* Обеспечивать наличие **точного** набора пакетов и версий к установке, включая их зависимости, чтобы вы были уверены, что сможете запускать проект в продакшн точно так же, как и на компьютере при разработке — это называется **locking**
* И многое другое
## Заключение { #conclusion }
Если вы прочитали и поняли всё это, теперь **вы знаете гораздо больше** о виртуальных окружениях, чем многие разработчики. 🤓
Знание этих деталей, скорее всего, пригодится вам в будущем, когда вы будете отлаживать что‑то сложное: вы будете понимать, **как всё работает под капотом**. 😎
================================================
FILE: docs/ru/llm-prompt.md
================================================
Translate to Russian (русский язык).
Language code: ru.
---
Use a neutral tone (not overly formal or informal).
Use correct Russian grammar — appropriate cases, suffixes, and endings depending on context.
For the following technical terms, use these specific translations to ensure consistency and clarity across the documentation:
* production (meaning production software or environment): продакшн (do not change the ending, for example, translate `in production` as `в продакшн` (not `в продакшене`))
* completion (meaning code auto-completion): автозавершение
* editor (meaning component of IDE): редактор кода
* adopt (meaning start to use): использовать (or `начать использовать`)
* headers (meaning HTTP-headers): HTTP-заголовки
* cookie sessions: сессии с использованием cookie
* tested (adjective): протестированный
* middleware: middleware (don't translate, but add `промежуточный слой` if clarification is needed)
* path operation: операция пути (optionally clarify as `обработчик пути`)
* path operation function: функция-обработчик пути (or `функция обработки пути`)
* proprietary: проприетарный
* benchmark: бенчмарк (add (`тест производительности`) if clarification is needed or use just `тест производительности`)
* ASGI server: ASGI-сервер
* In a hurry? : Нет времени?
* response status code: статус-код ответа
* HTTP status code: HTTP статус-код
* issue (meaning GitHub issue): Issue (add `тикет\обращение` if clarification is needed)
* PR (meaning GitHub pull request): пулл-реквест (add `запрос на изменение` if clarification is needed)
* run (meaning run the code): запустить (or `прогнать` if it's about testing the program)
* to reach users: донести до пользователей (or `привлечь внимание пользователей` in the promotion context)
* body (meaning HTTP request body): тело запроса
* body (meaning HTTP response body): тело ответа
* body parameter : body-параметр (or `параметр тела запроса`)
* validate: валидировать (or `выполнить валидацию`)
* requirements (meaning dependencies): зависимости
* auto-reload: авто-перезагрузка (or `перезагрузить автоматически` if used as a verb)
* show (meaning show on the screen): отобразить
* parsing (noun): парсинг
* origin (in web development): origin (add `источник` if clarification is needed)
* include: включать (add `в себя` if it's appropriate, or use `содержать` as an alternative)
* virtual environment: виртуальное окружение
* framework: фреймворк
* path paremeter: path-параметр
* path (as in URL path): путь
* form (as in HTML form): форма
* media type: тип содержимого (or `медиа-тип`)
* request: HTTP-запрос
* response: HTTP-ответ
* type hints: аннотации типов
* type annotations: аннотации типов
* context manager: менеджер контекста
* code base: кодовая база
* instantiate: создать экземпляр (avoid "инстанцировать")
* load balancer: балансировщик нагрузки
* load balance: балансировка нагрузки
* worker process: воркер-процесс (or `процесс воркера`)
* worker: воркер
* lifespan: lifespan (do not translate when it's about lifespan events, but translate as `жизненный цикл` or `срок жизни` in other cases)
* mount (verb): монтировать
* mount (noun): точка монтирования / mount (keep in English if it's a FastAPI keyword)
* plugin: плагин
* plug-in: плагин
* full stack: full stack (do not translate)
* full-stack: full-stack (do not translate)
* loop (as in async loop): цикл событий
* Machine Learning: Машинное обучение
* Deep Learning: Глубокое обучение
* callback hell: callback hell (clarify as `ад обратных вызовов`)
* on the fly: на лету
* scratch the surface: поверхностно ознакомиться
* tip: совет (or `подсказка` depending on context)
* Pydantic model: Pydantic-модель (`модель Pydantic` and `Pydantic модель` are also fine)
* declare: объявить
* have the next best performance, after: быть на следующем месте по производительности после
* timing attack: тайминговая атака (clarify `атака по времени` if needed)
* OAuth2 scope: OAuth2 scope (clarify `область` if needed)
* TLS Termination Proxy: прокси-сервер TSL-терминации
* utilize (resources): использовать
* сontent: содержимое (or `контент`)
* raise exception: вызвать исключение (also possible to use `сгенерировать исключение` or `выбросить исключение`)
* password flow: password flow (clarify as `аутентификация по паролю` if needed)
* tutorial: руководство (or `учебник`)
* too long; didn't read: слишком длинно; не читал
* proxy with a stripped path prefix: прокси с функцией удаления префикса пути
* nerd: умник
* sub application: подприложение
* webhook request: вебхук-запрос
* serve (meaning providing access to something): «отдавать» (or `предоставлять доступ к`)
* recap (noun): резюме
* utility function: вспомогательная функция
* fast to code: позволяет быстро писать код
* Tutorial - User Guide: Учебник - Руководство пользователя
* submodule: подмодуль
* subpackage: подпакет
* router: роутер
* building, deploying, accessing (when describing features of FastAPI Cloud): созданиe образа, развертывание и доступ
* type checker tool: инструмент проверки типов
Do not add whitespace in `т.д.`, `т.п.`.
================================================
FILE: docs/ru/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/tr/docs/_llm-test.md
================================================
# LLM test dosyası { #llm-test-file }
Bu doküman, dokümantasyonu çeviren LLM'nin `scripts/translate.py` içindeki `general_prompt`'u ve `docs/{language code}/llm-prompt.md` içindeki dile özel prompt'u anlayıp anlamadığını test eder. Dile özel prompt, `general_prompt`'a eklenir.
Buraya eklenen testler, dile özel prompt'ları tasarlayan herkes tarafından görülecektir.
Şu şekilde kullanın:
* Dile özel bir prompt bulundurun: `docs/{language code}/llm-prompt.md`.
* Bu dokümanın hedeflediğiniz dile sıfırdan yeni bir çevirisini yapın (örneğin `translate.py` içindeki `translate-page` komutu). Bu, çeviriyi `docs/{language code}/docs/_llm-test.md` altında oluşturur.
* Çeviride her şeyin yolunda olup olmadığını kontrol edin.
* Gerekirse dile özel prompt'u, genel prompt'u veya İngilizce dokümanı iyileştirin.
* Ardından çeviride kalan sorunları elle düzeltin; böylece iyi bir çeviri elde edin.
* İyi çeviri yerindeyken yeniden çeviri yapın. İdeal sonuç, LLM'nin artık çeviride hiçbir değişiklik yapmamasıdır. Bu da genel prompt'un ve dile özel prompt'un olabilecek en iyi hâle geldiği anlamına gelir (bazen rastgele gibi görünen birkaç değişiklik yapabilir; çünkü [LLM'ler deterministik algoritmalar değildir](https://doublespeak.chat/#/handbook#deterministic-output)).
Testler:
## Code snippets { #code-snippets }
//// tab | Test
Bu bir code snippet: `foo`. Bu da başka bir code snippet: `bar`. Bir tane daha: `baz quux`.
////
//// tab | Bilgi
Code snippet'lerin içeriği olduğu gibi bırakılmalıdır.
`scripts/translate.py` içindeki genel prompt'ta `### Content of code snippets` bölümüne bakın.
////
## Alıntılar { #quotes }
//// tab | Test
Dün bir arkadaşım şunu yazdı: "If you spell incorrectly correctly, you have spelled it incorrectly". Ben de şunu yanıtladım: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'".
/// note | Not
LLM muhtemelen bunu yanlış çevirecektir. Yeniden çeviri yapıldığında düzeltilmiş çeviriyi koruyup korumadığı önemlidir.
///
////
//// tab | Bilgi
Prompt tasarlayan kişi, düz tırnakları tipografik tırnaklara dönüştürüp dönüştürmemeyi seçebilir. Olduğu gibi bırakmak da uygundur.
Örneğin `docs/de/llm-prompt.md` içindeki `### Quotes` bölümüne bakın.
////
## Code snippet'lerde alıntılar { #quotes-in-code-snippets }
//// tab | Test
`pip install "foo[bar]"`
Code snippet'lerde string literal örnekleri: `"this"`, `'that'`.
Code snippet'lerde string literal için zor bir örnek: `f"I like {'oranges' if orange else "apples"}"`
Hardcore: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Bilgi
... Ancak code snippet'lerin içindeki tırnaklar olduğu gibi kalmalıdır.
////
## Code block'lar { #code-blocks }
//// tab | Test
Bir Bash code örneği...
```bash
# Evrene bir selam yazdır
echo "Hello universe"
```
...ve bir console code örneği...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...ve bir başka console code örneği...
```console
// "Code" adında bir dizin oluştur
$ mkdir code
// O dizine geç
$ cd code
```
...ve bir Python code örneği...
```Python
wont_work() # Bu çalışmayacak 😱
works(foo="bar") # Bu çalışır 🎉
```
...ve hepsi bu.
////
//// tab | Bilgi
Code block'ların içindeki code değiştirilmemelidir; tek istisna yorumlardır (comments).
`scripts/translate.py` içindeki genel prompt'ta `### Content of code blocks` bölümüne bakın.
////
## Sekmeler ve renkli kutular { #tabs-and-colored-boxes }
//// tab | Test
/// info | Bilgi
Bazı metin
///
/// note | Not
Bazı metin
///
/// note | Teknik Detaylar
Bazı metin
///
/// check | Ek bilgi
Bazı metin
///
/// tip | İpucu
Bazı metin
///
/// warning | Uyarı
Bazı metin
///
/// danger | Tehlike
Bazı metin
///
////
//// tab | Bilgi
Sekmelerin ve `Info`/`Note`/`Warning`/vb. blokların başlığı, dikey çizgiden (`|`) sonra çeviri olarak eklenmelidir.
`scripts/translate.py` içindeki genel prompt'ta `### Special blocks` ve `### Tab blocks` bölümlerine bakın.
////
## Web ve internal link'ler { #web-and-internal-links }
//// tab | Test
Link metni çevrilmelidir, link adresi değişmeden kalmalıdır:
* [Yukarıdaki başlığa link](#code-snippets)
* [Dahili link](index.md#installation)
* [Harici link](https://sqlmodel.tiangolo.com/)
* [Bir stile bağlantı](https://fastapi.tiangolo.com/css/styles.css)
* [Bir betiğe bağlantı](https://fastapi.tiangolo.com/js/logic.js)
* [Bir görsele bağlantı](https://fastapi.tiangolo.com/img/foo.jpg)
Link metni çevrilmelidir, link adresi çeviriye işaret etmelidir:
* [FastAPI link](https://fastapi.tiangolo.com/tr/)
////
//// tab | Bilgi
Link'ler çevrilmelidir, ancak adresleri değişmeden kalmalıdır. Bir istisna, FastAPI dokümantasyonunun sayfalarına verilen mutlak link'lerdir. Bu durumda link, çeviriye işaret etmelidir.
`scripts/translate.py` içindeki genel prompt'ta `### Links` bölümüne bakın.
////
## HTML "abbr" öğeleri { #html-abbr-elements }
//// tab | Test
Burada HTML "abbr" öğeleriyle sarılmış bazı şeyler var (bazıları uydurma):
### abbr tam bir ifade verir { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### abbr tam bir ifade ve bir açıklama verir { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Bilgi
"abbr" öğelerinin "title" attribute'ları belirli talimatlara göre çevrilir.
Çeviriler, LLM'nin kaldırmaması gereken kendi "abbr" öğelerini ekleyebilir. Örneğin İngilizce kelimeleri açıklamak için.
`scripts/translate.py` içindeki genel prompt'ta `### HTML abbr elements` bölümüne bakın.
////
## HTML "dfn" öğeleri { #html-dfn-elements }
* küme
* Derin Öğrenme
## Başlıklar { #headings }
//// tab | Test
### Bir web uygulaması geliştirin - bir öğretici { #develop-a-webapp-a-tutorial }
Merhaba.
### Type hint'ler ve -annotation'lar { #type-hints-and-annotations }
Tekrar merhaba.
### Super- ve subclass'lar { #super-and-subclasses }
Tekrar merhaba.
////
//// tab | Bilgi
Başlıklarla ilgili tek katı kural, LLM'nin süslü parantezler içindeki hash kısmını değiştirmemesidir; böylece link'ler bozulmaz.
`scripts/translate.py` içindeki genel prompt'ta `### Headings` bölümüne bakın.
Dile özel bazı talimatlar için örneğin `docs/de/llm-prompt.md` içindeki `### Headings` bölümüne bakın.
////
## Dokümanlarda kullanılan terimler { #terms-used-in-the-docs }
//// tab | Test
* siz
* sizin
* örn.
* vb.
* `foo` bir `int` olarak
* `bar` bir `str` olarak
* `baz` bir `list` olarak
* Tutorial - Kullanıcı kılavuzu
* İleri Düzey Kullanıcı Kılavuzu
* SQLModel dokümanları
* API dokümanları
* otomatik dokümanlar
* Veri Bilimi
* Deep Learning
* Machine Learning
* Dependency Injection
* HTTP Basic authentication
* HTTP Digest
* ISO formatı
* JSON Schema standardı
* JSON schema
* schema tanımı
* Password Flow
* Mobil
* deprecated
* designed
* invalid
* on the fly
* standard
* default
* case-sensitive
* case-insensitive
* uygulamayı serve etmek
* sayfayı serve etmek
* app
* application
* request
* response
* error response
* path operation
* path operation decorator
* path operation function
* body
* request body
* response body
* JSON body
* form body
* file body
* function body
* parameter
* body parameter
* path parameter
* query parameter
* cookie parameter
* header parameter
* form parameter
* function parameter
* event
* startup event
* server'ın startup'ı
* shutdown event
* lifespan event
* handler
* event handler
* exception handler
* handle etmek
* model
* Pydantic model
* data model
* database model
* form model
* model object
* class
* base class
* parent class
* subclass
* child class
* sibling class
* class method
* header
* headers
* authorization header
* `Authorization` header
* forwarded header
* dependency injection system
* dependency
* dependable
* dependant
* I/O bound
* CPU bound
* concurrency
* parallelism
* multiprocessing
* env var
* environment variable
* `PATH`
* `PATH` variable
* authentication
* authentication provider
* authorization
* authorization form
* authorization provider
* kullanıcı authenticate olur
* sistem kullanıcıyı authenticate eder
* CLI
* command line interface
* server
* client
* cloud provider
* cloud service
* geliştirme
* geliştirme aşamaları
* dict
* dictionary
* enumeration
* enum
* enum member
* encoder
* decoder
* encode etmek
* decode etmek
* exception
* raise etmek
* expression
* statement
* frontend
* backend
* GitHub discussion
* GitHub issue
* performance
* performance optimization
* return type
* return value
* security
* security scheme
* task
* background task
* task function
* template
* template engine
* type annotation
* type hint
* server worker
* Uvicorn worker
* Gunicorn Worker
* worker process
* worker class
* workload
* deployment
* deploy etmek
* SDK
* software development kit
* `APIRouter`
* `requirements.txt`
* Bearer Token
* breaking change
* bug
* button
* callable
* code
* commit
* context manager
* coroutine
* database session
* disk
* domain
* engine
* fake X
* HTTP GET method
* item
* library
* lifespan
* lock
* middleware
* mobile application
* module
* mounting
* network
* origin
* override
* payload
* processor
* property
* proxy
* pull request
* query
* RAM
* remote machine
* status code
* string
* tag
* web framework
* wildcard
* return etmek
* validate etmek
////
//// tab | Bilgi
Bu, dokümanlarda görülen (çoğunlukla) teknik terimlerin eksiksiz ve normatif olmayan bir listesidir. Prompt tasarlayan kişi için, LLM'nin hangi terimlerde desteğe ihtiyaç duyduğunu anlamada yardımcı olabilir. Örneğin iyi bir çeviriyi sürekli daha zayıf bir çeviriye geri alıyorsa. Ya da sizin dilinizde bir terimi çekimlemekte (conjugating/declinating) zorlanıyorsa.
Örneğin `docs/de/llm-prompt.md` içindeki `### List of English terms and their preferred German translations` bölümüne bakın.
////
================================================
FILE: docs/tr/docs/about/index.md
================================================
# Hakkında { #about }
FastAPI, tasarımı, ilham kaynağı ve daha fazlası hakkında. 🤓
================================================
FILE: docs/tr/docs/advanced/additional-responses.md
================================================
# OpenAPI'de Ek Response'lar { #additional-responses-in-openapi }
/// warning | Uyarı
Bu konu oldukça ileri seviye bir konudur.
**FastAPI**'ye yeni başlıyorsanız buna ihtiyaç duymayabilirsiniz.
///
Ek status code'lar, media type'lar, açıklamalar vb. ile ek response'lar tanımlayabilirsiniz.
Bu ek response'lar OpenAPI şemasına dahil edilir; dolayısıyla API dokümanlarında da görünürler.
Ancak bu ek response'lar için, status code'unuzu ve içeriğinizi vererek `JSONResponse` gibi bir `Response`'u doğrudan döndürdüğünüzden emin olmanız gerekir.
## `model` ile Ek Response { #additional-response-with-model }
*Path operation decorator*'larınıza `responses` adlı bir parametre geçebilirsiniz.
Bu parametre bir `dict` alır: anahtarlar her response için status code'lardır (`200` gibi), değerler ise her birine ait bilgileri içeren başka `dict`'lerdir.
Bu response `dict`'lerinin her birinde, `response_model`'e benzer şekilde bir Pydantic model içeren `model` anahtarı olabilir.
**FastAPI** bu modeli alır, JSON Schema'sını üretir ve OpenAPI'de doğru yere ekler.
Örneğin, `404` status code'u ve `Message` Pydantic model'i ile başka bir response tanımlamak için şunu yazabilirsiniz:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Not
`JSONResponse`'u doğrudan döndürmeniz gerektiğini unutmayın.
///
/// info | Bilgi
`model` anahtarı OpenAPI'nin bir parçası değildir.
**FastAPI** buradaki Pydantic model'i alır, JSON Schema'yı üretir ve doğru yere yerleştirir.
Doğru yer şurasıdır:
* Değeri başka bir JSON nesnesi (`dict`) olan `content` anahtarının içinde:
* Media type anahtarı (örn. `application/json`) bulunur; bunun değeri başka bir JSON nesnesidir ve onun içinde:
* Değeri model'den gelen JSON Schema olan `schema` anahtarı vardır; doğru yer burasıdır.
* **FastAPI** bunu doğrudan gömmek yerine OpenAPI'deki başka bir yerde bulunan global JSON Schema'lara bir referans ekler. Böylece diğer uygulamalar ve client'lar bu JSON Schema'ları doğrudan kullanabilir, daha iyi code generation araçları sağlayabilir, vb.
///
Bu *path operation* için OpenAPI'de üretilen response'lar şöyle olur:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Schema'lar OpenAPI şemasının içinde başka bir yere referanslanır:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Ana Response İçin Ek Media Type'lar { #additional-media-types-for-the-main-response }
Aynı `responses` parametresini, aynı ana response için farklı media type'lar eklemek amacıyla da kullanabilirsiniz.
Örneğin, `image/png` için ek bir media type ekleyerek, *path operation*'ınızın bir JSON nesnesi (media type `application/json`) ya da bir PNG görseli döndürebildiğini belirtebilirsiniz:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Not
Görseli `FileResponse` kullanarak doğrudan döndürmeniz gerektiğine dikkat edin.
///
/// info | Bilgi
`responses` parametrenizde açıkça farklı bir media type belirtmediğiniz sürece FastAPI, response'un ana response class'ı ile aynı media type'a sahip olduğunu varsayar (varsayılan `application/json`).
Ancak media type'ı `None` olan özel bir response class belirttiyseniz, FastAPI ilişkili bir model'i olan tüm ek response'lar için `application/json` kullanır.
///
## Bilgileri Birleştirme { #combining-information }
`response_model`, `status_code` ve `responses` parametreleri dahil olmak üzere, response bilgilerini birden fazla yerden birleştirebilirsiniz.
Varsayılan `200` status code'unu (ya da gerekiyorsa özel bir tane) kullanarak bir `response_model` tanımlayabilir, ardından aynı response için ek bilgileri `responses` içinde, doğrudan OpenAPI şemasına ekleyebilirsiniz.
**FastAPI**, `responses` içindeki ek bilgileri korur ve model'inizin JSON Schema'sı ile birleştirir.
Örneğin, Pydantic model kullanan ve özel bir `description` içeren `404` status code'lu bir response tanımlayabilirsiniz.
Ayrıca `response_model`'inizi kullanan, ancak özel bir `example` içeren `200` status code'lu bir response da tanımlayabilirsiniz:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Bunların hepsi OpenAPI'nize birleştirilerek dahil edilir ve API dokümanlarında gösterilir:
## Ön Tanımlı Response'ları Özel Olanlarla Birleştirme { #combine-predefined-responses-and-custom-ones }
Birçok *path operation* için geçerli olacak bazı ön tanımlı response'larınız olabilir; ancak bunları her *path operation*'ın ihtiyaç duyduğu özel response'larla birleştirmek isteyebilirsiniz.
Bu durumlarda, Python'daki bir `dict`'i `**dict_to_unpack` ile "unpacking" tekniğini kullanabilirsiniz:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Burada `new_dict`, `old_dict` içindeki tüm key-value çiftlerini ve buna ek olarak yeni key-value çiftini içerir:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Bu tekniği, *path operation*'larınızda bazı ön tanımlı response'ları yeniden kullanmak ve bunları ek özel response'larla birleştirmek için kullanabilirsiniz.
Örneğin:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## OpenAPI Response'ları Hakkında Daha Fazla Bilgi { #more-information-about-openapi-responses }
Response'ların içine tam olarak neleri dahil edebileceğinizi görmek için OpenAPI spesifikasyonundaki şu bölümlere bakabilirsiniz:
* [OpenAPI Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), `Response Object`'i içerir.
* [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), buradaki her şeyi `responses` parametreniz içinde, her bir response'un içine doğrudan ekleyebilirsiniz. Buna `description`, `headers`, `content` (bunun içinde farklı media type'lar ve JSON Schema'lar tanımlarsınız) ve `links` dahildir.
================================================
FILE: docs/tr/docs/advanced/additional-status-codes.md
================================================
# Ek Status Code'ları { #additional-status-codes }
Varsayılan olarak **FastAPI**, response'ları bir `JSONResponse` kullanarak döndürür; *path operation*'ınızdan döndürdüğünüz içeriği bu `JSONResponse`'un içine yerleştirir.
Varsayılan status code'u veya *path operation* içinde sizin belirlediğiniz status code'u kullanır.
## Ek status code'ları { #additional-status-codes_1 }
Ana status code'a ek olarak başka status code'lar da döndürmek istiyorsanız, `JSONResponse` gibi bir `Response`'u doğrudan döndürerek bunu yapabilirsiniz ve ek status code'u doğrudan orada ayarlarsınız.
Örneğin, item'ları güncellemeye izin veren bir *path operation*'ınız olduğunu düşünelim; başarılı olduğunda 200 "OK" HTTP status code'unu döndürüyor olsun.
Ancak yeni item'ları da kabul etmesini istiyorsunuz. Ve item daha önce yoksa, onu oluşturup 201 "Created" HTTP status code'unu döndürsün.
Bunu yapmak için `JSONResponse` import edin ve içeriğinizi doğrudan onunla döndürün; istediğiniz `status_code`'u da ayarlayın:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Uyarı
Yukarıdaki örnekte olduğu gibi bir `Response`'u doğrudan döndürdüğünüzde, response aynen olduğu gibi döndürülür.
Bir model ile serialize edilmez, vb.
İçinde olmasını istediğiniz veriyi taşıdığından emin olun ve değerlerin geçerli JSON olduğundan emin olun (eğer `JSONResponse` kullanıyorsanız).
///
/// note | Teknik Detaylar
`from starlette.responses import JSONResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.responses` içindekileri `fastapi.responses` altında da sunar. Ancak mevcut response'ların çoğu doğrudan Starlette'ten gelir. `status` için de aynı durum geçerlidir.
///
## OpenAPI ve API docs { #openapi-and-api-docs }
Ek status code'ları ve response'ları doğrudan döndürürseniz, FastAPI sizin ne döndüreceğinizi önceden bilemeyeceği için bunlar OpenAPI şemasına (API docs) dahil edilmez.
Ancak bunu kodunuzda şu şekilde dokümante edebilirsiniz: [Ek Response'lar](additional-responses.md).
================================================
FILE: docs/tr/docs/advanced/advanced-dependencies.md
================================================
# Gelişmiş Dependency'ler { #advanced-dependencies }
## Parametreli dependency'ler { #parameterized-dependencies }
Şimdiye kadar gördüğümüz tüm dependency'ler sabit bir function ya da class idi.
Ancak, birçok farklı function veya class tanımlamak zorunda kalmadan, dependency üzerinde bazı parametreler ayarlamak isteyebileceğiniz durumlar olabilir.
Örneğin, query parametresi `q`'nun belirli bir sabit içeriği barındırıp barındırmadığını kontrol eden bir dependency istediğimizi düşünelim.
Ama bu sabit içeriği parametreleştirebilmek istiyoruz.
## "Callable" bir instance { #a-callable-instance }
Python'da bir class'ın instance'ını "callable" yapmanın bir yolu vardır.
Class'ın kendisini değil (zaten callable'dır), o class'ın bir instance'ını.
Bunu yapmak için `__call__` adında bir method tanımlarız:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
Bu durumda, ek parametreleri ve alt-dependency'leri kontrol etmek için **FastAPI**'nin kullanacağı şey bu `__call__` olacaktır; ayrıca daha sonra *path operation function* içindeki parametreye bir değer geçmek için çağrılacak olan da budur.
## Instance'ı parametreleştirme { #parameterize-the-instance }
Ve şimdi, dependency'yi "parametreleştirmek" için kullanacağımız instance parametrelerini tanımlamak üzere `__init__` kullanabiliriz:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
Bu durumda **FastAPI**, `__init__` ile asla uğraşmaz veya onu önemsemez; onu doğrudan kendi kodumuzda kullanırız.
## Bir instance oluşturma { #create-an-instance }
Bu class'tan bir instance'ı şöyle oluşturabiliriz:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Böylece dependency'mizi "parametreleştirmiş" oluruz; artık içinde `"bar"` vardır ve bu değer `checker.fixed_content` attribute'u olarak durur.
## Instance'ı dependency olarak kullanma { #use-the-instance-as-a-dependency }
Sonra `Depends(FixedContentQueryChecker)` yerine `Depends(checker)` içinde bu `checker`'ı kullanabiliriz. Çünkü dependency, class'ın kendisi değil, `checker` instance'ıdır.
Ve dependency çözülürken **FastAPI** bu `checker`'ı şöyle çağırır:
```Python
checker(q="somequery")
```
...ve bunun döndürdüğü her şeyi, *path operation function* içinde `fixed_content_included` parametresine dependency değeri olarak geçirir:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | İpucu
Bunların tamamı biraz yapay görünebilir. Ayrıca bunun nasıl faydalı olduğu da henüz çok net olmayabilir.
Bu örnekler bilerek basit tutuldu; ama mekanizmanın nasıl çalıştığını gösteriyor.
Security bölümlerinde, aynı şekilde implement edilmiş yardımcı function'lar bulunuyor.
Buradaki her şeyi anladıysanız, security için kullanılan bu yardımcı araçların arka planda nasıl çalıştığını da zaten biliyorsunuz demektir.
///
## `yield`, `HTTPException`, `except` ve Background Tasks ile Dependency'ler { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Uyarı
Büyük ihtimalle bu teknik detaylara ihtiyacınız yok.
Bu detaylar, özellikle 0.121.0'dan eski bir FastAPI uygulamanız varsa ve `yield` kullanan dependency'lerle ilgili sorunlar yaşıyorsanız faydalıdır.
///
`yield` kullanan dependency'ler; farklı kullanım senaryolarını kapsamak ve bazı sorunları düzeltmek için zaman içinde evrildi. Aşağıda nelerin değiştiğinin bir özeti var.
### `yield` ve `scope` ile dependency'ler { #dependencies-with-yield-and-scope }
0.121.0 sürümünde FastAPI, `Depends(scope="function")` desteğini ekledi.
`Depends(scope="function")` kullanıldığında, `yield` sonrasındaki çıkış kodu, *path operation function* biter bitmez, response client'a geri gönderilmeden önce çalıştırılır.
`Depends(scope="request")` (varsayılan) kullanıldığında ise `yield` sonrasındaki çıkış kodu, response gönderildikten sonra çalıştırılır.
Daha fazlasını [`yield` ile Dependency'ler - Erken çıkış ve `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) dokümantasyonunda okuyabilirsiniz.
### `yield` ve `StreamingResponse` ile dependency'ler, Teknik Detaylar { #dependencies-with-yield-and-streamingresponse-technical-details }
FastAPI 0.118.0 öncesinde, `yield` kullanan bir dependency kullanırsanız, *path operation function* döndükten sonra ama response gönderilmeden hemen önce `yield` sonrasındaki çıkış kodu çalıştırılırdı.
Amaç, response'un ağ üzerinden taşınmasını beklerken gereğinden uzun süre resource tutmaktan kaçınmaktı.
Bu değişiklik aynı zamanda şunu da ifade ediyordu: `StreamingResponse` döndürürseniz, `yield` kullanan dependency'nin çıkış kodu zaten çalışmış olurdu.
Örneğin, `yield` kullanan bir dependency içinde bir veritabanı session'ınız varsa, `StreamingResponse` veri stream ederken bu session'ı kullanamazdı; çünkü `yield` sonrasındaki çıkış kodunda session zaten kapatılmış olurdu.
Bu davranış 0.118.0'da geri alındı ve `yield` sonrasındaki çıkış kodunun, response gönderildikten sonra çalıştırılması sağlandı.
/// info | Bilgi
Aşağıda göreceğiniz gibi, bu davranış 0.106.0 sürümünden önceki davranışa oldukça benzer; ancak köşe durumlar için çeşitli iyileştirmeler ve bug fix'ler içerir.
///
#### Erken Çıkış Kodu için Kullanım Senaryoları { #use-cases-with-early-exit-code }
Bazı özel koşullardaki kullanım senaryoları, response gönderilmeden önce `yield` kullanan dependency'lerin çıkış kodunun çalıştırıldığı eski davranıştan fayda görebilir.
Örneğin, `yield` kullanan bir dependency içinde yalnızca bir kullanıcıyı doğrulamak için veritabanı session'ı kullanan bir kodunuz olduğunu düşünün; ama bu session *path operation function* içinde bir daha hiç kullanılmıyor, yalnızca dependency içinde kullanılıyor **ve** response'un gönderilmesi uzun sürüyor. Mesela veriyi yavaş gönderen bir `StreamingResponse` var, ama herhangi bir nedenle veritabanını kullanmıyor.
Bu durumda veritabanı session'ı, response tamamen gönderilene kadar elde tutulur. Ancak session kullanılmıyorsa, bunu elde tutmak gerekli değildir.
Şöyle görünebilir:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
`Session`'ın otomatik kapatılması olan çıkış kodu şurada:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...yavaş veri gönderen response'un gönderimi bittikten sonra çalıştırılır:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Ama `generate_stream()` veritabanı session'ını kullanmadığı için, response gönderilirken session'ı açık tutmak aslında gerekli değildir.
SQLModel (veya SQLAlchemy) kullanarak bu spesifik senaryoya sahipseniz, session'a artık ihtiyacınız kalmadıktan sonra session'ı açıkça kapatabilirsiniz:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Böylece session veritabanı bağlantısını serbest bırakır ve diğer request'ler bunu kullanabilir.
`yield` kullanan bir dependency'den erken çıkış gerektiren farklı bir kullanım senaryonuz varsa, lütfen kullanım senaryonuzla birlikte ve `yield` kullanan dependency'ler için erken kapatmadan neden fayda göreceğinizi açıklayarak bir [GitHub Discussion Sorusu](https://github.com/fastapi/fastapi/discussions/new?category=questions) oluşturun.
`yield` kullanan dependency'lerde erken kapatma için ikna edici kullanım senaryoları varsa, erken kapatmayı seçmeli (opt-in) hale getiren yeni bir yöntem eklemeyi düşünebilirim.
### `yield` ve `except` ile dependency'ler, Teknik Detaylar { #dependencies-with-yield-and-except-technical-details }
FastAPI 0.110.0 öncesinde, `yield` kullanan bir dependency kullanır, sonra o dependency içinde `except` ile bir exception yakalar ve exception'ı tekrar raise etmezseniz; exception otomatik olarak herhangi bir exception handler'a veya internal server error handler'a raise/forward edilirdi.
Bu davranış 0.110.0 sürümünde değiştirildi. Amaç, handler olmayan (internal server errors) forward edilmiş exception'ların yönetilmemesinden kaynaklanan bellek tüketimini düzeltmek ve bunu normal Python kodunun davranışıyla tutarlı hale getirmekti.
### Background Tasks ve `yield` ile dependency'ler, Teknik Detaylar { #background-tasks-and-dependencies-with-yield-technical-details }
FastAPI 0.106.0 öncesinde, `yield` sonrasında exception raise etmek mümkün değildi; çünkü `yield` kullanan dependency'lerdeki çıkış kodu response gönderildikten *sonra* çalıştırılıyordu. Bu nedenle [Exception Handler'ları](../tutorial/handling-errors.md#install-custom-exception-handlers) zaten çalışmış olurdu.
Bu tasarımın ana sebeplerinden biri, background task'lerin içinde dependency'lerin "yield ettiği" aynı objeleri kullanmaya izin vermekti; çünkü çıkış kodu, background task'ler bittikten sonra çalıştırılıyordu.
Bu davranış FastAPI 0.106.0'da, response'un ağ üzerinde taşınmasını beklerken resource tutmamak amacıyla değiştirildi.
/// tip | İpucu
Ek olarak, bir background task normalde ayrı ele alınması gereken bağımsız bir mantık setidir ve kendi resource'larına sahip olmalıdır (ör. kendi veritabanı bağlantısı).
Bu şekilde muhtemelen daha temiz bir kod elde edersiniz.
///
Bu davranışa güvenerek kod yazdıysanız, artık background task'ler için resource'ları background task'in içinde oluşturmalı ve içeride yalnızca `yield` kullanan dependency'lerin resource'larına bağlı olmayan verileri kullanmalısınız.
Örneğin, aynı veritabanı session'ını kullanmak yerine background task içinde yeni bir veritabanı session'ı oluşturur ve veritabanındaki objeleri bu yeni session ile alırsınız. Ardından, background task function'ına veritabanından gelen objeyi parametre olarak geçirmek yerine, o objenin ID'sini geçirir ve objeyi background task function'ı içinde yeniden elde edersiniz.
================================================
FILE: docs/tr/docs/advanced/advanced-python-types.md
================================================
# Gelişmiş Python Tipleri { #advanced-python-types }
Python tipleriyle çalışırken işinize yarayabilecek bazı ek fikirler.
## `Union` veya `Optional` Kullanımı { #using-union-or-optional }
Kodunuz herhangi bir nedenle `|` kullanamıyorsa — örneğin bir tip açıklamasında (type annotation) değil de `response_model=` gibi bir yerdeyse — dikey çizgi (`|`) yerine `typing` içindeki `Union`'ı kullanabilirsiniz.
Örneğin, bir şeyin `str` ya da `None` olabileceğini şöyle belirtebilirsiniz:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing`, bir şeyin `None` olabileceğini belirtmek için `Optional` ile bir kısayol da sunar.
Benim oldukça öznel bakış açıma göre küçük bir ipucu:
- 🚨 `Optional[SomeType]` kullanmaktan kaçının
- Bunun yerine ✨ **`Union[SomeType, None]` kullanın** ✨.
İkisi de eşdeğer ve temelde aynıdır; ancak "**optional**" kelimesi değerin isteğe bağlı olduğunu ima eder. Oysa aslında " `None` olabilir" demektir; değer isteğe bağlı olmasa ve hâlâ zorunlu olsa bile.
Bence `Union[SomeType, None]` ne demek istediğini daha açık anlatır.
Burada mesele sadece kelimeler ve isimler. Ancak bu kelimeler sizin ve ekip arkadaşlarınızın koda bakışını etkileyebilir.
Örnek olarak şu fonksiyona bakalım:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
`name` parametresi `Optional[str]` olarak tanımlıdır; ancak isteğe bağlı değildir, parametre olmadan fonksiyonu çağıramazsınız:
```Python
say_hi() # Ah hayır, bu hata fırlatır! 😱
```
`name` parametresi varsayılan bir değeri olmadığı için hâlâ zorunludur (yani *optional* değildir). Yine de `name`, değer olarak `None` kabul eder:
```Python
say_hi(name=None) # Bu çalışır, None geçerlidir 🎉
```
İyi haber şu ki, çoğu durumda tip birliklerini (union) tanımlamak için doğrudan `|` kullanabilirsiniz:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Dolayısıyla, normalde `Optional` ve `Union` gibi isimler için endişelenmenize gerek yok. 😎
================================================
FILE: docs/tr/docs/advanced/async-tests.md
================================================
# Async Testler { #async-tests }
Sağlanan `TestClient` ile **FastAPI** uygulamalarınızı nasıl test edeceğinizi zaten gördünüz. Şimdiye kadar yalnızca senkron testler yazdık, yani `async` fonksiyonlar kullanmadan.
Testlerinizde asenkron fonksiyonlar kullanabilmek faydalı olabilir; örneğin veritabanınızı asenkron olarak sorguluyorsanız. Diyelim ki FastAPI uygulamanıza request gönderilmesini test etmek ve ardından async bir veritabanı kütüphanesi kullanırken backend'in doğru veriyi veritabanına başarıyla yazdığını doğrulamak istiyorsunuz.
Bunu nasıl çalıştırabileceğimize bir bakalım.
## pytest.mark.anyio { #pytest-mark-anyio }
Testlerimizde asenkron fonksiyonlar çağırmak istiyorsak, test fonksiyonlarımızın da asenkron olması gerekir. AnyIO bunun için güzel bir plugin sağlar; böylece bazı test fonksiyonlarının asenkron olarak çağrılacağını belirtebiliriz.
## HTTPX { #httpx }
**FastAPI** uygulamanız `async def` yerine normal `def` fonksiyonları kullanıyor olsa bile, altta yatan yapı hâlâ bir `async` uygulamadır.
`TestClient`, standart pytest kullanarak normal `def` test fonksiyonlarınızın içinden asenkron FastAPI uygulamasını çağırmak için içeride bazı “sihirli” işlemler yapar. Ancak bu sihir, onu asenkron fonksiyonların içinde kullandığımızda artık çalışmaz. Testlerimizi asenkron çalıştırdığımızda, test fonksiyonlarımızın içinde `TestClient` kullanamayız.
`TestClient`, [HTTPX](https://www.python-httpx.org) tabanlıdır ve neyse ki API'yi test etmek için HTTPX'i doğrudan kullanabiliriz.
## Örnek { #example }
Basit bir örnek için, [Daha Büyük Uygulamalar](../tutorial/bigger-applications.md) ve [Test Etme](../tutorial/testing.md) bölümlerinde anlatılana benzer bir dosya yapısı düşünelim:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
`main.py` dosyası şöyle olur:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
`test_main.py` dosyasında `main.py` için testler yer alır, artık şöyle görünebilir:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Çalıştırma { #run-it }
Testlerinizi her zamanki gibi şu şekilde çalıştırabilirsiniz:
```console
$ pytest
---> 100%
```
## Detaylı Anlatım { #in-detail }
`@pytest.mark.anyio` marker'ı, pytest'e bu test fonksiyonunun asenkron olarak çağrılması gerektiğini söyler:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | İpucu
Test fonksiyonu artık `TestClient` kullanırken eskiden olduğu gibi sadece `def` değil, `async def`.
///
Ardından app ile bir `AsyncClient` oluşturup `await` kullanarak ona async request'ler gönderebiliriz.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Bu, şu kullanıma denktir:
```Python
response = client.get('/')
```
...ki daha önce request'leri `TestClient` ile bu şekilde gönderiyorduk.
/// tip | İpucu
Yeni `AsyncClient` ile async/await kullandığımızı unutmayın; request asenkron çalışır.
///
/// warning | Uyarı
Uygulamanız lifespan event'lerine dayanıyorsa, `AsyncClient` bu event'leri tetiklemez. Tetiklendiklerinden emin olmak için [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage) paketindeki `LifespanManager`'ı kullanın.
///
## Diğer Asenkron Fonksiyon Çağrıları { #other-asynchronous-function-calls }
Test fonksiyonu artık asenkron olduğundan, testlerinizde FastAPI uygulamanıza request göndermenin yanında başka `async` fonksiyonları da (çağırıp `await` ederek) kodunuzun başka yerlerinde yaptığınız gibi aynı şekilde kullanabilirsiniz.
/// tip | İpucu
Testlerinize asenkron fonksiyon çağrıları entegre ederken `RuntimeError: Task attached to a different loop` hatasıyla karşılaşırsanız (ör. [MongoDB'nin MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop) kullanımı), event loop gerektiren nesneleri yalnızca async fonksiyonların içinde oluşturmanız gerektiğini unutmayın; örneğin bir `@app.on_event("startup")` callback'i içinde.
///
================================================
FILE: docs/tr/docs/advanced/behind-a-proxy.md
================================================
# Proxy Arkasında Çalıştırma { #behind-a-proxy }
Birçok durumda, FastAPI uygulamanızın önünde Traefik veya Nginx gibi bir **proxy** kullanırsınız.
Bu proxy'ler HTTPS sertifikalarını ve diğer bazı işleri üstlenebilir.
## Proxy Forwarded Header'ları { #proxy-forwarded-headers }
Uygulamanızın önündeki bir **proxy**, request'leri **server**'ınıza göndermeden önce genelde bazı header'ları dinamik olarak ayarlar. Böylece server, request'in proxy tarafından **forward** edildiğini; domain dahil orijinal (public) URL'yi, HTTPS kullanıldığını vb. bilgileri anlayabilir.
**Server** programı (örneğin **FastAPI CLI** üzerinden **Uvicorn**) bu header'ları yorumlayabilir ve ardından bu bilgiyi uygulamanıza aktarabilir.
Ancak güvenlik nedeniyle, server güvenilir bir proxy arkasında olduğunu bilmediği için bu header'ları yorumlamaz.
/// note | Teknik Detaylar
Proxy header'ları şunlardır:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Proxy Forwarded Header'larını Etkinleştirme { #enable-proxy-forwarded-headers }
FastAPI CLI'yi `--forwarded-allow-ips` *CLI Option*'ı ile başlatıp, bu forwarded header'ları okumada güvenilecek IP adreslerini verebilirsiniz.
Bunu `--forwarded-allow-ips="*"` olarak ayarlarsanız, gelen tüm IP'lere güvenir.
**Server**'ınız güvenilir bir **proxy** arkasındaysa ve onunla sadece proxy konuşuyorsa, bu ayar server'ın o **proxy**'nin IP'si her neyse onu kabul etmesini sağlar.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### HTTPS ile Redirect'ler { #redirects-with-https }
Örneğin `/items/` adında bir *path operation* tanımladığınızı düşünelim:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Client `/items`'a gitmeye çalışırsa, varsayılan olarak `/items/`'a redirect edilir.
Ancak *CLI Option* `--forwarded-allow-ips` ayarlanmadan önce, `http://localhost:8000/items/`'a redirect edebilir.
Oysa uygulamanız `https://mysuperapp.com` üzerinde host ediliyor olabilir ve redirect'in `https://mysuperapp.com/items/` olması gerekir.
Artık `--proxy-headers` ayarını yaparak FastAPI'nin doğru adrese redirect edebilmesini sağlarsınız. 😎
```
https://mysuperapp.com/items/
```
/// tip | İpucu
HTTPS hakkında daha fazla bilgi için [HTTPS Hakkında](../deployment/https.md) rehberine bakın.
///
### Proxy Forwarded Header'ları Nasıl Çalışır { #how-proxy-forwarded-headers-work }
**Proxy**'nin, client ile **application server** arasında forwarded header'ları nasıl eklediğini gösteren görsel bir temsil:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
**Proxy**, orijinal client request'ini araya girerek (intercept) alır ve request'i **application server**'a iletmeden önce özel *forwarded* header'ları (`X-Forwarded-*`) ekler.
Bu header'lar, aksi halde kaybolacak olan orijinal request bilgilerini korur:
* **X-Forwarded-For**: Orijinal client'ın IP adresi
* **X-Forwarded-Proto**: Orijinal protokol (`https`)
* **X-Forwarded-Host**: Orijinal host (`mysuperapp.com`)
**FastAPI CLI** `--forwarded-allow-ips` ile yapılandırıldığında bu header'lara güvenir ve örneğin redirect'lerde doğru URL'leri üretmek için bunları kullanır.
## Path Prefix'i Kırpılan (Stripped) Bir Proxy { #proxy-with-a-stripped-path-prefix }
Uygulamanıza bir path prefix ekleyen bir proxy'niz olabilir.
Bu durumlarda uygulamanızı yapılandırmak için `root_path` kullanabilirsiniz.
`root_path`, FastAPI'nin (Starlette üzerinden) üzerine kurulduğu ASGI spesifikasyonunun sağladığı bir mekanizmadır.
`root_path` bu özel senaryoları yönetmek için kullanılır.
Ayrıca sub-application mount ederken de içeride kullanılır.
Path prefix'i kırpılan bir proxy kullanmak, şu anlama gelir: Kodunuzda `/app` altında bir path tanımlarsınız; ancak üstte bir katman (proxy) ekleyip **FastAPI** uygulamanızı `/api/v1` gibi bir path'in altına koyarsınız.
Bu durumda, orijinal `/app` path'i aslında `/api/v1/app` altında servis edilir.
Kodunuzun tamamı sadece `/app` varmış gibi yazılmış olsa bile.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Proxy, request'i app server'a (muhtemelen FastAPI CLI üzerinden Uvicorn) iletmeden önce **path prefix**'i anlık olarak **"kırpar"** (strip). Böylece uygulamanız hâlâ `/app` altında servis ediliyormuş gibi davranır ve tüm kodunuzu `/api/v1` prefix'ini içerecek şekilde güncellemeniz gerekmez.
Buraya kadar her şey normal çalışır.
Ancak entegre doküman arayüzünü (frontend) açtığınızda, OpenAPI şemasını `/api/v1/openapi.json` yerine `/openapi.json` üzerinden almayı bekler.
Dolayısıyla tarayıcıda çalışan frontend `/openapi.json`'a erişmeye çalışır ve OpenAPI şemasını alamaz.
Çünkü uygulamamız proxy arkasında `/api/v1` path prefix'i ile çalışmaktadır; frontend'in OpenAPI şemasını `/api/v1/openapi.json` üzerinden çekmesi gerekir.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | İpucu
`0.0.0.0` IP'si, genelde programın ilgili makine/server üzerindeki tüm kullanılabilir IP'lerde dinlediği anlamına gelir.
///
Docs UI'nin, bu API `server`'ının (proxy arkasında) `/api/v1` altında bulunduğunu belirtmek için OpenAPI şemasına da ihtiyacı olur. Örneğin:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Burada daha fazla şey var
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Burada daha fazla şey var
}
}
```
Bu örnekte "Proxy", **Traefik** gibi bir şey olabilir. Server da FastAPI uygulamanızı çalıştıran (Uvicorn'lu) FastAPI CLI olabilir.
### `root_path` Sağlama { #providing-the-root-path }
Bunu yapmak için `--root-path` komut satırı seçeneğini şöyle kullanabilirsiniz:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Hypercorn kullanıyorsanız, onda da `--root-path` seçeneği vardır.
/// note | Teknik Detaylar
ASGI spesifikasyonu bu kullanım senaryosu için bir `root_path` tanımlar.
`--root-path` komut satırı seçeneği de bu `root_path`'i sağlar.
///
### Mevcut `root_path`'i Kontrol Etme { #checking-the-current-root-path }
Uygulamanızın her request için kullandığı mevcut `root_path` değerini alabilirsiniz; bu değer ASGI spesifikasyonunun bir parçası olan `scope` dict'inin içindedir.
Burada sadece göstermek için bunu mesaja dahil ediyoruz.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Ardından Uvicorn'u şu şekilde başlatırsanız:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Response şöyle bir şey olur:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### FastAPI Uygulamasında `root_path` Ayarlama { #setting-the-root-path-in-the-fastapi-app }
Alternatif olarak, `--root-path` gibi bir komut satırı seçeneği (veya muadili) sağlayamıyorsanız, FastAPI uygulamanızı oluştururken `root_path` parametresini ayarlayabilirsiniz:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
`FastAPI`'ye `root_path` vermek, Uvicorn veya Hypercorn'a `--root-path` komut satırı seçeneğini vermekle eşdeğerdir.
### `root_path` Hakkında { #about-root-path }
Şunu unutmayın: Server (Uvicorn) bu `root_path`'i, uygulamaya iletmek dışında başka bir amaçla kullanmaz.
Ancak tarayıcınızla [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) adresine giderseniz normal response'u görürsünüz:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Yani `http://127.0.0.1:8000/api/v1/app` üzerinden erişilmeyi beklemez.
Uvicorn, proxy'nin Uvicorn'a `http://127.0.0.1:8000/app` üzerinden erişmesini bekler; bunun üstüne ekstra `/api/v1` prefix'ini eklemek proxy'nin sorumluluğudur.
## Stripped Path Prefix Kullanan Proxy'ler Hakkında { #about-proxies-with-a-stripped-path-prefix }
Stripped path prefix kullanan bir proxy, yapılandırma yöntemlerinden yalnızca biridir.
Birçok durumda varsayılan davranış, proxy'nin stripped path prefix kullanmaması olacaktır.
Böyle bir durumda (stripped path prefix olmadan), proxy `https://myawesomeapp.com` gibi bir yerde dinler; tarayıcı `https://myawesomeapp.com/api/v1/app`'e giderse ve sizin server'ınız (ör. Uvicorn) `http://127.0.0.1:8000` üzerinde dinliyorsa, proxy (stripped path prefix olmadan) Uvicorn'a aynı path ile erişir: `http://127.0.0.1:8000/api/v1/app`.
## Traefik ile Local Olarak Test Etme { #testing-locally-with-traefik }
[Traefik](https://docs.traefik.io/) kullanarak, stripped path prefix'li deneyi local'de kolayca çalıştırabilirsiniz.
[Traefik'i indirin](https://github.com/containous/traefik/releases); tek bir binary'dir, sıkıştırılmış dosyayı çıkarıp doğrudan terminalden çalıştırabilirsiniz.
Ardından `traefik.toml` adında bir dosya oluşturup şunu yazın:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Bu, Traefik'e 9999 portunda dinlemesini ve `routes.toml` adlı başka bir dosyayı kullanmasını söyler.
/// tip | İpucu
Standart HTTP portu 80 yerine 9999 portunu kullanıyoruz; böylece admin (`sudo`) yetkileriyle çalıştırmanız gerekmez.
///
Şimdi diğer dosyayı, `routes.toml`'u oluşturun:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Bu dosya, Traefik'i `/api/v1` path prefix'ini kullanacak şekilde yapılandırır.
Ardından Traefik, request'leri `http://127.0.0.1:8000` üzerinde çalışan Uvicorn'unuza yönlendirir.
Şimdi Traefik'i başlatın:
Ve şimdi uygulamanızı `--root-path` seçeneğiyle başlatın:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Response'ları Kontrol Edin { #check-the-responses }
Şimdi Uvicorn'un portundaki URL'ye giderseniz: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), normal response'u görürsünüz:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | İpucu
`http://127.0.0.1:8000/app` üzerinden erişiyor olsanız bile, `root_path` değerinin `--root-path` seçeneğinden alınıp `/api/v1` olarak gösterildiğine dikkat edin.
///
Şimdi de Traefik'in portundaki URL'yi, path prefix ile birlikte açın: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Aynı response'u alırız:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
ama bu sefer proxy'nin sağladığı prefix path olan `/api/v1` ile gelen URL'de.
Elbette buradaki fikir, herkesin uygulamaya proxy üzerinden erişmesidir; dolayısıyla `/api/v1` path prefix'li sürüm "doğru" olandır.
Uvicorn'un doğrudan sunduğu, path prefix olmayan sürüm (`http://127.0.0.1:8000/app`) ise sadece _proxy_'nin (Traefik) erişmesi için kullanılmalıdır.
Bu da Proxy'nin (Traefik) path prefix'i nasıl kullandığını ve server'ın (Uvicorn) `--root-path` seçeneğinden gelen `root_path`'i nasıl kullandığını gösterir.
### Docs UI'yi Kontrol Edin { #check-the-docs-ui }
Şimdi işin eğlenceli kısmı. ✨
Uygulamaya erişmenin "resmi" yolu, tanımladığımız path prefix ile proxy üzerinden erişmektir. Bu yüzden beklendiği gibi, Uvicorn'un doğrudan servis ettiği docs UI'yi URL'de path prefix olmadan açarsanız çalışmaz; çünkü proxy üzerinden erişileceğini varsayar.
Şuradan kontrol edebilirsiniz: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
Ancak docs UI'yi proxy üzerinden, `9999` portuyla, `/api/v1/docs` altında "resmi" URL'den açarsak doğru çalışır! 🎉
Şuradan kontrol edebilirsiniz: [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Tam istediğimiz gibi. ✔️
Bunun nedeni, FastAPI'nin OpenAPI içinde varsayılan `server`'ı, `root_path` tarafından verilen URL ile oluşturmak için bu `root_path`'i kullanmasıdır.
## Ek `server`'lar { #additional-servers }
/// warning | Uyarı
Bu daha ileri seviye bir kullanım senaryosudur. İsterseniz atlayabilirsiniz.
///
Varsayılan olarak **FastAPI**, OpenAPI şemasında `root_path` için bir `server` oluşturur.
Ancak başka alternatif `servers` da sağlayabilirsiniz; örneğin *aynı* docs UI'nin hem staging hem de production ortamıyla etkileşime girmesini istiyorsanız.
Özel bir `servers` listesi verirseniz ve bir `root_path` varsa (çünkü API'niz proxy arkasındadır), **FastAPI** bu `root_path` ile bir "server"ı listenin başına ekler.
Örneğin:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Şöyle bir OpenAPI şeması üretir:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Burada daha fazla şey var
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Burada daha fazla şey var
}
}
```
/// tip | İpucu
`url` değeri `/api/v1` olan, `root_path`'ten alınmış otomatik üretilen server'a dikkat edin.
///
Docs UI'de, [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) adresinde şöyle görünür:
/// tip | İpucu
Docs UI, seçtiğiniz server ile etkileşime girer.
///
/// note | Teknik Detaylar
OpenAPI spesifikasyonunda `servers` özelliği opsiyoneldir.
`servers` parametresini belirtmezseniz ve `root_path` `/` ile aynıysa, üretilen OpenAPI şemasında `servers` özelliği varsayılan olarak tamamen çıkarılır; bu da `url` değeri `/` olan tek bir server ile eşdeğerdir.
///
### `root_path`'ten Otomatik `server` Eklenmesini Kapatma { #disable-automatic-server-from-root-path }
**FastAPI**'nin `root_path` kullanarak otomatik bir server eklemesini istemiyorsanız, `root_path_in_servers=False` parametresini kullanabilirsiniz:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
Böylece OpenAPI şemasına dahil etmez.
## Bir Sub-Application Mount Etme { #mounting-a-sub-application }
Bir sub-application'ı ([Sub Applications - Mounts](sub-applications.md) bölümünde anlatıldığı gibi) mount etmeniz gerekiyorsa ve aynı zamanda `root_path` ile bir proxy kullanıyorsanız, bunu beklendiği gibi normal şekilde yapabilirsiniz.
FastAPI içeride `root_path`'i akıllıca kullanır; dolayısıyla doğrudan çalışır. ✨
================================================
FILE: docs/tr/docs/advanced/custom-response.md
================================================
# Özel Response - HTML, Stream, File ve Diğerleri { #custom-response-html-stream-file-others }
Varsayılan olarak **FastAPI**, JSON response'lar döndürür.
Bunu, [Doğrudan bir Response döndür](response-directly.md) bölümünde gördüğünüz gibi doğrudan bir `Response` döndürerek geçersiz kılabilirsiniz.
Ancak doğrudan bir `Response` döndürürseniz (veya `JSONResponse` gibi herhangi bir alt sınıfını), veri otomatik olarak dönüştürülmez (bir `response_model` tanımlamış olsanız bile) ve dokümantasyon da otomatik üretilmez (örneğin, üretilen OpenAPI’nin parçası olarak HTTP header `Content-Type` içindeki ilgili "media type" dahil edilmez).
Bununla birlikte, *path operation decorator* içinde `response_class` parametresini kullanarak hangi `Response`’un (örn. herhangi bir `Response` alt sınıfı) kullanılacağını da ilan edebilirsiniz.
*path operation function*’ınızdan döndürdüğünüz içerik, o `Response`’un içine yerleştirilir.
/// note | Not
Media type’ı olmayan bir response class kullanırsanız, FastAPI response’unuzun content içermediğini varsayar; bu yüzden ürettiği OpenAPI dokümanında response formatını dokümante etmez.
///
## JSON Response'lar { #json-responses }
Varsayılan olarak FastAPI JSON response'lar döndürür.
Bir [Response Model](../tutorial/response-model.md) tanımlarsanız, FastAPI veriyi Pydantic kullanarak JSON’a serialize eder.
Bir response modeli tanımlamazsanız, FastAPI [JSON Compatible Encoder](../tutorial/encoder.md) bölümünde açıklanan `jsonable_encoder`’ı kullanır ve sonucu bir `JSONResponse` içine koyar.
`JSONResponse` örneğinde olduğu gibi JSON media type’ına (`application/json`) sahip bir `response_class` tanımlarsanız, döndürdüğünüz veri; *path operation decorator* içinde tanımladığınız herhangi bir Pydantic `response_model` ile otomatik olarak dönüştürülür (ve filtrelenir). Ancak veri Pydantic ile JSON bytes’a serialize edilmez; bunun yerine `jsonable_encoder` ile dönüştürülür ve ardından Python’un standart JSON kütüphanesini kullanarak bytes’a serialize edecek olan `JSONResponse` class’ına iletilir.
### JSON Performansı { #json-performance }
Kısaca, en yüksek performansı istiyorsanız bir [Response Model](../tutorial/response-model.md) kullanın ve *path operation decorator* içinde `response_class` tanımlamayın.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML Response { #html-response }
**FastAPI**’den doğrudan HTML içeren bir response döndürmek için `HTMLResponse` kullanın.
* `HTMLResponse` import edin.
* *path operation decorator*’ınızın `response_class` parametresi olarak `HTMLResponse` verin.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Bilgi
`response_class` parametresi, response’un "media type"’ını tanımlamak için de kullanılır.
Bu durumda HTTP header `Content-Type`, `text/html` olarak ayarlanır.
Ve OpenAPI’de de bu şekilde dokümante edilir.
///
### Bir `Response` Döndür { #return-a-response }
[Doğrudan bir Response döndür](response-directly.md) bölümünde görüldüğü gibi, *path operation* içinde doğrudan bir response döndürerek response’u override edebilirsiniz.
Yukarıdaki örneğin aynısı, bu sefer bir `HTMLResponse` döndürerek, şöyle görünebilir:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Uyarı
*path operation function*’ınızın doğrudan döndürdüğü bir `Response`, OpenAPI’de dokümante edilmez (örneğin `Content-Type` dokümante edilmez) ve otomatik interaktif dokümanlarda görünmez.
///
/// info | Bilgi
Elbette gerçek `Content-Type` header’ı, status code vb. değerler, döndürdüğünüz `Response` objesinden gelir.
///
### OpenAPI’de Dokümante Et ve `Response`’u Override Et { #document-in-openapi-and-override-response }
Response’u fonksiyonun içinden override etmek ama aynı zamanda OpenAPI’de "media type"’ı dokümante etmek istiyorsanız, `response_class` parametresini kullanıp ayrıca bir `Response` objesi döndürebilirsiniz.
Bu durumda `response_class` sadece OpenAPI *path operation*’ını dokümante etmek için kullanılır; sizin `Response`’unuz ise olduğu gibi kullanılır.
#### Doğrudan bir `HTMLResponse` Döndür { #return-an-htmlresponse-directly }
Örneğin şöyle bir şey olabilir:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
Bu örnekte `generate_html_response()` fonksiyonu, HTML’i bir `str` olarak döndürmek yerine zaten bir `Response` üretip döndürmektedir.
`generate_html_response()` çağrısının sonucunu döndürerek, varsayılan **FastAPI** davranışını override edecek bir `Response` döndürmüş olursunuz.
Ama `response_class` içinde `HTMLResponse` da verdiğiniz için **FastAPI**, bunu OpenAPI’de ve interaktif dokümanlarda `text/html` ile HTML olarak nasıl dokümante edeceğini bilir:
## Mevcut Response'lar { #available-responses }
Mevcut response'lardan bazıları aşağıdadır.
Unutmayın: `Response` ile başka herhangi bir şeyi döndürebilir, hatta özel bir alt sınıf da oluşturabilirsiniz.
/// note | Teknik Detaylar
`from starlette.responses import HTMLResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici için kolaylık olsun diye `starlette.responses` içindekileri `fastapi.responses` olarak da sağlar. Ancak mevcut response'ların çoğu doğrudan Starlette’ten gelir.
///
### `Response` { #response }
Ana `Response` class’ıdır; diğer tüm response'lar bundan türetilir.
Bunu doğrudan döndürebilirsiniz.
Şu parametreleri kabul eder:
* `content` - Bir `str` veya `bytes`.
* `status_code` - Bir `int` HTTP status code.
* `headers` - String’lerden oluşan bir `dict`.
* `media_type` - Media type’ı veren bir `str`. Örn. `"text/html"`.
FastAPI (aslında Starlette) otomatik olarak bir Content-Length header’ı ekler. Ayrıca `media_type`’a göre bir Content-Type header’ı ekler ve text türleri için sona bir charset ekler.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Yukarıda okuduğunuz gibi, bir miktar text veya bytes alır ve HTML response döndürür.
### `PlainTextResponse` { #plaintextresponse }
Bir miktar text veya bytes alır ve düz metin response döndürür.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Bir miktar veri alır ve `application/json` olarak encode edilmiş bir response döndürür.
Yukarıda okuduğunuz gibi, **FastAPI**’de varsayılan response budur.
/// note | Teknik Detaylar
Ancak bir response modeli veya dönüş tipi (return type) tanımlarsanız, veri doğrudan JSON’a serialize edilir ve JSON için doğru media type’a sahip bir response, `JSONResponse` class’ı kullanılmadan doğrudan döndürülür.
Bu, en iyi performansı elde etmenin ideal yoludur.
///
### `RedirectResponse` { #redirectresponse }
HTTP redirect döndürür. Varsayılan olarak 307 status code (Temporary Redirect) kullanır.
`RedirectResponse`’u doğrudan döndürebilirsiniz:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Veya `response_class` parametresi içinde kullanabilirsiniz:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
Bunu yaparsanız, *path operation* function’ınızdan doğrudan URL döndürebilirsiniz.
Bu durumda kullanılan `status_code`, `RedirectResponse` için varsayılan olan `307` olur.
---
Ayrıca `status_code` parametresini `response_class` parametresiyle birlikte kullanabilirsiniz:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Bir async generator veya normal generator/iterator (içinde `yield` olan bir fonksiyon) alır ve response body’yi stream eder.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Teknik Detaylar
Bir `async` görev, yalnızca bir `await` noktasına geldiğinde iptal edilebilir. Eğer hiç `await` yoksa, generator (içinde `yield` olan fonksiyon) düzgün şekilde iptal edilemez ve iptal istendikten sonra bile çalışmaya devam edebilir.
Bu küçük örneğin `await` ifadesine ihtiyacı olmadığı için, event loop’un iptali ele alabilmesi adına `await anyio.sleep(0)` ekliyoruz.
Büyük veya sonsuz akışlarda bu daha da önemlidir.
///
/// tip | İpucu
Doğrudan bir `StreamingResponse` döndürmek yerine, muhtemelen [Veri Stream Etme](./stream-data.md) bölümündeki tarzı takip etmelisiniz; çok daha kullanışlıdır ve iptali arka planda sizin için halleder.
JSON Lines stream ediyorsanız, [JSON Lines Stream Etme](../tutorial/stream-json-lines.md) kılavuzunu izleyin.
///
### `FileResponse` { #fileresponse }
Asenkron olarak bir dosyayı response olarak stream eder.
Diğer response türlerine göre instantiate ederken farklı argümanlar alır:
* `path` - Stream edilecek dosyanın dosya path'i.
* `headers` - Eklenecek özel header’lar; dictionary olarak.
* `media_type` - Media type’ı veren string. Ayarlanmazsa, dosya adı veya path kullanılarak media type tahmin edilir.
* `filename` - Ayarlanırsa response içindeki `Content-Disposition`’a dahil edilir.
File response'ları uygun `Content-Length`, `Last-Modified` ve `ETag` header’larını içerir.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
`response_class` parametresini de kullanabilirsiniz:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
Bu durumda *path operation* function’ınızdan doğrudan dosya path'ini döndürebilirsiniz.
## Özel response class { #custom-response-class }
`Response`’dan türeterek kendi özel response class’ınızı oluşturabilir ve kullanabilirsiniz.
Örneğin, bazı ayarlarla [`orjson`](https://github.com/ijl/orjson) kullanmak istediğinizi varsayalım.
Diyelim ki girintili ve biçimlendirilmiş JSON döndürmek istiyorsunuz; bunun için orjson seçeneği `orjson.OPT_INDENT_2`’yi kullanmak istiyorsunuz.
Bir `CustomORJSONResponse` oluşturabilirsiniz. Burada yapmanız gereken temel şey, content’i `bytes` olarak döndüren bir `Response.render(content)` metodu yazmaktır:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Artık şunu döndürmek yerine:
```json
{"message": "Hello World"}
```
...bu response şunu döndürür:
```json
{
"message": "Hello World"
}
```
Elbette JSON’u formatlamaktan çok daha iyi şekillerde bundan faydalanabilirsiniz. 😉
### `orjson` mı Response Model mi { #orjson-or-response-model }
Aradığınız şey performans ise, büyük olasılıkla bir `orjson` response’tan ziyade bir [Response Model](../tutorial/response-model.md) kullanmak daha iyi olacaktır.
Bir response modeliyle FastAPI, veriyi JSON’a serialize etmek için Pydantic’i kullanır; böylece diğer durumlarda olacağı gibi `jsonable_encoder` ile ara dönüşümlere gerek kalmaz.
Ve kaputun altında, Pydantic JSON’a serialize etmek için `orjson` ile aynı Rust tabanlı mekanizmaları kullanır; bu nedenle bir response modeliyle zaten en iyi performansı elde edersiniz.
## Varsayılan response class { #default-response-class }
Bir **FastAPI** class instance’ı veya bir `APIRouter` oluştururken, varsayılan olarak hangi response class’ının kullanılacağını belirtebilirsiniz.
Bunu tanımlayan parametre `default_response_class`’tır.
Aşağıdaki örnekte **FastAPI**, tüm *path operations* için varsayılan olarak JSON yerine `HTMLResponse` kullanır.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | İpucu
Daha önce olduğu gibi, *path operations* içinde `response_class`’ı yine override edebilirsiniz.
///
## Ek dokümantasyon { #additional-documentation }
OpenAPI’de media type’ı ve daha birçok detayı `responses` kullanarak da tanımlayabilirsiniz: [OpenAPI’de Ek Response'lar](additional-responses.md).
================================================
FILE: docs/tr/docs/advanced/dataclasses.md
================================================
# Dataclass Kullanımı { #using-dataclasses }
FastAPI, **Pydantic** üzerine inşa edilmiştir ve request/response tanımlamak için Pydantic model'lerini nasıl kullanacağınızı gösteriyordum.
Ancak FastAPI, [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) kullanmayı da aynı şekilde destekler:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Bu destek hâlâ **Pydantic** sayesinde vardır; çünkü Pydantic, [`dataclasses` için dahili destek](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel) sunar.
Yani yukarıdaki kod Pydantic'i doğrudan kullanmasa bile, FastAPI bu standart dataclass'ları Pydantic'in kendi dataclass biçimine dönüştürmek için Pydantic'i kullanmaktadır.
Ve elbette aynı özellikleri destekler:
* veri doğrulama (data validation)
* veri serileştirme (data serialization)
* veri dokümantasyonu (data documentation), vb.
Bu, Pydantic model'lerinde olduğu gibi çalışır. Aslında arka planda da aynı şekilde, Pydantic kullanılarak yapılır.
/// info | Bilgi
Dataclass'ların, Pydantic model'lerinin yapabildiği her şeyi yapamadığını unutmayın.
Bu yüzden yine de Pydantic model'lerini kullanmanız gerekebilir.
Ancak elinizde zaten bir sürü dataclass varsa, bunları FastAPI ile bir web API'yi beslemek için kullanmak güzel bir numaradır. 🤓
///
## `response_model` İçinde Dataclass'lar { #dataclasses-in-response-model }
`response_model` parametresinde `dataclasses` da kullanabilirsiniz:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Dataclass otomatik olarak bir Pydantic dataclass'ına dönüştürülür.
Bu sayede şeması API docs kullanıcı arayüzünde görünür:
## İç İçe Veri Yapılarında Dataclass'lar { #dataclasses-in-nested-data-structures }
İç içe veri yapıları oluşturmak için `dataclasses` ile diğer type annotation'ları da birleştirebilirsiniz.
Bazı durumlarda yine de Pydantic'in `dataclasses` sürümünü kullanmanız gerekebilir. Örneğin, otomatik oluşturulan API dokümantasyonunda hata alıyorsanız.
Bu durumda standart `dataclasses` yerine, drop-in replacement olan `pydantic.dataclasses` kullanabilirsiniz:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. `field` hâlâ standart `dataclasses` içinden import edilir.
2. `pydantic.dataclasses`, `dataclasses` için bir drop-in replacement'tır.
3. `Author` dataclass'ı, `Item` dataclass'larından oluşan bir liste içerir.
4. `Author` dataclass'ı, `response_model` parametresi olarak kullanılır.
5. Request body olarak dataclass'larla birlikte diğer standart type annotation'ları da kullanabilirsiniz.
Bu örnekte, `Item` dataclass'larından oluşan bir listedir.
6. Burada `items` içeren bir dictionary döndürüyoruz; `items` bir dataclass listesi.
FastAPI, veriyi JSON'a serileştirmeyi yine başarır.
7. Burada `response_model`, `Author` dataclass'larından oluşan bir listenin type annotation'ını kullanıyor.
Yine `dataclasses` ile standart type annotation'ları birleştirebilirsiniz.
8. Bu *path operation function*, `async def` yerine normal `def` kullanıyor.
Her zaman olduğu gibi, FastAPI'de ihtiyaca göre `def` ve `async def`’i birlikte kullanabilirsiniz.
Hangisini ne zaman kullanmanız gerektiğine dair hızlı bir hatırlatma isterseniz, [`async` ve `await`](../async.md#in-a-hurry) dokümanındaki _"In a hurry?"_ bölümüne bakın.
9. Bu *path operation function* dataclass döndürmüyor (isterse döndürebilir), onun yerine dahili verilerle bir dictionary listesi döndürüyor.
FastAPI, response'u dönüştürmek için (dataclass'ları içeren) `response_model` parametresini kullanacaktır.
Karmaşık veri yapıları oluşturmak için `dataclasses` ile diğer type annotation'ları pek çok farklı kombinasyonda birleştirebilirsiniz.
Daha spesifik ayrıntılar için yukarıdaki kod içi annotation ipuçlarına bakın.
## Daha Fazla Öğrenin { #learn-more }
`dataclasses`'ı diğer Pydantic model'leriyle de birleştirebilir, onlardan kalıtım alabilir, kendi model'lerinize dahil edebilirsiniz, vb.
Daha fazlası için [Pydantic'in dataclasses dokümantasyonuna](https://docs.pydantic.dev/latest/concepts/dataclasses/) bakın.
## Sürüm { #version }
Bu özellik FastAPI `0.67.0` sürümünden beri mevcuttur. 🔖
================================================
FILE: docs/tr/docs/advanced/events.md
================================================
# Lifespan Olayları { #lifespan-events }
Uygulama **başlamadan** önce çalıştırılması gereken mantığı (kodu) tanımlayabilirsiniz. Bu, bu kodun **bir kez**, uygulama **request almaya başlamadan önce** çalıştırılacağı anlamına gelir.
Benzer şekilde, uygulama **kapanırken** çalıştırılması gereken mantığı (kodu) da tanımlayabilirsiniz. Bu durumda bu kod, muhtemelen **çok sayıda request** işlendi **sonra**, **bir kez** çalıştırılır.
Bu kod, uygulama request almaya **başlamadan** önce ve request’leri işlemeyi **bitirdikten** hemen sonra çalıştığı için, uygulamanın tüm **lifespan**’ını (birazdan "lifespan" kelimesi önemli olacak 😉) kapsar.
Bu yaklaşım, tüm uygulama boyunca kullanacağınız ve request’ler arasında **paylaşılan** **resource**’ları kurmak ve/veya sonrasında bunları **temizlemek** için çok faydalıdır. Örneğin bir veritabanı connection pool’u ya da paylaşılan bir machine learning modelini yüklemek gibi.
## Kullanım Senaryosu { #use-case }
Önce bir **kullanım senaryosu** örneğiyle başlayalım, sonra bunu bununla nasıl çözeceğimize bakalım.
Request’leri işlemek için kullanmak istediğiniz bazı **machine learning modelleriniz** olduğunu hayal edelim. 🤖
Aynı modeller request’ler arasında paylaşılır; yani request başına bir model, kullanıcı başına bir model vb. gibi değil.
Modeli yüklemenin, diskten çok fazla **data** okunması gerektiği için **oldukça uzun sürebildiğini** düşünelim. Dolayısıyla bunu her request için yapmak istemezsiniz.
Modeli modülün/dosyanın en üst seviyesinde yükleyebilirdiniz; ancak bu, basit bir otomatik test çalıştırdığınızda bile **modelin yükleneceği** anlamına gelir. Böyle olunca test, kodun bağımsız bir kısmını çalıştırabilmek için önce modelin yüklenmesini beklemek zorunda kalır ve **yavaş** olur.
Burada çözeceğimiz şey bu: modeli request’ler işlenmeden önce yükleyelim, ama kod yüklenirken değil; yalnızca uygulama request almaya başlamadan hemen önce.
## Lifespan { #lifespan }
Bu *startup* ve *shutdown* mantığını, `FastAPI` uygulamasının `lifespan` parametresi ve bir "context manager" kullanarak tanımlayabilirsiniz (bunun ne olduğunu birazdan göstereceğim).
Önce bir örnekle başlayıp sonra ayrıntılarına bakalım.
Aşağıdaki gibi `yield` kullanan async bir `lifespan()` fonksiyonu oluşturuyoruz:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Burada, `yield` öncesinde (sahte) model fonksiyonunu machine learning modellerini içeren dictionary’e koyarak, modeli yükleme gibi maliyetli bir *startup* işlemini simüle ediyoruz. Bu kod, *startup* sırasında, uygulama **request almaya başlamadan önce** çalıştırılır.
Ardından `yield`’den hemen sonra modeli bellekten kaldırıyoruz (unload). Bu kod, uygulama **request’leri işlemeyi bitirdikten sonra**, *shutdown*’dan hemen önce çalıştırılır. Örneğin memory veya GPU gibi resource’ları serbest bırakabilir.
/// tip | İpucu
`shutdown`, uygulamayı **durdurduğunuzda** gerçekleşir.
Belki yeni bir sürüm başlatmanız gerekiyordur, ya da çalıştırmaktan sıkılmışsınızdır. 🤷
///
### Lifespan fonksiyonu { #lifespan-function }
Dikkat edilmesi gereken ilk şey, `yield` içeren async bir fonksiyon tanımlıyor olmamız. Bu, `yield` kullanan Dependencies’e oldukça benzer.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
Fonksiyonun `yield`’den önceki kısmı, uygulama başlamadan **önce** çalışır.
`yield`’den sonraki kısım ise, uygulama işini bitirdikten **sonra** çalışır.
### Async Context Manager { #async-context-manager }
Bakarsanız, fonksiyon `@asynccontextmanager` ile dekore edilmiş.
Bu da fonksiyonu "**async context manager**" denen şeye dönüştürür.
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Python’da **context manager**, `with` ifadesi içinde kullanabildiğiniz bir yapıdır. Örneğin `open()` bir context manager olarak kullanılabilir:
```Python
with open("file.txt") as file:
file.read()
```
Python’ın güncel sürümlerinde bir de **async context manager** vardır. Bunu `async with` ile kullanırsınız:
```Python
async with lifespan(app):
await do_stuff()
```
Yukarıdaki gibi bir context manager veya async context manager oluşturduğunuzda, yaptığı şey şudur: `with` bloğuna girmeden önce `yield`’den önceki kodu çalıştırır, `with` bloğundan çıktıktan sonra da `yield`’den sonraki kodu çalıştırır.
Yukarıdaki kod örneğimizde bunu doğrudan kullanmıyoruz; bunun yerine FastAPI’ye veriyoruz ki o kullansın.
`FastAPI` uygulamasının `lifespan` parametresi bir **async context manager** alır; dolayısıyla oluşturduğumuz yeni `lifespan` async context manager’ını buraya geçebiliriz.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Alternatif Events (kullanımdan kaldırıldı) { #alternative-events-deprecated }
/// warning | Uyarı
*startup* ve *shutdown* işlemlerini yönetmenin önerilen yolu, yukarıda anlatıldığı gibi `FastAPI` uygulamasının `lifespan` parametresini kullanmaktır. Bir `lifespan` parametresi sağlarsanız, `startup` ve `shutdown` event handler’ları artık çağrılmaz. Ya tamamen `lifespan` ya da tamamen events; ikisi birden değil.
Muhtemelen bu bölümü atlayabilirsiniz.
///
*startup* ve *shutdown* sırasında çalıştırılacak bu mantığı tanımlamanın alternatif bir yolu daha vardır.
Uygulama başlamadan önce veya uygulama kapanırken çalıştırılması gereken event handler’ları (fonksiyonları) tanımlayabilirsiniz.
Bu fonksiyonlar `async def` ile veya normal `def` ile tanımlanabilir.
### `startup` eventi { #startup-event }
Uygulama başlamadan önce çalıştırılacak bir fonksiyon eklemek için, `"startup"` event’i ile tanımlayın:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
Bu durumda `startup` event handler fonksiyonu, "database" öğesini (sadece bir `dict`) bazı değerlerle başlatır.
Birden fazla event handler fonksiyonu ekleyebilirsiniz.
Ve tüm `startup` event handler’ları tamamlanmadan uygulamanız request almaya başlamaz.
### `shutdown` eventi { #shutdown-event }
Uygulama kapanırken çalıştırılacak bir fonksiyon eklemek için, `"shutdown"` event’i ile tanımlayın:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Burada `shutdown` event handler fonksiyonu, `log.txt` dosyasına `"Application shutdown"` satırını yazar.
/// info | Bilgi
`open()` fonksiyonunda `mode="a"` "append" anlamına gelir; yani satır, önceki içeriği silmeden dosyada ne varsa onun sonuna eklenir.
///
/// tip | İpucu
Dikkat edin, bu örnekte bir dosyayla etkileşen standart Python `open()` fonksiyonunu kullanıyoruz.
Dolayısıyla disk’e yazılmasını beklemeyi gerektiren I/O (input/output) söz konusu.
Ancak `open()` `async` ve `await` kullanmaz.
Bu yüzden event handler fonksiyonunu `async def` yerine standart `def` ile tanımlarız.
///
### `startup` ve `shutdown` birlikte { #startup-and-shutdown-together }
*startup* ve *shutdown* mantığınızın birbiriyle bağlantılı olma ihtimali yüksektir; bir şeyi başlatıp sonra bitirmek, bir resource edinip sonra serbest bırakmak vb. isteyebilirsiniz.
Bunu, ortak mantık veya değişken paylaşmayan ayrı fonksiyonlarda yapmak daha zordur; çünkü değerleri global değişkenlerde tutmanız veya benzer numaralar yapmanız gerekir.
Bu nedenle artık bunun yerine, yukarıda açıklandığı gibi `lifespan` kullanmanız önerilmektedir.
## Teknik Detaylar { #technical-details }
Meraklı nerd’ler için küçük bir teknik detay. 🤓
Altta, ASGI teknik spesifikasyonunda bu, [Lifespan Protokolü](https://asgi.readthedocs.io/en/latest/specs/lifespan.html)’nün bir parçasıdır ve `startup` ile `shutdown` adında event’ler tanımlar.
/// info | Bilgi
Starlette `lifespan` handler’ları hakkında daha fazlasını [Starlette Lifespan dokümanları](https://www.starlette.dev/lifespan/) içinde okuyabilirsiniz.
Ayrıca kodunuzun başka bölgelerinde de kullanılabilecek lifespan state’i nasıl yöneteceğinizi de kapsar.
///
## Alt Uygulamalar { #sub-applications }
🚨 Unutmayın: Bu lifespan event’leri (`startup` ve `shutdown`) yalnızca ana uygulama için çalıştırılır; [Alt Uygulamalar - Mounts](sub-applications.md) için çalıştırılmaz.
================================================
FILE: docs/tr/docs/advanced/generate-clients.md
================================================
# SDK Üretme { #generating-sdks }
**FastAPI**, **OpenAPI** spesifikasyonunu temel aldığı için API'leri birçok aracın anlayabildiği standart bir formatta tanımlanabilir.
Bu sayede güncel **dokümantasyon**, birden fazla dilde istemci kütüphaneleri (**SDKs**) ve kodunuzla senkron kalan **test** veya **otomasyon iş akışları** üretmek kolaylaşır.
Bu rehberde, FastAPI backend'iniz için bir **TypeScript SDK** üretmeyi öğreneceksiniz.
## Açık Kaynak SDK Üreteçleri { #open-source-sdk-generators }
Esnek bir seçenek olan [OpenAPI Generator](https://openapi-generator.tech/), **birçok programlama dilini** destekler ve OpenAPI spesifikasyonunuzdan SDK üretebilir.
**TypeScript client**'lar için [Hey API](https://heyapi.dev/), TypeScript ekosistemi için özel olarak tasarlanmış, optimize bir deneyim sunan bir çözümdür.
Daha fazla SDK üretecini [OpenAPI.Tools](https://openapi.tools/#sdk) üzerinde keşfedebilirsiniz.
/// tip | İpucu
FastAPI otomatik olarak **OpenAPI 3.1** spesifikasyonları üretir; bu yüzden kullanacağınız aracın bu sürümü desteklemesi gerekir.
///
## FastAPI Sponsorlarından SDK Üreteçleri { #sdk-generators-from-fastapi-sponsors }
Bu bölüm, FastAPI'yi sponsorlayan şirketlerin sunduğu **yatırım destekli** ve **şirket destekli** çözümleri öne çıkarır. Bu ürünler, yüksek kaliteli üretilen SDK'ların üzerine **ek özellikler** ve **entegrasyonlar** sağlar.
✨ [**FastAPI'ye sponsor olarak**](../help-fastapi.md#sponsor-the-author) ✨ bu şirketler, framework'ün ve **ekosisteminin** sağlıklı ve **sürdürülebilir** kalmasına yardımcı olur.
Sponsor olmaları aynı zamanda FastAPI **topluluğuna** (size) güçlü bir bağlılığı da gösterir; yalnızca **iyi bir hizmet** sunmayı değil, aynı zamanda **güçlü ve gelişen bir framework** olan FastAPI'yi desteklemeyi de önemsediklerini gösterir. 🙇
Örneğin şunları deneyebilirsiniz:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Bu çözümlerin bazıları açık kaynak olabilir veya ücretsiz katman sunabilir; yani finansal bir taahhüt olmadan deneyebilirsiniz. Başka ticari SDK üreteçleri de vardır ve internette bulunabilir. 🤓
## TypeScript SDK Oluşturma { #create-a-typescript-sdk }
Basit bir FastAPI uygulamasıyla başlayalım:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
*Path operation*'ların, request payload ve response payload için kullandıkları modelleri `Item` ve `ResponseMessage` modelleriyle tanımladıklarına dikkat edin.
### API Dokümanları { #api-docs }
`/docs` adresine giderseniz, request'lerde gönderilecek ve response'larda alınacak veriler için **schema**'ları içerdiğini görürsünüz:
Bu schema'ları görebilirsiniz, çünkü uygulamada modellerle birlikte tanımlandılar.
Bu bilgi uygulamanın **OpenAPI schema**'sında bulunur ve sonrasında API dokümanlarında gösterilir.
OpenAPI'ye dahil edilen, modellerden gelen bu bilginin aynısı **client code üretmek** için kullanılabilir.
### Hey API { #hey-api }
Modelleri olan bir FastAPI uygulamamız olduğunda, Hey API ile bir TypeScript client üretebiliriz. Bunu yapmanın en hızlı yolu npx kullanmaktır.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Bu komut `./src/client` içine bir TypeScript SDK üretecektir.
Web sitelerinde [`@hey-api/openapi-ts` kurulumunu](https://heyapi.dev/openapi-ts/get-started) öğrenebilir ve [üretilen çıktıyı](https://heyapi.dev/openapi-ts/output) inceleyebilirsiniz.
### SDK'yı Kullanma { #using-the-sdk }
Artık client code'u import edip kullanabilirsiniz. Şuna benzer görünebilir; method'lar için otomatik tamamlama aldığınıza dikkat edin:
Ayrıca gönderilecek payload için de otomatik tamamlama alırsınız:
/// tip | İpucu
`name` ve `price` için otomatik tamamlamaya dikkat edin; bunlar FastAPI uygulamasında, `Item` modelinde tanımlanmıştı.
///
Gönderdiğiniz veriler için satır içi hatalar (inline errors) da alırsınız:
Response objesi de otomatik tamamlama sunacaktır:
## Tag'lerle FastAPI Uygulaması { #fastapi-app-with-tags }
Birçok durumda FastAPI uygulamanız daha büyük olacaktır ve farklı *path operation* gruplarını ayırmak için muhtemelen tag'leri kullanacaksınız.
Örneğin **items** için bir bölüm, **users** için başka bir bölüm olabilir ve bunları tag'lerle ayırabilirsiniz:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Tag'lerle TypeScript Client Üretme { #generate-a-typescript-client-with-tags }
Tag'leri kullanan bir FastAPI uygulaması için client ürettiğinizde, genelde client code da tag'lere göre ayrılır.
Bu sayede client code tarafında her şey doğru şekilde sıralanır ve gruplandırılır:
Bu örnekte şunlar var:
* `ItemsService`
* `UsersService`
### Client Method İsimleri { #client-method-names }
Şu an üretilen `createItemItemsPost` gibi method isimleri çok temiz görünmüyor:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...çünkü client üreteci, her *path operation* için OpenAPI'nin dahili **operation ID** değerini kullanır.
OpenAPI, her operation ID'nin tüm *path operation*'lar arasında benzersiz olmasını ister. Bu yüzden FastAPI; operation ID'yi benzersiz tutabilmek için **function adı**, **path** ve **HTTP method/operation** bilgilerini birleştirerek üretir.
Ancak bunu bir sonraki adımda nasıl iyileştirebileceğinizi göstereceğim. 🤓
## Özel Operation ID'ler ve Daha İyi Method İsimleri { #custom-operation-ids-and-better-method-names }
Bu operation ID'lerin **üretilme** şeklini **değiştirerek**, client'larda daha basit **method isimleri** elde edebilirsiniz.
Bu durumda, her operation ID'nin **benzersiz** olduğundan başka bir şekilde emin olmanız gerekir.
Örneğin, her *path operation*'ın bir tag'i olmasını sağlayabilir ve operation ID'yi **tag** ve *path operation* **adı**na (function adı) göre üretebilirsiniz.
### Benzersiz ID Üreten Özel Fonksiyon { #custom-generate-unique-id-function }
FastAPI, her *path operation* için bir **unique ID** kullanır. Bu ID, **operation ID** için ve ayrıca request/response'lar için gerekebilecek özel model isimleri için de kullanılır.
Bu fonksiyonu özelleştirebilirsiniz. Bir `APIRoute` alır ve string döndürür.
Örneğin burada ilk tag'i (muhtemelen tek tag'iniz olur) ve *path operation* adını (function adı) kullanıyor.
Sonrasında bu özel fonksiyonu `generate_unique_id_function` parametresiyle **FastAPI**'ye geçebilirsiniz:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Özel Operation ID'lerle TypeScript Client Üretme { #generate-a-typescript-client-with-custom-operation-ids }
Artık client'ı tekrar üretirseniz, geliştirilmiş method isimlerini göreceksiniz:
Gördüğünüz gibi method isimleri artık önce tag'i, sonra function adını içeriyor; URL path'i ve HTTP operation bilgisini artık taşımıyor.
### Client Üretecine Vermeden Önce OpenAPI Spesifikasyonunu Ön İşlemek { #preprocess-the-openapi-specification-for-the-client-generator }
Üretilen kodda hâlâ bazı **tekrarlanan bilgiler** var.
Bu method'un **items** ile ilişkili olduğunu zaten biliyoruz; çünkü bu kelime `ItemsService` içinde var (tag'den geliyor). Ama method adında da tag adı önek olarak duruyor. 😕
OpenAPI genelinde muhtemelen bunu korumak isteriz; çünkü operation ID'lerin **benzersiz** olmasını sağlar.
Ancak üretilen client için, client'ları üretmeden hemen önce OpenAPI operation ID'lerini **değiştirip**, method isimlerini daha hoş ve **temiz** hale getirebiliriz.
OpenAPI JSON'u `openapi.json` diye bir dosyaya indirip, şu tarz bir script ile **öndeki tag'i kaldırabiliriz**:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Bununla operation ID'ler `items-get_items` gibi değerlerden sadece `get_items` olacak şekilde yeniden adlandırılır; böylece client üreteci daha basit method isimleri üretebilir.
### Ön İşlenmiş OpenAPI ile TypeScript Client Üretme { #generate-a-typescript-client-with-the-preprocessed-openapi }
Sonuç artık bir `openapi.json` dosyasında olduğuna göre, input konumunu güncellemeniz gerekir:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Yeni client'ı ürettikten sonra, tüm **otomatik tamamlama**, **satır içi hatalar**, vb. ile birlikte **temiz method isimleri** elde edersiniz:
## Faydalar { #benefits }
Otomatik üretilen client'ları kullanınca şu alanlarda **otomatik tamamlama** elde edersiniz:
* Method'lar.
* Body'deki request payload'ları, query parametreleri, vb.
* Response payload'ları.
Ayrıca her şey için **satır içi hatalar** (inline errors) da olur.
Backend kodunu her güncellediğinizde ve frontend'i **yeniden ürettiğinizde**, yeni *path operation*'lar method olarak eklenir, eskileri kaldırılır ve diğer değişiklikler de üretilen koda yansır. 🤓
Bu, bir şey değiştiğinde client code'a otomatik olarak **yansıyacağı** anlamına gelir. Ayrıca client'ı **build** ettiğinizde, kullanılan verilerde bir **uyuşmazlık** (mismatch) varsa hata alırsınız.
Böylece üretimde son kullanıcılara hata yansımasını beklemek ve sonra sorunun nerede olduğunu debug etmeye çalışmak yerine, geliştirme sürecinin çok erken aşamalarında **birçok hatayı tespit edersiniz**. ✨
================================================
FILE: docs/tr/docs/advanced/index.md
================================================
# Gelişmiş Kullanıcı Rehberi { #advanced-user-guide }
## Ek Özellikler { #additional-features }
Ana [Tutorial - User Guide](../tutorial/index.md) sayfası, **FastAPI**'nin tüm temel özelliklerini tanımanız için yeterli olmalıdır.
Sonraki bölümlerde diğer seçenekleri, konfigürasyonları ve ek özellikleri göreceksiniz.
/// tip | İpucu
Sonraki bölümler **mutlaka "gelişmiş" olmak zorunda değildir**.
Ve kullanım amacınıza bağlı olarak, çözüm bunlardan birinde olabilir.
///
## Önce Tutorial'ı Okuyun { #read-the-tutorial-first }
Ana [Tutorial - User Guide](../tutorial/index.md) sayfasındaki bilgilerle **FastAPI**'nin çoğu özelliğini yine de kullanabilirsiniz.
Ve sonraki bölümler, onu zaten okuduğunuzu ve bu temel fikirleri bildiğinizi varsayar.
================================================
FILE: docs/tr/docs/advanced/json-base64-bytes.md
================================================
# JSON'da Bytes'i Base64 Olarak Kullanma { #json-with-bytes-as-base64 }
Uygulamanız JSON veri alıp gönderiyorsa ve bunun içine ikili (binary) veri eklemeniz gerekiyorsa, veriyi base64 olarak encode edebilirsiniz.
## Base64 ve Dosyalar { #base64-vs-files }
İkili veriyi JSON içinde encode etmek yerine, yükleme için [Request Files](../tutorial/request-files.md) ve gönderim için [Custom Response - FileResponse](./custom-response.md#fileresponse--fileresponse-) kullanıp kullanamayacağınıza önce bir bakın.
JSON sadece UTF-8 ile encode edilmiş string'ler içerebilir, dolayısıyla ham bytes içeremez.
Base64 ikili veriyi string olarak encode edebilir, ancak bunu yapmak için orijinal ikili veriden daha fazla karakter kullanır; bu yüzden genellikle normal dosyalardan daha verimsiz olur.
Base64'ü sadece gerçekten JSON içine ikili veri koymanız gerekiyorsa ve bunun için dosya kullanamıyorsanız tercih edin.
## Pydantic `bytes` { #pydantic-bytes }
`bytes` alanları olan bir Pydantic model tanımlayabilir, ardından model config'inde `val_json_bytes` kullanarak giriş JSON verisini base64 ile doğrulamasını (validate) söyleyebilirsiniz; bu doğrulamanın bir parçası olarak base64 string'i bytes'a decode eder.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
`/docs`'a bakarsanız, `data` alanının base64 ile encode edilmiş bytes beklediğini görürsünüz:
Şöyle bir request gönderebilirsiniz:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | İpucu
`aGVsbG8=` değeri, `hello` kelimesinin base64 encoding'idir.
///
Sonrasında Pydantic base64 string'ini decode eder ve modelin `data` alanında size orijinal bytes'ı verir.
Şöyle bir response alırsınız:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Çıkış Verisi için Pydantic `bytes` { #pydantic-bytes-for-output-data }
Çıkış verisi için de model config'inde `ser_json_bytes` ile `bytes` alanları kullanabilirsiniz; Pydantic JSON response üretirken bytes'ı base64 olarak serialize eder.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Giriş ve Çıkış Verisi için Pydantic `bytes` { #pydantic-bytes-for-input-and-output-data }
Elbette, aynı modeli base64 kullanacak şekilde yapılandırıp hem girişte (*validate*) `val_json_bytes` ile hem de çıkışta (*serialize*) `ser_json_bytes` ile JSON veri alıp gönderirken kullanabilirsiniz.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/tr/docs/advanced/middleware.md
================================================
# İleri Seviye Middleware { #advanced-middleware }
Ana tutorial'da uygulamanıza [Özel Middleware](../tutorial/middleware.md) eklemeyi gördünüz.
Ardından [`CORSMiddleware` ile CORS'u yönetmeyi](../tutorial/cors.md) de okudunuz.
Bu bölümde diğer middleware'leri nasıl kullanacağımıza bakacağız.
## ASGI middleware'leri ekleme { #adding-asgi-middlewares }
**FastAPI**, Starlette üzerine kurulu olduğu ve ASGI spesifikasyonunu uyguladığı için, herhangi bir ASGI middleware'ini kullanabilirsiniz.
Bir middleware'in çalışması için özellikle FastAPI ya da Starlette için yazılmış olması gerekmez; ASGI spec'ine uyduğu sürece yeterlidir.
Genel olarak ASGI middleware'leri, ilk argüman olarak bir ASGI app almayı bekleyen class'lar olur.
Dolayısıyla üçüncü taraf ASGI middleware'lerinin dokümantasyonunda muhtemelen şöyle bir şey yapmanızı söylerler:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Ancak FastAPI (aslında Starlette) bunu yapmanın daha basit bir yolunu sunar; böylece dahili middleware'ler server hatalarını doğru şekilde ele alır ve özel exception handler'lar düzgün çalışır.
Bunun için `app.add_middleware()` kullanırsınız (CORS örneğindeki gibi).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` ilk argüman olarak bir middleware class'ı alır ve middleware'e aktarılacak ek argümanları da kabul eder.
## Entegre middleware'ler { #integrated-middlewares }
**FastAPI**, yaygın kullanım senaryoları için birkaç middleware içerir; şimdi bunları nasıl kullanacağımıza bakacağız.
/// note | Teknik Detaylar
Bir sonraki örneklerde `from starlette.middleware.something import SomethingMiddleware` kullanmanız da mümkündür.
**FastAPI**, size (geliştirici olarak) kolaylık olsun diye `fastapi.middleware` içinde bazı middleware'leri sağlar. Ancak mevcut middleware'lerin çoğu doğrudan Starlette'ten gelir.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Gelen tüm request'lerin `https` veya `wss` olmasını zorunlu kılar.
`http` veya `ws` olarak gelen herhangi bir request, bunun yerine güvenli şemaya redirect edilir.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
HTTP Host Header saldırılarına karşı korunmak için, gelen tüm request'lerde `Host` header'ının doğru ayarlanmış olmasını zorunlu kılar.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Aşağıdaki argümanlar desteklenir:
* `allowed_hosts` - Hostname olarak izin verilmesi gereken domain adlarının listesi. `*.example.com` gibi wildcard domain'ler subdomain eşleştirmesi için desteklenir. Herhangi bir hostname'e izin vermek için `allowed_hosts=["*"]` kullanın veya middleware'i hiç eklemeyin.
* `www_redirect` - True olarak ayarlanırsa, izin verilen host'ların www olmayan sürümlerine gelen request'ler www sürümlerine redirect edilir. Varsayılanı `True`'dur.
Gelen bir request doğru şekilde doğrulanmazsa `400` response gönderilir.
## `GZipMiddleware` { #gzipmiddleware }
`Accept-Encoding` header'ında `"gzip"` içeren herhangi bir request için GZip response'larını yönetir.
Middleware hem standart hem de streaming response'ları ele alır.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Aşağıdaki argümanlar desteklenir:
* `minimum_size` - Bayt cinsinden bu minimum boyuttan küçük response'lara GZip uygulama. Varsayılanı `500`'dür.
* `compresslevel` - GZip sıkıştırması sırasında kullanılır. 1 ile 9 arasında bir tamsayıdır. Varsayılanı `9`'dur. Daha düşük değer daha hızlı sıkıştırma ama daha büyük dosya boyutları üretir; daha yüksek değer daha yavaş sıkıştırma ama daha küçük dosya boyutları üretir.
## Diğer middleware'ler { #other-middlewares }
Başka birçok ASGI middleware'i vardır.
Örneğin:
* [Uvicorn'un `ProxyHeadersMiddleware`'i](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
* [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Diğer mevcut middleware'leri görmek için [Starlette'in Middleware dokümanlarına](https://www.starlette.dev/middleware/) ve [ASGI Awesome List](https://github.com/florimondmanca/awesome-asgi) listesine bakın.
================================================
FILE: docs/tr/docs/advanced/openapi-callbacks.md
================================================
# OpenAPI Callback'leri { #openapi-callbacks }
Başka biri tarafından (muhtemelen API'nizi kullanacak olan aynı geliştirici tarafından) oluşturulmuş bir *external API*'ye request tetikleyebilen bir *path operation* ile bir API oluşturabilirsiniz.
API uygulamanızın *external API*'yi çağırdığı sırada gerçekleşen sürece "callback" denir. Çünkü dış geliştiricinin yazdığı yazılım API'nize bir request gönderir ve ardından API'niz geri çağrı yaparak (*call back*), bir *external API*'ye request gönderir (muhtemelen aynı geliştiricinin oluşturduğu).
Bu durumda, o external API'nin nasıl görünmesi gerektiğini dokümante etmek isteyebilirsiniz. Hangi *path operation*'a sahip olmalı, hangi body'yi beklemeli, hangi response'u döndürmeli, vb.
## Callback'leri olan bir uygulama { #an-app-with-callbacks }
Bunların hepsine bir örnekle bakalım.
Fatura oluşturmayı sağlayan bir uygulama geliştirdiğinizi düşünün.
Bu faturaların `id`, `title` (opsiyonel), `customer` ve `total` alanları olacak.
API'nizin kullanıcısı (external bir geliştirici) API'nizde bir POST request ile fatura oluşturacak.
Sonra API'niz (varsayalım ki):
* Faturayı external geliştiricinin bir müşterisine gönderir.
* Parayı tahsil eder.
* API kullanıcısına (external geliştiriciye) tekrar bir bildirim gönderir.
* Bu, external geliştiricinin sağladığı bir *external API*'ye (sizin API'nizden) bir POST request gönderilerek yapılır (işte bu "callback"tir).
## Normal **FastAPI** uygulaması { #the-normal-fastapi-app }
Önce callback eklemeden önce normal API uygulamasının nasıl görüneceğine bakalım.
Bir `Invoice` body alacak bir *path operation*'ı ve callback için URL'yi taşıyacak `callback_url` adlı bir query parametresi olacak.
Bu kısım oldukça standart; kodun çoğu muhtemelen size zaten tanıdık gelecektir:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | İpucu
`callback_url` query parametresi, Pydantic'in [Url](https://docs.pydantic.dev/latest/api/networks/) tipini kullanır.
///
Tek yeni şey, *path operation decorator*'ına argüman olarak verilen `callbacks=invoices_callback_router.routes`. Bunun ne olduğuna şimdi bakacağız.
## Callback'i dokümante etmek { #documenting-the-callback }
Callback'in gerçek kodu, büyük ölçüde sizin API uygulamanıza bağlıdır.
Ve bir uygulamadan diğerine oldukça değişebilir.
Sadece bir-iki satır kod bile olabilir, örneğin:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Ancak callback'in belki de en önemli kısmı, API'nizin kullanıcısının (external geliştiricinin) *external API*'yi doğru şekilde uyguladığından emin olmaktır; çünkü sizin API'niz callback'in request body'sinde belirli veriler gönderecektir, vb.
Dolayısıyla sıradaki adım olarak, sizin API'nizden callback almak için o *external API*'nin nasıl görünmesi gerektiğini dokümante eden kodu ekleyeceğiz.
Bu dokümantasyon, API'nizde `/docs` altındaki Swagger UI'da görünecek ve external geliştiricilere *external API*'yi nasıl inşa edeceklerini gösterecek.
Bu örnek callback'in kendisini implemente etmiyor (o zaten tek satır kod olabilir), sadece dokümantasyon kısmını ekliyor.
/// tip | İpucu
Gerçek callback, sadece bir HTTP request'tir.
Callback'i kendiniz implemente ederken [HTTPX](https://www.python-httpx.org) veya [Requests](https://requests.readthedocs.io/) gibi bir şey kullanabilirsiniz.
///
## Callback dokümantasyon kodunu yazın { #write-the-callback-documentation-code }
Bu kod uygulamanızda çalıştırılmayacak; sadece o *external API*'nin nasıl görünmesi gerektiğini dokümante etmek için gerekiyor.
Ancak **FastAPI** ile bir API için otomatik dokümantasyonu kolayca nasıl üreteceğinizi zaten biliyorsunuz.
O halde aynı bilgiyi kullanarak, *external API*'nin nasıl görünmesi gerektiğini dokümante edeceğiz... external API'nin implemente etmesi gereken *path operation*'ları oluşturarak (API'nizin çağıracağı olanlar).
/// tip | İpucu
Bir callback'i dokümante eden kodu yazarken, kendinizi external geliştirici olarak hayal etmek faydalı olabilir. Ve şu anda sizin API'nizi değil, *external API*'yi implemente ettiğinizi düşünün.
Bu bakış açısını (external geliştiricinin bakış açısını) geçici olarak benimsemek; parametreleri nereye koyacağınızı, body için Pydantic modelini, response için modelini vb. external API tarafında nasıl tasarlayacağınızı daha net hale getirebilir.
///
### Bir callback `APIRouter` oluşturun { #create-a-callback-apirouter }
Önce bir veya daha fazla callback içerecek yeni bir `APIRouter` oluşturun.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Callback *path operation*'ını oluşturun { #create-the-callback-path-operation }
Callback *path operation*'ını oluşturmak için, yukarıda oluşturduğunuz aynı `APIRouter`'ı kullanın.
Normal bir FastAPI *path operation*'ı gibi görünmelidir:
* Muhtemelen alması gereken body'nin bir deklarasyonu olmalı, örn. `body: InvoiceEvent`.
* Ayrıca döndürmesi gereken response'un deklarasyonu da olabilir, örn. `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Normal bir *path operation*'dan 2 temel farkı vardır:
* Gerçek bir koda ihtiyaç duymaz; çünkü uygulamanız bu kodu asla çağırmayacak. Bu yalnızca *external API*'yi dokümante etmek için kullanılır. Yani fonksiyon sadece `pass` içerebilir.
* *path*, bir [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (aşağıda daha fazlası) içerebilir; böylece parametreler ve sizin API'nize gönderilen orijinal request'in bazı parçalarıyla değişkenler kullanılabilir.
### Callback path ifadesi { #the-callback-path-expression }
Callback *path*'i, sizin API'nize gönderilen orijinal request'in bazı parçalarını içerebilen bir [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) barındırabilir.
Bu örnekte, bu bir `str`:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Yani API'nizin kullanıcısı (external geliştirici) sizin API'nize şu adrese bir request gönderirse:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
ve JSON body şu şekilde olursa:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
o zaman sizin API'niz faturayı işleyecek ve daha sonra bir noktada `callback_url`'ye (yani *external API*'ye) bir callback request gönderecek:
```
https://www.external.org/events/invoices/2expen51ve
```
ve JSON body yaklaşık şöyle bir şey içerecek:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
ve o *external API*'den şu gibi bir JSON body içeren response bekleyecek:
```JSON
{
"ok": true
}
```
/// tip | İpucu
Callback URL'sinin, `callback_url` içindeki query parametresi olarak alınan URL'yi (`https://www.external.org/events`) ve ayrıca JSON body'nin içindeki fatura `id`'sini (`2expen51ve`) birlikte kullandığına dikkat edin.
///
### Callback router'ını ekleyin { #add-the-callback-router }
Bu noktada, yukarıda oluşturduğunuz callback router'ında gerekli callback *path operation*'ları (external geliştiricinin *external API*'de implemente etmesi gerekenler) hazır.
Şimdi sizin API'nizin *path operation decorator*'ında `callbacks` parametresini kullanarak, callback router'ının `.routes` attribute'unu (bu aslında route/*path operation*'lardan oluşan bir `list`) geçin:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | İpucu
`callback=` içine router'ın kendisini (`invoices_callback_router`) değil, `invoices_callback_router.routes` şeklinde `.routes` attribute'unu verdiğinize dikkat edin.
///
### Dokümanları kontrol edin { #check-the-docs }
Artık uygulamanızı başlatıp [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresine gidebilirsiniz.
*Path operation*'ınız için, *external API*'nin nasıl görünmesi gerektiğini gösteren bir "Callbacks" bölümünü içeren dokümanları göreceksiniz:
================================================
FILE: docs/tr/docs/advanced/openapi-webhooks.md
================================================
# OpenAPI Webhook'lar { #openapi-webhooks }
Bazı durumlarda, API'nizi kullanan **kullanıcılara** uygulamanızın *onların* uygulamasını (request göndererek) bazı verilerle çağırabileceğini; genellikle bir tür **event** hakkında **bildirim** yapmak için kullanacağını söylemek istersiniz.
Bu da şunu ifade eder: Kullanıcılarınızın API'nize request göndermesi şeklindeki normal akış yerine, request'i **sizin API'niz** (veya uygulamanız) **onların sistemine** (onların API'sine, onların uygulamasına) **gönderebilir**.
Buna genellikle **webhook** denir.
## Webhook adımları { #webhooks-steps }
Süreç genellikle şöyledir: Kodunuzda göndereceğiniz mesajın ne olduğunu, yani request'in **body**'sini **siz tanımlarsınız**.
Ayrıca uygulamanızın bu request'leri veya event'leri hangi **anlarda** göndereceğini de bir şekilde tanımlarsınız.
Ve **kullanıcılarınız** da bir şekilde (örneğin bir web dashboard üzerinden) uygulamanızın bu request'leri göndermesi gereken **URL**'yi tanımlar.
Webhook'lar için URL'lerin nasıl kaydedileceğine dair tüm **mantık** ve bu request'leri gerçekten gönderen kod tamamen size bağlıdır. Bunu **kendi kodunuzda** istediğiniz gibi yazarsınız.
## **FastAPI** ve OpenAPI ile webhook'ları dokümante etmek { #documenting-webhooks-with-fastapi-and-openapi }
**FastAPI** ile OpenAPI kullanarak bu webhook'ların adlarını, uygulamanızın gönderebileceği HTTP operation türlerini (örn. `POST`, `PUT`, vb.) ve uygulamanızın göndereceği request **body**'lerini tanımlayabilirsiniz.
Bu, kullanıcılarınızın **webhook** request'lerinizi alacak şekilde **API'lerini implement etmesini** çok daha kolaylaştırabilir; hatta kendi API kodlarının bir kısmını otomatik üretebilirler.
/// info | Bilgi
Webhook'lar OpenAPI 3.1.0 ve üzeri sürümlerde mevcuttur; FastAPI `0.99.0` ve üzeri tarafından desteklenir.
///
## Webhook'ları olan bir uygulama { #an-app-with-webhooks }
Bir **FastAPI** uygulaması oluşturduğunuzda, *webhook*'ları tanımlamak için kullanabileceğiniz bir `webhooks` attribute'u vardır; *path operation* tanımlar gibi, örneğin `@app.webhooks.post()` ile.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Tanımladığınız webhook'lar **OpenAPI** şemasında ve otomatik **docs UI**'da yer alır.
/// info | Bilgi
`app.webhooks` nesnesi aslında sadece bir `APIRouter`'dır; uygulamanızı birden fazla dosya ile yapılandırırken kullanacağınız türün aynısıdır.
///
Dikkat edin: Webhook'larda aslında bir *path* (ör. `/items/`) deklare etmiyorsunuz; oraya verdiğiniz metin sadece webhook'un bir **identifier**'ıdır (event'in adı). Örneğin `@app.webhooks.post("new-subscription")` içinde webhook adı `new-subscription`'dır.
Bunun nedeni, webhook request'ini almak istedikleri gerçek **URL path**'i **kullanıcılarınızın** başka bir şekilde (örn. bir web dashboard üzerinden) tanımlamasının beklenmesidir.
### Dokümanları kontrol edin { #check-the-docs }
Şimdi uygulamanızı başlatıp [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresine gidin.
Dokümanlarınızda normal *path operation*'ları ve artık bazı **webhook**'ları da göreceksiniz:
================================================
FILE: docs/tr/docs/advanced/path-operation-advanced-configuration.md
================================================
# Path Operation İleri Düzey Yapılandırma { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | Uyarı
OpenAPI konusunda "uzman" değilseniz, muhtemelen buna ihtiyacınız yok.
///
*path operation*’ınızda kullanılacak OpenAPI `operationId` değerini `operation_id` parametresiyle ayarlayabilirsiniz.
Bunun her operation için benzersiz olduğundan emin olmanız gerekir.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### operationId olarak *path operation function* adını kullanma { #using-the-path-operation-function-name-as-the-operationid }
API’lerinizin function adlarını `operationId` olarak kullanmak istiyorsanız, hepsini dolaşıp her *path operation*’ın `operation_id` değerini `APIRoute.name` ile override edebilirsiniz.
Bunu, tüm *path operation*’ları ekledikten sonra yapmalısınız.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | İpucu
`app.openapi()` fonksiyonunu manuel olarak çağırıyorsanız, bunu yapmadan önce `operationId`’leri güncellemelisiniz.
///
/// warning | Uyarı
Bunu yaparsanız, her bir *path operation function*’ın adının benzersiz olduğundan emin olmanız gerekir.
Farklı modüllerde (Python dosyalarında) olsalar bile.
///
## OpenAPI’den Hariç Tutma { #exclude-from-openapi }
Bir *path operation*’ı üretilen OpenAPI şemasından (dolayısıyla otomatik dokümantasyon sistemlerinden) hariç tutmak için `include_in_schema` parametresini kullanın ve `False` yapın:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Docstring’den İleri Düzey Açıklama { #advanced-description-from-docstring }
OpenAPI için, bir *path operation function*’ın docstring’inden kullanılacak satırları sınırlandırabilirsiniz.
Bir `\f` (escape edilmiş "form feed" karakteri) eklerseniz, **FastAPI** OpenAPI için kullanılan çıktıyı bu noktada **keser**.
Dokümantasyonda görünmez, ancak diğer araçlar (Sphinx gibi) geri kalan kısmı kullanabilir.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Ek Responses { #additional-responses }
Muhtemelen bir *path operation* için `response_model` ve `status_code` tanımlamayı görmüşsünüzdür.
Bu, bir *path operation*’ın ana response’u ile ilgili metadata’yı tanımlar.
Ek response’ları; modelleri, status code’ları vb. ile birlikte ayrıca da tanımlayabilirsiniz.
Dokümantasyonda bununla ilgili ayrı bir bölüm var; [OpenAPI’de Ek Responses](additional-responses.md) sayfasından okuyabilirsiniz.
## OpenAPI Extra { #openapi-extra }
Uygulamanızda bir *path operation* tanımladığınızda, **FastAPI** OpenAPI şemasına dahil edilmek üzere o *path operation* ile ilgili metadata’yı otomatik olarak üretir.
/// note | Teknik Detaylar
OpenAPI spesifikasyonunda buna [Operation Nesnesi](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object) denir.
///
Bu, *path operation* hakkında tüm bilgileri içerir ve otomatik dokümantasyonu üretmek için kullanılır.
`tags`, `parameters`, `requestBody`, `responses` vb. alanları içerir.
Bu *path operation*’a özel OpenAPI şeması normalde **FastAPI** tarafından otomatik üretilir; ancak siz bunu genişletebilirsiniz.
/// tip | İpucu
Bu, düşük seviyeli bir genişletme noktasıdır.
Yalnızca ek response’lar tanımlamanız gerekiyorsa, bunu yapmanın daha pratik yolu [OpenAPI’de Ek Responses](additional-responses.md) kullanmaktır.
///
Bir *path operation* için OpenAPI şemasını `openapi_extra` parametresiyle genişletebilirsiniz.
### OpenAPI Extensions { #openapi-extensions }
Örneğin bu `openapi_extra`, [OpenAPI Uzantıları](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) tanımlamak için faydalı olabilir:
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Otomatik API dokümanlarını açtığınızda, extension’ınız ilgili *path operation*’ın en altında görünür.
Ayrıca ortaya çıkan OpenAPI’yi (API’nizde `/openapi.json`) görüntülerseniz, extension’ınızı ilgili *path operation*’ın bir parçası olarak orada da görürsünüz:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Özel OpenAPI *path operation* şeması { #custom-openapi-path-operation-schema }
`openapi_extra` içindeki dictionary, *path operation* için otomatik üretilen OpenAPI şemasıyla derinlemesine (deep) birleştirilir.
Böylece otomatik üretilen şemaya ek veri ekleyebilirsiniz.
Örneğin, Pydantic ile FastAPI’nin otomatik özelliklerini kullanmadan request’i kendi kodunuzla okuyup doğrulamaya karar verebilirsiniz; ancak yine de OpenAPI şemasında request’i tanımlamak isteyebilirsiniz.
Bunu `openapi_extra` ile yapabilirsiniz:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
Bu örnekte herhangi bir Pydantic model tanımlamadık. Hatta request body JSON olarak ayrıştırılmıyor; doğrudan `bytes` olarak okunuyor ve `magic_data_reader()` fonksiyonu bunu bir şekilde parse etmekten sorumlu oluyor.
Buna rağmen, request body için beklenen şemayı tanımlayabiliriz.
### Özel OpenAPI content type { #custom-openapi-content-type }
Aynı yöntemi kullanarak, Pydantic model ile JSON Schema’yı tanımlayıp bunu *path operation* için özel OpenAPI şeması bölümüne dahil edebilirsiniz.
Ve bunu, request içindeki veri tipi JSON olmasa bile yapabilirsiniz.
Örneğin bu uygulamada, FastAPI’nin Pydantic modellerinden JSON Schema çıkarmaya yönelik entegre işlevselliğini ve JSON için otomatik doğrulamayı kullanmıyoruz. Hatta request content type’ını JSON değil, YAML olarak tanımlıyoruz:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Buna rağmen, varsayılan entegre işlevselliği kullanmasak da, YAML olarak almak istediğimiz veri için JSON Schema’yı manuel üretmek üzere bir Pydantic model kullanmaya devam ediyoruz.
Ardından request’i doğrudan kullanıp body’yi `bytes` olarak çıkarıyoruz. Bu da FastAPI’nin request payload’ını JSON olarak parse etmeye çalışmayacağı anlamına gelir.
Sonrasında kodumuzda bu YAML içeriğini doğrudan parse ediyor, ardından YAML içeriğini doğrulamak için yine aynı Pydantic modeli kullanıyoruz:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | İpucu
Burada aynı Pydantic modeli tekrar kullanıyoruz.
Aynı şekilde, başka bir yöntemle de doğrulama yapabilirdik.
///
================================================
FILE: docs/tr/docs/advanced/response-change-status-code.md
================================================
# Response - Status Code Değiştirme { #response-change-status-code }
Muhtemelen daha önce varsayılan bir [Response Status Code](../tutorial/response-status-code.md) ayarlayabileceğinizi okumuşsunuzdur.
Ancak bazı durumlarda, varsayılandan farklı bir status code döndürmeniz gerekir.
## Kullanım senaryosu { #use-case }
Örneğin, varsayılan olarak "OK" `200` HTTP status code'u döndürmek istediğinizi düşünün.
Ama veri mevcut değilse onu oluşturmak ve "CREATED" `201` HTTP status code'u döndürmek istiyorsunuz.
Aynı zamanda, döndürdüğünüz veriyi bir `response_model` ile filtreleyip dönüştürebilmeyi de sürdürmek istiyorsunuz.
Bu tür durumlarda bir `Response` parametresi kullanabilirsiniz.
## Bir `Response` parametresi kullanın { #use-a-response-parameter }
*Path operation function* içinde `Response` tipinde bir parametre tanımlayabilirsiniz (cookie ve header'lar için yapabildiğiniz gibi).
Ardından bu *geçici (temporal)* `Response` nesnesi üzerinde `status_code` değerini ayarlayabilirsiniz.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Sonrasında, normalde yaptığınız gibi ihtiyacınız olan herhangi bir nesneyi döndürebilirsiniz (`dict`, bir veritabanı modeli, vb.).
Ve eğer bir `response_model` tanımladıysanız, döndürdüğünüz nesneyi filtrelemek ve dönüştürmek için yine kullanılacaktır.
**FastAPI**, status code'u (ayrıca cookie ve header'ları) bu *geçici (temporal)* response'tan alır ve `response_model` ile filtrelenmiş, sizin döndürdüğünüz değeri içeren nihai response'a yerleştirir.
Ayrıca `Response` parametresini dependency'lerde de tanımlayıp status code'u orada ayarlayabilirsiniz. Ancak unutmayın, en son ayarlanan değer geçerli olur.
================================================
FILE: docs/tr/docs/advanced/response-cookies.md
================================================
# Response Cookie'leri { #response-cookies }
## Bir `Response` parametresi kullanın { #use-a-response-parameter }
*Path operation function* içinde `Response` tipinde bir parametre tanımlayabilirsiniz.
Ardından bu *geçici* response nesnesi üzerinde cookie'leri set edebilirsiniz.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Sonrasında normalde yaptığınız gibi ihtiyaç duyduğunuz herhangi bir nesneyi döndürebilirsiniz (bir `dict`, bir veritabanı modeli vb.).
Ayrıca bir `response_model` tanımladıysanız, döndürdüğünüz nesneyi filtrelemek ve dönüştürmek için yine kullanılacaktır.
**FastAPI**, bu *geçici* response'u cookie'leri (ayrıca header'ları ve status code'u) çıkarmak için kullanır ve bunları, döndürdüğünüz değeri içeren nihai response'a ekler. Döndürdüğünüz değer, varsa `response_model` ile filtrelenmiş olur.
`Response` parametresini dependency'lerde de tanımlayıp, onların içinde cookie (ve header) set edebilirsiniz.
## Doğrudan bir `Response` döndürün { #return-a-response-directly }
Kodunuzda doğrudan bir `Response` döndürürken de cookie oluşturabilirsiniz.
Bunu yapmak için, [Doğrudan Response Döndürme](response-directly.md) bölümünde anlatıldığı gibi bir response oluşturabilirsiniz.
Sonra bunun içinde Cookie'leri set edin ve response'u döndürün:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip
`Response` parametresini kullanmak yerine doğrudan bir response döndürürseniz, FastAPI onu olduğu gibi (doğrudan) döndürür.
Bu yüzden, verinizin doğru tipte olduğundan emin olmanız gerekir. Örneğin `JSONResponse` döndürüyorsanız, verinin JSON ile uyumlu olması gerekir.
Ayrıca `response_model` tarafından filtrelenmesi gereken bir veriyi göndermediğinizden de emin olun.
///
### Daha fazla bilgi { #more-info }
/// note | Teknik Detaylar
`from starlette.responses import Response` veya `from starlette.responses import JSONResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olması için `fastapi.responses` içinde `starlette.responses` ile aynı response sınıflarını sunar. Ancak mevcut response'ların büyük kısmı doğrudan Starlette'ten gelir.
Ve `Response`, header ve cookie set etmek için sık kullanıldığından, **FastAPI** bunu `fastapi.Response` olarak da sağlar.
///
Mevcut tüm parametreleri ve seçenekleri görmek için [Starlette dokümantasyonu](https://www.starlette.dev/responses/#set-cookie)'na bakın.
================================================
FILE: docs/tr/docs/advanced/response-directly.md
================================================
# Doğrudan Bir Response Döndürme { #return-a-response-directly }
**FastAPI** ile bir *path operation* oluşturduğunuzda, normalde ondan herhangi bir veri döndürebilirsiniz: bir `dict`, bir `list`, bir Pydantic model, bir veritabanı modeli vb.
Bir [Response Model](../tutorial/response-model.md) deklare ederseniz, FastAPI veriyi Pydantic kullanarak JSON'a serialize etmek için bunu kullanır.
Bir response model deklare etmezseniz, FastAPI [JSON Uyumlu Encoder](../tutorial/encoder.md)'da anlatılan `jsonable_encoder`'ı kullanır ve bunu bir `JSONResponse` içine koyar.
Ayrıca doğrudan bir `JSONResponse` oluşturup döndürebilirsiniz.
/// tip | İpucu
[Response Model](../tutorial/response-model.md) kullanmak, doğrudan `JSONResponse` döndürmeye kıyasla genellikle çok daha iyi performans sağlar; çünkü veriyi Pydantic ile, Rust tarafında serialize eder.
///
## Bir `Response` Döndürme { #return-a-response }
Aslında herhangi bir `Response` veya onun herhangi bir alt sınıfını döndürebilirsiniz.
/// info | Bilgi
`JSONResponse` zaten `Response`'un bir alt sınıfıdır.
///
Bir `Response` döndürdüğünüzde, **FastAPI** bunu olduğu gibi doğrudan iletir.
Pydantic model'leriyle herhangi bir veri dönüşümü yapmaz, içeriği başka bir tipe çevirmez vb.
Bu size ciddi bir esneklik sağlar. Herhangi bir veri türü döndürebilir, herhangi bir veri deklarasyonunu veya validasyonunu override edebilirsiniz.
Bu aynı zamanda size ciddi bir sorumluluk yükler. Döndürdüğünüz verinin doğru, doğru formatta, serialize edilebilir vb. olduğundan emin olmanız gerekir.
## Bir `Response` İçinde `jsonable_encoder` Kullanma { #using-the-jsonable-encoder-in-a-response }
**FastAPI**, sizin döndürdüğünüz `Response` üzerinde hiçbir değişiklik yapmadığı için, içeriğinin gönderilmeye hazır olduğundan emin olmanız gerekir.
Örneğin, bir Pydantic model'i, önce JSON-uyumlu tiplere çevrilmeden (`datetime`, `UUID` vb.) doğrudan bir `JSONResponse` içine koyamazsınız. Önce tüm veri tipleri JSON-uyumlu hale gelecek şekilde `dict`'e çevrilmesi gerekir.
Bu gibi durumlarda, response'a vermeden önce verinizi dönüştürmek için `jsonable_encoder` kullanabilirsiniz:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Teknik Detaylar
`from starlette.responses import JSONResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olması için `starlette.responses` içeriğini `fastapi.responses` üzerinden de sunar. Ancak mevcut response'ların çoğu doğrudan Starlette'tan gelir.
///
## Özel Bir `Response` Döndürme { #returning-a-custom-response }
Yukarıdaki örnek ihtiyaç duyduğunuz tüm parçaları gösteriyor, ancak henüz çok kullanışlı değil. Çünkü `item`'ı zaten doğrudan döndürebilirdiniz ve **FastAPI** varsayılan olarak onu sizin için bir `JSONResponse` içine koyup `dict`'e çevirirdi vb.
Şimdi bunu kullanarak nasıl özel bir response döndürebileceğinize bakalım.
Diyelim ki [XML](https://en.wikipedia.org/wiki/XML) response döndürmek istiyorsunuz.
XML içeriğinizi bir string içine koyabilir, onu bir `Response` içine yerleştirip döndürebilirsiniz:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Bir Response Model Nasıl Çalışır { #how-a-response-model-works }
Bir path operation içinde [Response Model - Dönüş Tipi](../tutorial/response-model.md) deklare ettiğinizde, **FastAPI** veriyi Pydantic kullanarak JSON'a serialize etmek için bunu kullanır.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Bu işlem Rust tarafında gerçekleştiği için, sıradan Python ve `JSONResponse` sınıfıyla yapılmasına kıyasla performans çok daha iyi olacaktır.
Bir `response_model` veya dönüş tipi kullandığınızda, FastAPI veriyi dönüştürmek için (daha yavaş olacağı için) `jsonable_encoder`'ı ya da `JSONResponse` sınıfını kullanmaz.
Bunun yerine, response model'i (veya dönüş tipini) kullanarak Pydantic ile üretilen JSON baytlarını alır ve doğrudan JSON için doğru medya tipiyle (`application/json`) bir `Response` döndürür.
## Notlar { #notes }
Bir `Response`'u doğrudan döndürdüğünüzde, verisi otomatik olarak validate edilmez, dönüştürülmez (serialize edilmez) veya dokümante edilmez.
Ancak yine de [OpenAPI'de Ek Response'lar](additional-responses.md) bölümünde anlatıldığı şekilde dokümante edebilirsiniz.
İlerleyen bölümlerde, otomatik veri dönüşümü, dokümantasyon vb. özellikleri korurken bu özel `Response`'ları nasıl kullanıp declare edebileceğinizi göreceksiniz.
================================================
FILE: docs/tr/docs/advanced/response-headers.md
================================================
# Response Header'ları { #response-headers }
## Bir `Response` parametresi kullanın { #use-a-response-parameter }
*Path operation function* içinde (cookie'lerde yapabildiğiniz gibi) tipi `Response` olan bir parametre tanımlayabilirsiniz.
Sonra da bu *geçici* response nesnesi üzerinde header'ları ayarlayabilirsiniz.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Ardından normalde yaptığınız gibi ihtiyacınız olan herhangi bir nesneyi döndürebilirsiniz (bir `dict`, bir veritabanı modeli vb.).
Eğer bir `response_model` tanımladıysanız, döndürdüğünüz nesneyi filtrelemek ve dönüştürmek için yine kullanılacaktır.
**FastAPI**, header'ları (aynı şekilde cookie'leri ve status code'u) bu *geçici* response'dan alır ve döndürdüğünüz değeri (varsa bir `response_model` ile filtrelenmiş hâliyle) içeren nihai response'a ekler.
`Response` parametresini dependency'lerde de tanımlayıp, onların içinde header (ve cookie) ayarlayabilirsiniz.
## Doğrudan bir `Response` döndürün { #return-a-response-directly }
Doğrudan bir `Response` döndürdüğünüzde de header ekleyebilirsiniz.
[Bir Response'u Doğrudan Döndürün](response-directly.md) bölümünde anlatıldığı gibi bir response oluşturun ve header'ları ek bir parametre olarak geçin:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Teknik Detaylar
`from starlette.responses import Response` veya `from starlette.responses import JSONResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.responses` içeriğini `fastapi.responses` olarak da sunar. Ancak mevcut response'ların çoğu doğrudan Starlette'ten gelir.
Ayrıca `Response` header ve cookie ayarlamak için sık kullanıldığından, **FastAPI** bunu `fastapi.Response` altında da sağlar.
///
## Özel Header'lar { #custom-headers }
Özel/proprietary header'ların [`X-` prefix'i kullanılarak](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) eklenebileceğini unutmayın.
Ancak tarayıcıdaki bir client'ın görebilmesini istediğiniz özel header'larınız varsa, bunları CORS ayarlarınıza eklemeniz gerekir ([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md) bölümünde daha fazla bilgi), bunun için [Starlette'in CORS dokümanında](https://www.starlette.dev/middleware/#corsmiddleware) açıklanan `expose_headers` parametresini kullanın.
================================================
FILE: docs/tr/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
En basit senaryolarda HTTP Basic Auth kullanabilirsiniz.
HTTP Basic Auth’ta uygulama, içinde kullanıcı adı ve şifre bulunan bir header bekler.
Eğer bunu almazsa HTTP 401 "Unauthorized" hatası döndürür.
Ayrıca değeri `Basic` olan ve isteğe bağlı `realm` parametresi içerebilen `WWW-Authenticate` header’ını da döndürür.
Bu da tarayıcıya, kullanıcı adı ve şifre için entegre giriş penceresini göstermesini söyler.
Ardından kullanıcı adı ve şifreyi yazdığınızda tarayıcı bunları otomatik olarak header içinde gönderir.
## Basit HTTP Basic Auth { #simple-http-basic-auth }
* `HTTPBasic` ve `HTTPBasicCredentials` import edin.
* `HTTPBasic` kullanarak bir "`security` scheme" oluşturun.
* *path operation*’ınızda bir dependency ile bu `security`’yi kullanın.
* Bu, `HTTPBasicCredentials` tipinde bir nesne döndürür:
* İçinde gönderilen `username` ve `password` bulunur.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
URL’yi ilk kez açmaya çalıştığınızda (veya dokümanlardaki "Execute" butonuna tıkladığınızda) tarayıcı sizden kullanıcı adınızı ve şifrenizi ister:
## Kullanıcı adını kontrol edin { #check-the-username }
Daha kapsamlı bir örneğe bakalım.
Kullanıcı adı ve şifrenin doğru olup olmadığını kontrol etmek için bir dependency kullanın.
Bunun için kullanıcı adı ve şifreyi kontrol ederken Python standart modülü olan [`secrets`](https://docs.python.org/3/library/secrets.html)'i kullanın.
`secrets.compare_digest()`; `bytes` ya da yalnızca ASCII karakterleri (İngilizce’deki karakterler) içeren bir `str` almalıdır. Bu da `Sebastián` içindeki `á` gibi karakterlerle çalışmayacağı anlamına gelir.
Bunu yönetmek için önce `username` ve `password` değerlerini UTF-8 ile encode ederek `bytes`’a dönüştürürüz.
Sonra `secrets.compare_digest()` kullanarak `credentials.username`’in `"stanleyjobson"` ve `credentials.password`’ün `"swordfish"` olduğundan emin olabiliriz.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Bu, kabaca şuna benzer olurdu:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Bir hata döndür
...
```
Ancak `secrets.compare_digest()` kullanarak, "timing attacks" denilen bir saldırı türüne karşı güvenli olursunuz.
### Timing Attacks { #timing-attacks }
Peki "timing attack" nedir?
Bazı saldırganların kullanıcı adı ve şifreyi tahmin etmeye çalıştığını düşünelim.
Ve `johndoe` kullanıcı adı ve `love123` şifresi ile bir request gönderiyorlar.
Uygulamanızdaki Python kodu o zaman kabaca şuna denk olur:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Ancak Python, `johndoe` içindeki ilk `j` ile `stanleyjobson` içindeki ilk `s`’i karşılaştırdığı anda `False` döndürür; çünkü iki string’in aynı olmadığını zaten anlar ve "kalan harfleri karşılaştırmak için daha fazla hesaplama yapmaya gerek yok" diye düşünür. Uygulamanız da "Incorrect username or password" der.
Sonra saldırganlar bu sefer `stanleyjobsox` kullanıcı adı ve `love123` şifresi ile dener.
Uygulama kodunuz da şuna benzer bir şey yapar:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Bu kez Python, iki string’in aynı olmadığını fark etmeden önce hem `stanleyjobsox` hem de `stanleyjobson` içinde `stanleyjobso` kısmının tamamını karşılaştırmak zorunda kalır. Bu nedenle "Incorrect username or password" yanıtını vermesi birkaç mikro saniye daha uzun sürer.
#### Yanıt süresi saldırganlara yardımcı olur { #the-time-to-answer-helps-the-attackers }
Bu noktada saldırganlar, server’ın "Incorrect username or password" response’unu göndermesinin birkaç mikro saniye daha uzun sürdüğünü fark ederek _bir şeyleri_ doğru yaptıklarını anlar; yani başlangıçtaki bazı harfler doğrudur.
Sonra tekrar denerken, bunun `johndoe`’dan ziyade `stanleyjobsox`’a daha yakın bir şey olması gerektiğini bilerek devam edebilirler.
#### "Profesyonel" bir saldırı { #a-professional-attack }
Elbette saldırganlar bunu elle tek tek denemez; bunu yapan bir program yazarlar. Muhtemelen saniyede binlerce ya da milyonlarca test yaparlar ve her seferinde yalnızca bir doğru harf daha elde ederler.
Böylece birkaç dakika ya da birkaç saat içinde doğru kullanıcı adı ve şifreyi, yanıt süresini kullanarak ve uygulamamızın "yardımıyla" tahmin etmiş olurlar.
#### `secrets.compare_digest()` ile düzeltin { #fix-it-with-secrets-compare-digest }
Ancak bizim kodumuzda `secrets.compare_digest()` kullanıyoruz.
Kısacası, `stanleyjobsox` ile `stanleyjobson`’u karşılaştırmak için geçen süre, `johndoe` ile `stanleyjobson`’u karşılaştırmak için geçen süreyle aynı olur. Şifre için de aynı şekilde.
Bu sayede uygulama kodunuzda `secrets.compare_digest()` kullanarak bu güvenlik saldırıları ailesine karşı güvenli olursunuz.
### Hatayı döndürün { #return-the-error }
Credential’ların hatalı olduğunu tespit ettikten sonra, 401 status code ile (credential verilmediğinde dönenle aynı) bir `HTTPException` döndürün ve tarayıcının giriş penceresini yeniden göstermesi için `WWW-Authenticate` header’ını ekleyin:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/tr/docs/advanced/security/index.md
================================================
# Gelişmiş Güvenlik { #advanced-security }
## Ek Özellikler { #additional-features }
[Öğretici - Kullanıcı Kılavuzu: Güvenlik](../../tutorial/security/index.md) sayfasında ele alınanların dışında güvenlikle ilgili bazı ek özellikler vardır.
/// tip | İpucu
Sonraki bölümler **mutlaka "gelişmiş" olmak zorunda değildir**.
Ve kullanım durumunuza göre, çözüm bu bölümlerden birinde olabilir.
///
## Önce Öğreticiyi Okuyun { #read-the-tutorial-first }
Sonraki bölümler, ana [Öğretici - Kullanıcı Kılavuzu: Güvenlik](../../tutorial/security/index.md) sayfasını zaten okuduğunuzu varsayar.
Hepsi aynı kavramlara dayanır, ancak bazı ek işlevselliklere izin verir.
================================================
FILE: docs/tr/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2 scope'ları { #oauth2-scopes }
OAuth2 scope'larını **FastAPI** ile doğrudan kullanabilirsiniz; sorunsuz çalışacak şekilde entegre edilmiştir.
Bu sayede OAuth2 standardını takip eden, daha ince taneli bir izin sistemini OpenAPI uygulamanıza (ve API dokümanlarınıza) entegre edebilirsiniz.
Scope'lu OAuth2; Facebook, Google, GitHub, Microsoft, X (Twitter) vb. birçok büyük kimlik doğrulama sağlayıcısının kullandığı mekanizmadır. Kullanıcı ve uygulamalara belirli izinler vermek için bunu kullanırlar.
Facebook, Google, GitHub, Microsoft, X (Twitter) ile "giriş yaptığınızda", o uygulama scope'lu OAuth2 kullanıyor demektir.
Bu bölümde, **FastAPI** uygulamanızda aynı scope'lu OAuth2 ile authentication ve authorization'ı nasıl yöneteceğinizi göreceksiniz.
/// warning | Uyarı
Bu bölüm az çok ileri seviye sayılır. Yeni başlıyorsanız atlayabilirsiniz.
OAuth2 scope'larına mutlaka ihtiyacınız yok; authentication ve authorization'ı istediğiniz şekilde ele alabilirsiniz.
Ancak scope'lu OAuth2, API'nize (OpenAPI ile) ve API dokümanlarınıza güzel biçimde entegre edilebilir.
Buna rağmen, bu scope'ları (veya başka herhangi bir security/authorization gereksinimini) kodunuzda ihtiyaç duyduğunuz şekilde yine siz zorunlu kılarsınız.
Birçok durumda scope'lu OAuth2 gereğinden fazla (overkill) olabilir.
Ama ihtiyacınız olduğunu biliyorsanız ya da merak ediyorsanız okumaya devam edin.
///
## OAuth2 scope'ları ve OpenAPI { #oauth2-scopes-and-openapi }
OAuth2 spesifikasyonu, "scope"ları boşluklarla ayrılmış string'lerden oluşan bir liste olarak tanımlar.
Bu string'lerin her birinin içeriği herhangi bir formatta olabilir, ancak boşluk içermemelidir.
Bu scope'lar "izinleri" temsil eder.
OpenAPI'de (ör. API dokümanlarında) "security scheme" tanımlayabilirsiniz.
Bu security scheme'lerden biri OAuth2 kullanıyorsa, scope'ları da tanımlayıp kullanabilirsiniz.
Her bir "scope" sadece bir string'dir (boşluksuz).
Genellikle belirli güvenlik izinlerini tanımlamak için kullanılır, örneğin:
* `users:read` veya `users:write` sık görülen örneklerdir.
* `instagram_basic` Facebook / Instagram tarafından kullanılır.
* `https://www.googleapis.com/auth/drive` Google tarafından kullanılır.
/// info | Bilgi
OAuth2'de "scope", gereken belirli bir izni bildiren bir string'den ibarettir.
`:` gibi başka karakterler içermesi ya da bir URL olması önemli değildir.
Bu detaylar implementasyon'a bağlıdır.
OAuth2 için bunlar sadece string'dir.
///
## Genel görünüm { #global-view }
Önce, ana **Tutorial - User Guide** içindeki [Password (ve hashing) ile OAuth2, JWT token'lı Bearer](../../tutorial/security/oauth2-jwt.md) örneklerinden, OAuth2 scope'larına geçince hangi kısımların değiştiğine hızlıca bakalım:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Şimdi bu değişiklikleri adım adım inceleyelim.
## OAuth2 Security scheme { #oauth2-security-scheme }
İlk değişiklik, artık OAuth2 security scheme'ini iki adet kullanılabilir scope ile tanımlamamız: `me` ve `items`.
`scopes` parametresi; her scope'un key, açıklamasının ise value olduğu bir `dict` alır:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Bu scope'ları tanımladığımız için, login/authorize yaptığınızda API dokümanlarında görünecekler.
Ve hangi scope'lara erişim vermek istediğinizi seçebileceksiniz: `me` ve `items`.
Bu, Facebook/Google/GitHub vb. ile giriş yaparken izin verdiğinizde kullanılan mekanizmanın aynısıdır:
## Scope'lu JWT token { #jwt-token-with-scopes }
Şimdi token *path operation*'ını, istenen scope'ları döndürecek şekilde değiştirin.
Hâlâ aynı `OAuth2PasswordRequestForm` kullanılıyor. Bu form, request'te aldığı her scope için `str`'lerden oluşan bir `list` içeren `scopes` özelliğine sahiptir.
Ve scope'ları JWT token'ın bir parçası olarak döndürüyoruz.
/// danger | Uyarı
Basitlik için burada, gelen scope'ları doğrudan token'a ekliyoruz.
Ama uygulamanızda güvenlik açısından, yalnızca kullanıcının gerçekten sahip olabileceği scope'ları (veya sizin önceden tanımladıklarınızı) eklediğinizden emin olmalısınız.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## *Path operation*'larda ve dependency'lerde scope tanımlama { #declare-scopes-in-path-operations-and-dependencies }
Artık `/users/me/items/` için olan *path operation*'ın `items` scope'unu gerektirdiğini tanımlıyoruz.
Bunun için `fastapi` içinden `Security` import edip kullanıyoruz.
Dependency'leri (`Depends` gibi) tanımlamak için `Security` kullanabilirsiniz; fakat `Security`, ayrıca string'lerden oluşan bir scope listesi alan `scopes` parametresini de alır.
Bu durumda `Security`'ye dependency fonksiyonu olarak `get_current_active_user` veriyoruz (`Depends` ile yaptığımız gibi).
Ama ayrıca bir `list` olarak scope'ları da veriyoruz; burada tek bir scope var: `items` (daha fazla da olabilir).
Ve `get_current_active_user` dependency fonksiyonu, sadece `Depends` ile değil `Security` ile de alt-dependency'ler tanımlayabilir. Kendi alt-dependency fonksiyonunu (`get_current_user`) ve daha fazla scope gereksinimini tanımlar.
Bu örnekte `me` scope'unu gerektiriyor (birden fazla scope da isteyebilirdi).
/// note | Not
Farklı yerlerde farklı scope'lar eklemek zorunda değilsiniz.
Burada, **FastAPI**'nin farklı seviyelerde tanımlanan scope'ları nasıl ele aldığını göstermek için böyle yapıyoruz.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Teknik Detaylar
`Security` aslında `Depends`'in bir alt sınıfıdır ve sadece birazdan göreceğimiz bir ek parametreye sahiptir.
Ancak `Depends` yerine `Security` kullanınca **FastAPI**, security scope'larının tanımlanabileceğini bilir, bunları içeride kullanır ve API'yi OpenAPI ile dokümante eder.
Fakat `fastapi` içinden `Query`, `Path`, `Depends`, `Security` vb. import ettiğiniz şeyler, aslında özel sınıflar döndüren fonksiyonlardır.
///
## `SecurityScopes` kullanımı { #use-securityscopes }
Şimdi `get_current_user` dependency'sini güncelleyelim.
Bu fonksiyon, yukarıdaki dependency'ler tarafından kullanılıyor.
Burada, daha önce oluşturduğumuz aynı OAuth2 scheme'i dependency olarak tanımlıyoruz: `oauth2_scheme`.
Bu dependency fonksiyonunun kendi içinde bir scope gereksinimi olmadığı için, `oauth2_scheme` ile `Depends` kullanabiliriz; security scope'larını belirtmemiz gerekmiyorsa `Security` kullanmak zorunda değiliz.
Ayrıca `fastapi.security` içinden import edilen, `SecurityScopes` tipinde özel bir parametre tanımlıyoruz.
Bu `SecurityScopes` sınıfı, `Request`'e benzer (`Request`, request nesnesini doğrudan almak için kullanılmıştı).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## `scopes`'ları kullanma { #use-the-scopes }
`security_scopes` parametresi `SecurityScopes` tipinde olacaktır.
Bu nesnenin `scopes` adlı bir özelliği vardır; bu liste, kendisinin ve bunu alt-dependency olarak kullanan tüm dependency'lerin gerektirdiği tüm scope'ları içerir. Yani tüm "dependant"lar... kafa karıştırıcı gelebilir; aşağıda tekrar açıklanıyor.
`security_scopes` nesnesi (`SecurityScopes` sınıfından) ayrıca, bu scope'ları boşluklarla ayrılmış tek bir string olarak veren `scope_str` attribute'una sahiptir (bunu kullanacağız).
Sonrasında birkaç farklı noktada tekrar kullanabileceğimiz (`raise` edebileceğimiz) bir `HTTPException` oluşturuyoruz.
Bu exception içinde, gerekiyorsa, gerekli scope'ları boşlukla ayrılmış bir string olarak (`scope_str` ile) ekliyoruz. Bu scope'ları içeren string'i `WWW-Authenticate` header'ına koyuyoruz (spesifikasyonun bir parçası).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## `username` ve veri şeklinin doğrulanması { #verify-the-username-and-data-shape }
Bir `username` aldığımızı doğruluyoruz ve scope'ları çıkarıyoruz.
Ardından bu veriyi Pydantic model'i ile doğruluyoruz (`ValidationError` exception'ını yakalayarak). JWT token'ı okurken veya Pydantic ile veriyi doğrularken bir hata olursa, daha önce oluşturduğumuz `HTTPException`'ı fırlatıyoruz.
Bunun için Pydantic model'i `TokenData`'yı, `scopes` adlı yeni bir özellik ekleyerek güncelliyoruz.
Veriyi Pydantic ile doğrulayarak örneğin scope'ların tam olarak `str`'lerden oluşan bir `list` olduğunu ve `username`'in bir `str` olduğunu garanti edebiliriz.
Aksi halde, örneğin bir `dict` veya başka bir şey gelebilir; bu da daha sonra uygulamanın bir yerinde kırılmaya yol açıp güvenlik riski oluşturabilir.
Ayrıca bu `username` ile bir kullanıcı olduğunu doğruluyoruz; yoksa yine aynı exception'ı fırlatıyoruz.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## `scopes`'ların doğrulanması { #verify-the-scopes }
Şimdi bu dependency'nin ve tüm dependant'ların ( *path operation*'lar dahil) gerektirdiği tüm scope'ların, alınan token'da sağlanan scope'lar içinde olup olmadığını doğruluyoruz; değilse `HTTPException` fırlatıyoruz.
Bunun için, tüm bu scope'ları `str` olarak içeren bir `list` olan `security_scopes.scopes` kullanılır.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Dependency ağacı ve scope'lar { #dependency-tree-and-scopes }
Bu dependency ağacını ve scope'ları tekrar gözden geçirelim.
`get_current_active_user` dependency'si, alt-dependency olarak `get_current_user`'ı kullandığı için, `get_current_active_user` üzerinde tanımlanan `"me"` scope'u, `get_current_user`'a geçirilen `security_scopes.scopes` içindeki gerekli scope listesine dahil edilir.
*Path operation*'ın kendisi de `"items"` scope'unu tanımlar; bu da `get_current_user`'a geçirilen `security_scopes.scopes` listesinde yer alır.
Dependency'lerin ve scope'ların hiyerarşisi şöyle görünür:
* *Path operation* `read_own_items` şunlara sahiptir:
* Dependency ile gerekli scope'lar `["items"]`:
* `get_current_active_user`:
* `get_current_active_user` dependency fonksiyonu şunlara sahiptir:
* Dependency ile gerekli scope'lar `["me"]`:
* `get_current_user`:
* `get_current_user` dependency fonksiyonu şunlara sahiptir:
* Kendisinin gerektirdiği scope yok.
* `oauth2_scheme` kullanan bir dependency.
* `SecurityScopes` tipinde bir `security_scopes` parametresi:
* Bu `security_scopes` parametresinin `scopes` adlı bir özelliği vardır ve yukarıda tanımlanan tüm scope'ları içeren bir `list` taşır, yani:
* *Path operation* `read_own_items` için `security_scopes.scopes` `["me", "items"]` içerir.
* *Path operation* `read_users_me` için `security_scopes.scopes` `["me"]` içerir; çünkü bu scope `get_current_active_user` dependency'sinde tanımlanmıştır.
* *Path operation* `read_system_status` için `security_scopes.scopes` `[]` (boş) olur; çünkü herhangi bir `Security` ile `scopes` tanımlamamıştır ve dependency'si olan `get_current_user` da `scopes` tanımlamaz.
/// tip | İpucu
Buradaki önemli ve "sihirli" nokta şu: `get_current_user`, her *path operation* için kontrol etmesi gereken farklı bir `scopes` listesi alır.
Bu, belirli bir *path operation* için dependency ağacındaki her *path operation* ve her dependency üzerinde tanımlanan `scopes`'lara bağlıdır.
///
## `SecurityScopes` hakkında daha fazla detay { #more-details-about-securityscopes }
`SecurityScopes`'u herhangi bir noktada ve birden fazla yerde kullanabilirsiniz; mutlaka "kök" dependency'de olmak zorunda değildir.
Her zaman, **o spesifik** *path operation* ve **o spesifik** dependency ağacı için, mevcut `Security` dependency'lerinde ve tüm dependant'larda tanımlanan security scope'larını içerir.
`SecurityScopes`, dependant'ların tanımladığı tüm scope'ları barındırdığı için, gereken scope'ların token'da olup olmadığını merkezi bir dependency fonksiyonunda doğrulayıp, farklı *path operation*'larda farklı scope gereksinimleri tanımlayabilirsiniz.
Bu kontroller her *path operation* için bağımsız yapılır.
## Deneyin { #check-it }
API dokümanlarını açarsanız, authenticate olup hangi scope'ları authorize etmek istediğinizi seçebilirsiniz.
Hiç scope seçmezseniz "authenticated" olursunuz; ancak `/users/me/` veya `/users/me/items/`'e erişmeye çalıştığınızda, yeterli izniniz olmadığını söyleyen bir hata alırsınız. Yine de `/status/`'a erişebilirsiniz.
`me` scope'unu seçip `items` scope'unu seçmezseniz `/users/me/`'a erişebilirsiniz ama `/users/me/items/`'e erişemezsiniz.
Bu, bir üçüncü taraf uygulamanın, bir kullanıcı tarafından sağlanan token ile bu *path operation*'lardan birine erişmeye çalıştığında; kullanıcının uygulamaya kaç izin verdiğine bağlı olarak yaşayacağı durumdur.
## Üçüncü taraf entegrasyonları hakkında { #about-third-party-integrations }
Bu örnekte OAuth2 "password" flow'unu kullanıyoruz.
Bu, kendi uygulamamıza giriş yaptığımız durumlar için uygundur; muhtemelen kendi frontend'imiz vardır.
Çünkü `username` ve `password` alacağını bildiğimiz frontend'i biz kontrol ediyoruz, dolayısıyla güvenebiliriz.
Ancak başkalarının bağlanacağı bir OAuth2 uygulaması geliştiriyorsanız (yani Facebook, Google, GitHub vb. gibi bir authentication provider muadili geliştiriyorsanız) diğer flow'lardan birini kullanmalısınız.
En yaygını implicit flow'dur.
En güvenlisi code flow'dur; ancak daha fazla adım gerektirdiği için implementasyonu daha karmaşıktır. Daha karmaşık olduğundan, birçok sağlayıcı implicit flow'yu önermeye yönelir.
/// note | Not
Her authentication provider'ın flow'ları markasının bir parçası yapmak için farklı şekilde adlandırması yaygındır.
Ama sonuçta aynı OAuth2 standardını implement ediyorlar.
///
**FastAPI**, bu OAuth2 authentication flow'larının tamamı için `fastapi.security.oauth2` içinde yardımcı araçlar sunar.
## Decorator `dependencies` içinde `Security` { #security-in-decorator-dependencies }
Decorator'ın `dependencies` parametresinde bir `list` `Depends` tanımlayabildiğiniz gibi ( [Path operation decorator'larında Dependencies](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md) bölümünde açıklandığı üzere), burada `scopes` ile birlikte `Security` de kullanabilirsiniz.
================================================
FILE: docs/tr/docs/advanced/settings.md
================================================
# Ayarlar ve Ortam Değişkenleri { #settings-and-environment-variables }
Birçok durumda uygulamanızın bazı harici ayarlara veya konfigürasyonlara ihtiyacı olabilir; örneğin secret key'ler, veritabanı kimlik bilgileri, e-posta servisleri için kimlik bilgileri vb.
Bu ayarların çoğu değişkendir (değişebilir); örneğin veritabanı URL'leri. Ayrıca birçoğu hassas olabilir; örneğin secret'lar.
Bu nedenle bunları, uygulama tarafından okunan environment variable'lar ile sağlamak yaygındır.
/// tip | İpucu
Environment variable'ları anlamak için [Ortam Değişkenleri](../environment-variables.md) dokümanını okuyabilirsiniz.
///
## Tipler ve doğrulama { #types-and-validation }
Bu environment variable'lar yalnızca metin (string) taşıyabilir; çünkü Python'ın dışındadırlar ve diğer programlarla ve sistemin geri kalanıyla uyumlu olmaları gerekir (hatta Linux, Windows, macOS gibi farklı işletim sistemleriyle de).
Bu da, Python içinde bir environment variable'dan okunan herhangi bir değerin `str` olacağı anlamına gelir; farklı bir tipe dönüştürme veya herhangi bir doğrulama işlemi kod içinde yapılmalıdır.
## Pydantic `Settings` { #pydantic-settings }
Neyse ki Pydantic, environment variable'lardan gelen bu ayarları yönetmek için [Pydantic: Settings yönetimi](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) ile çok iyi bir yardımcı araç sunar.
### `pydantic-settings`'i kurun { #install-pydantic-settings }
Önce, [Sanal ortam](../virtual-environments.md) oluşturduğunuzdan, aktive ettiğinizden emin olun ve ardından `pydantic-settings` paketini kurun:
### `Settings` nesnesini oluşturun { #create-the-settings-object }
Pydantic'ten `BaseSettings` import edin ve bir alt sınıf (sub-class) oluşturun; tıpkı bir Pydantic model'inde olduğu gibi.
Pydantic model'lerinde olduğu gibi, type annotation'larla (ve gerekirse default değerlerle) class attribute'ları tanımlarsınız.
Pydantic model'lerinde kullandığınız aynı doğrulama özelliklerini ve araçlarını burada da kullanabilirsiniz; örneğin farklı veri tipleri ve `Field()` ile ek doğrulamalar.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | İpucu
Hızlıca kopyalayıp yapıştırmak istiyorsanız bu örneği kullanmayın; aşağıdaki son örneği kullanın.
///
Ardından, bu `Settings` sınıfının bir instance'ını oluşturduğunuzda (bu örnekte `settings` nesnesi), Pydantic environment variable'ları büyük/küçük harfe duyarsız şekilde okur; yani büyük harfli `APP_NAME` değişkeni, yine de `app_name` attribute'u için okunur.
Sonrasında veriyi dönüştürür ve doğrular. Böylece `settings` nesnesini kullandığınızda, tanımladığınız tiplerde verilere sahip olursunuz (örn. `items_per_user` bir `int` olur).
### `settings`'i kullanın { #use-the-settings }
Daha sonra uygulamanızda yeni `settings` nesnesini kullanabilirsiniz:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Server'ı çalıştırın { #run-the-server }
Sonraki adımda server'ı çalıştırırken konfigürasyonları environment variable olarak geçersiniz; örneğin `ADMIN_EMAIL` ve `APP_NAME` şu şekilde ayarlanabilir:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | İpucu
Tek bir komut için birden fazla env var ayarlamak istiyorsanız aralarına boşluk koyun ve hepsini komuttan önce yazın.
///
Böylece `admin_email` ayarı `"deadpool@example.com"` olur.
`app_name` `"ChimichangApp"` olur.
`items_per_user` ise default değeri olan `50` olarak kalır.
## Ayarları başka bir module'de tutma { #settings-in-another-module }
[Daha Büyük Uygulamalar - Birden Fazla Dosya](../tutorial/bigger-applications.md) bölümünde gördüğünüz gibi, bu ayarları başka bir module dosyasına koyabilirsiniz.
Örneğin `config.py` adında bir dosyanız şu şekilde olabilir:
{* ../../docs_src/settings/app01_py310/config.py *}
Ve ardından bunu `main.py` dosyasında kullanabilirsiniz:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | İpucu
[Daha Büyük Uygulamalar - Birden Fazla Dosya](../tutorial/bigger-applications.md) bölümünde gördüğünüz gibi, ayrıca bir `__init__.py` dosyasına da ihtiyacınız olacak.
///
## Dependency içinde ayarlar { #settings-in-a-dependency }
Bazı durumlarda, her yerde kullanılan global bir `settings` nesnesi yerine ayarları bir dependency üzerinden sağlamak faydalı olabilir.
Bu özellikle test sırasında çok işe yarar; çünkü bir dependency'yi kendi özel ayarlarınızla override etmek çok kolaydır.
### Config dosyası { #the-config-file }
Bir önceki örnekten devam edersek, `config.py` dosyanız şöyle görünebilir:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Dikkat edin, artık default bir instance `settings = Settings()` oluşturmuyoruz.
### Ana uygulama dosyası { #the-main-app-file }
Şimdi, yeni bir `config.Settings()` döndüren bir dependency oluşturuyoruz.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | İpucu
`@lru_cache` konusunu birazdan ele alacağız.
Şimdilik `get_settings()`'in normal bir fonksiyon olduğunu varsayabilirsiniz.
///
Sonra bunu dependency olarak *path operation function*'dan talep edebilir ve ihtiyaç duyduğumuz her yerde kullanabiliriz.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Ayarlar ve test { #settings-and-testing }
Ardından, `get_settings` için bir dependency override oluşturarak test sırasında farklı bir settings nesnesi sağlamak çok kolay olur:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
Dependency override içinde, yeni `Settings` nesnesini oluştururken `admin_email` için yeni bir değer ayarlarız ve sonra bu yeni nesneyi döndürürüz.
Sonrasında bunun kullanıldığını test edebiliriz.
## `.env` dosyası okuma { #reading-a-env-file }
Çok sayıda ayarınız varsa ve bunlar farklı ortamlarda sık sık değişiyorsa, bunları bir dosyaya koyup, sanki environment variable'mış gibi o dosyadan okumak faydalı olabilir.
Bu yaklaşım oldukça yaygındır ve bir adı vardır: Bu environment variable'lar genellikle `.env` adlı bir dosyaya konur ve bu dosyaya "dotenv" denir.
/// tip | İpucu
Nokta (`.`) ile başlayan dosyalar, Linux ve macOS gibi Unix-benzeri sistemlerde gizli dosyadır.
Ancak dotenv dosyasının mutlaka bu dosya adına sahip olması gerekmez.
///
Pydantic, harici bir kütüphane kullanarak bu tür dosyalardan okuma desteğine sahiptir. Daha fazlası için: [Pydantic Settings: Dotenv (.env) desteği](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | İpucu
Bunun çalışması için `pip install python-dotenv` yapmanız gerekir.
///
### `.env` dosyası { #the-env-file }
Şöyle bir `.env` dosyanız olabilir:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Ayarları `.env`'den okuyun { #read-settings-from-env }
Ardından `config.py` dosyanızı şöyle güncelleyin:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | İpucu
`model_config` attribute'u yalnızca Pydantic konfigürasyonu içindir. Daha fazlası için [Pydantic: Kavramlar: Konfigürasyon](https://docs.pydantic.dev/latest/concepts/config/).
///
Burada, Pydantic `Settings` sınıfınızın içinde `env_file` konfigürasyonunu tanımlar ve değer olarak kullanmak istediğimiz dotenv dosyasının dosya adını veririz.
### `lru_cache` ile `Settings`'i yalnızca bir kez oluşturma { #creating-the-settings-only-once-with-lru-cache }
Diskten dosya okumak normalde maliyetli (yavaş) bir işlemdir; bu yüzden muhtemelen bunu yalnızca bir kez yapıp aynı settings nesnesini tekrar kullanmak istersiniz. Her request için yeniden okumak istemezsiniz.
Ancak her seferinde şunu yaptığımızda:
```Python
Settings()
```
yeni bir `Settings` nesnesi oluşturulur ve oluşturulurken `.env` dosyasını yeniden okur.
Dependency fonksiyonu sadece şöyle olsaydı:
```Python
def get_settings():
return Settings()
```
bu nesneyi her request için oluştururduk ve `.env` dosyasını her request'te okurduk. ⚠️
Fakat en üstte `@lru_cache` decorator'ünü kullandığımız için `Settings` nesnesi yalnızca bir kez, ilk çağrıldığı anda oluşturulur. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Sonraki request'lerde dependency'ler içinden `get_settings()` çağrıldığında, `get_settings()`'in iç kodu tekrar çalıştırılıp yeni bir `Settings` nesnesi yaratılmak yerine, ilk çağrıda döndürülen aynı nesne tekrar tekrar döndürülür.
#### `lru_cache` Teknik Detayları { #lru-cache-technical-details }
`@lru_cache`, decorator olarak uygulandığı fonksiyonu, her seferinde tekrar hesaplamak yerine ilk seferde döndürdüğü değeri döndürecek şekilde değiştirir; yani fonksiyon kodunu her çağrıda yeniden çalıştırmaz.
Bu nedenle altındaki fonksiyon, argüman kombinasyonlarının her biri için bir kez çalıştırılır. Sonra bu argüman kombinasyonlarının her biri için döndürülmüş değerler, fonksiyon aynı argüman kombinasyonuyla çağrıldıkça tekrar tekrar kullanılır.
Örneğin, şöyle bir fonksiyonunuz varsa:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
programınız şu şekilde çalışabilir:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
Bizim `get_settings()` dependency'miz özelinde ise fonksiyon hiç argüman almaz; dolayısıyla her zaman aynı değeri döndürür.
Bu şekilde, neredeyse global bir değişken gibi davranır. Ancak bir dependency fonksiyonu kullandığı için testte kolayca override edebiliriz.
`@lru_cache`, Python standart kütüphanesinin bir parçası olan `functools` içindedir. Daha fazla bilgi için: [Python dokümanları: `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Özet { #recap }
Uygulamanızın ayarlarını veya konfigürasyonlarını yönetmek için, Pydantic model'lerinin tüm gücüyle birlikte Pydantic Settings'i kullanabilirsiniz.
* Dependency kullanarak test etmeyi basitleştirebilirsiniz.
* Bununla `.env` dosyalarını kullanabilirsiniz.
* `@lru_cache` kullanmak, dotenv dosyasını her request için tekrar tekrar okumayı engellerken, test sırasında override etmenize de izin verir.
================================================
FILE: docs/tr/docs/advanced/stream-data.md
================================================
# Veri Akışı { #stream-data }
Veriyi JSON olarak yapılandırabiliyorsanız, [JSON Lines Akışı](../tutorial/stream-json-lines.md) kullanın.
Ancak saf ikili (binary) veri ya da string akıtmak istiyorsanız, bunu şöyle yapabilirsiniz.
/// info | Bilgi
FastAPI 0.134.0 ile eklendi.
///
## Kullanım Senaryoları { #use-cases }
Doğrudan bir AI LLM (Büyük Dil Modeli) servisinin çıktısından saf string'leri akıtmak istediğinizde kullanabilirsiniz.
Ayrıca **büyük ikili (binary) dosyaları** akıtmak için de kullanabilirsiniz; veriyi okurken her parçayı (chunk) sırayla gönderirsiniz, tamamını belleğe almak zorunda kalmazsınız.
Bu şekilde **video** veya **ses** de akıtabilirsiniz; hatta işledikçe üretilip gönderilebilir.
## `yield` ile bir `StreamingResponse` { #a-streamingresponse-with-yield }
*Path operation function* içinde `response_class=StreamingResponse` belirtirseniz, her veri parçasını sırayla göndermek için `yield` kullanabilirsiniz.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI her veri parçasını olduğu gibi `StreamingResponse`'a verir; JSON'a ya da benzeri bir formata dönüştürmeye çalışmaz.
### Async Olmayan Path Operation Function'lar { #non-async-path-operation-functions }
Normal `def` fonksiyonlarını (yani `async` olmadan) da kullanabilir ve aynı şekilde `yield` yazabilirsiniz.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Tip Annotasyonu Yok { #no-annotation }
İkili (binary) veri akıtıyorsanız dönüş tipi annotasyonu belirtmeniz şart değildir.
FastAPI veriyi Pydantic ile JSON'a çevirmeye veya herhangi bir şekilde serileştirmeye çalışmayacağı için, bu durumda tip annotasyonu sadece editörünüz ve araçlarınız içindir; FastAPI tarafından kullanılmaz.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Bu aynı zamanda `StreamingResponse` ile veriyi tam olarak ihtiyaç duyduğunuz biçimde üretme ve encode etme konusunda hem bir özgürlük hem de bir sorumluluk verdiği anlamına gelir; tip annotasyonlarından bağımsızdır. 🤓
### Bytes Akışı { #stream-bytes }
Başlıca kullanım senaryolarından biri string yerine `bytes` akıtmaktır; elbette bunu yapabilirsiniz.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Özel bir `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
Yukarıdaki örneklerde veri baytları akıtıldı, ancak response'ta bir `Content-Type` header'ı yoktu; bu nedenle istemci hangi tür veriyi aldığını bilmiyordu.
Akıttığınız veri türüne uygun `Content-Type` header'ını ayarlayan, `StreamingResponse`'tan türetilmiş özel bir alt sınıf (subclass) oluşturabilirsiniz.
Örneğin, `media_type` özniteliğini kullanarak `Content-Type` header'ını `image/png` olarak ayarlayan bir `PNGStreamingResponse` oluşturabilirsiniz:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Ardından bu yeni sınıfı *path operation function* içinde `response_class=PNGStreamingResponse` olarak kullanabilirsiniz:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Bir Dosyayı Simüle Etme { #simulate-a-file }
Bu örnekte, yalnızca bellekte yaşayan ama aynı arayüzü kullanmamıza izin veren, dosya benzeri bir nesne olan `io.BytesIO` ile bir dosyayı simüle ediyoruz.
Örneğin, bir dosyada yapabileceğimiz gibi, içeriğini tüketmek için üzerinde iterate edebiliriz.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Teknik Detaylar
Diğer iki değişken olan `image_base64` ve `binary_image`, Base64 ile encode edilmiş bir görüntüdür; daha sonra bayt'lara çevrilip `io.BytesIO`'ya aktarılır.
Sadece bu örnek aynı dosyada yaşayabilsin, kopyalayıp olduğu gibi çalıştırabilesiniz diye. 🥚
///
`with` bloğu kullanarak, jeneratör fonksiyonu (içinde `yield` olan fonksiyon) tamamlandığında dosya benzeri nesnenin kapandığından emin oluruz. Yani response gönderimi bittikten sonra.
Bu özel örnekte o kadar da önemli değil, çünkü sahte ve bellekte (yani `io.BytesIO` ile). Ancak gerçek bir dosyada, onunla işiniz bittiğinde dosyanın kapandığından emin olmak önemlidir.
### Dosyalar ve Async { #files-and-async }
Çoğu durumda dosya benzeri nesneler, varsayılan olarak async ve await ile uyumlu değildir.
Örneğin, `await file.read()` ya da `async for chunk in file` gibi şeyler yoktur.
Ve birçok durumda, diskte ya da ağda okundukları için, okumak engelleyici (event loop'u bloke edebilen) bir işlem olabilir.
/// info | Bilgi
Yukarıdaki örnek aslında bir istisna; çünkü `io.BytesIO` nesnesi zaten bellekte, dolayısıyla onu okumak hiçbir şeyi bloke etmez.
Ancak çoğu durumda bir dosyayı veya dosya benzeri bir nesneyi okumak bloke edicidir.
///
Event loop'u bloke etmemek için, *path operation function*'ı `async def` yerine normal `def` ile tanımlayabilirsiniz; böylece FastAPI ana döngüyü bloke etmemek için bunu bir thread pool worker (iş parçacığı havuzu çalışanı) üzerinde çalıştırır.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | İpucu
Async bir fonksiyonun içinden bloklayıcı kod çağırmanız ya da bloklayıcı bir fonksiyonun içinden async bir fonksiyon çağırmanız gerekirse, FastAPI'nin kardeş kütüphanesi olan [Asyncer](https://asyncer.tiangolo.com)'ı kullanabilirsiniz.
///
### `yield from` { #yield-from }
Bir şeyin (ör. dosya benzeri bir nesne) üzerinde iterate ederken, her öğe için `yield` yapıyorsanız, `for` döngüsünü yazmak yerine `yield from` ile her öğeyi doğrudan yield edebilirsiniz.
Bu FastAPI'ye özgü değildir, tamamen Python'dur, ama bilinmesi güzel bir püf noktasıdır. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/tr/docs/advanced/strict-content-type.md
================================================
# Sıkı Content-Type Kontrolü { #strict-content-type-checking }
Varsayılan olarak FastAPI, JSON request body'leri için sıkı Content-Type header kontrolü uygular. Bu, JSON request'lerin body'lerinin JSON olarak parse edilebilmesi için geçerli bir Content-Type header'ı (örn. application/json) içermesi gerektiği anlamına gelir.
## CSRF Riski { #csrf-risk }
Bu varsayılan davranış, çok belirli bir senaryoda bir sınıf Cross-Site Request Forgery (CSRF) saldırılarına karşı koruma sağlar.
Bu saldırılar, tarayıcıların aşağıdaki durumlarda herhangi bir CORS preflight kontrolü yapmadan script’lerin request göndermesine izin vermesinden faydalanır:
- bir Content-Type header’ı yoksa (örn. body olarak Blob ile fetch() kullanıldığında)
- ve herhangi bir kimlik doğrulama bilgisi gönderilmiyorsa.
Bu tür saldırılar özellikle şu durumlarda önemlidir:
- uygulama yerelde (örn. localhost’ta) veya dahili bir ağda çalışıyorsa
- ve uygulamada hiç kimlik doğrulama yoksa, aynı ağdan gelen her request’in güvenilir olduğu varsayılıyorsa.
## Örnek Saldırı { #example-attack }
Yerelde çalışan bir AI agent’ı (yapay zeka ajanı) çalıştırmanın bir yolunu geliştirdiğinizi düşünün.
Bir API sunuyor:
```
http://localhost:8000/v1/agents/multivac
```
Ayrıca bir frontend var:
```
http://localhost:8000
```
/// tip | İpucu
İkisinin de host’u aynıdır.
///
Frontend’i kullanarak AI agent’a sizin adınıza işler yaptırabiliyorsunuz.
Uygulama yerelde çalıştığı ve açık internette olmadığı için, sadece yerel ağa güvenip herhangi bir kimlik doğrulama kurmamaya karar verdiniz.
Kullanıcılarınızdan biri de bunu indirip yerelde çalıştırabilir.
Sonra kötü niyetli bir web sitesini açabilir, örneğin:
```
https://evilhackers.example.com
```
Ve bu kötü niyetli site, body olarak Blob kullanan fetch() ile yerel API’ye request’ler gönderebilir:
```
http://localhost:8000/v1/agents/multivac
```
Kötü niyetli sitenin host’u ile yerel uygulamanın host’u farklı olsa bile, tarayıcı şu nedenlerle bir CORS preflight isteği tetiklemez:
- Herhangi bir kimlik doğrulama yoktur, bu nedenle credential göndermesi gerekmez.
- Tarayıcı, Content-Type header’ı eksik olduğundan JSON gönderildiğini düşünmez.
Böylece kötü niyetli site, yerel AI agent’ın kullanıcının eski patronuna sinirli mesajlar göndermesini sağlayabilir... ya da daha kötüsü. 😅
## Açık İnternet { #open-internet }
Uygulamanız açık internetteyse “ağa güvenmez” ve kimlik doğrulama olmadan kimsenin ayrıcalıklı request’ler göndermesine izin vermezsiniz.
Saldırganlar tarayıcı etkileşimine ihtiyaç duymadan basitçe bir script çalıştırıp API’nize request gönderebilir, bu yüzden muhtemelen ayrıcalıklı endpoint’leri zaten güvenceye almışsınızdır.
Bu durumda bu saldırı/riski sizler için geçerli değildir.
Bu risk ve saldırı, esasen uygulama sadece yerel ağda çalıştığında ve tek koruma varsayımının bu olduğu durumlarda önemlidir.
## Content-Type Olmadan Gelen Request’lere İzin Vermek { #allowing-requests-without-content-type }
Content-Type header’ı göndermeyen client’ları desteklemeniz gerekiyorsa, strict kontrolü strict_content_type=False ayarıyla kapatabilirsiniz:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
Bu ayarla, Content-Type header’ı olmayan request’lerin body’si JSON olarak parse edilir. Bu, FastAPI’nin eski sürümlerindeki davranışla aynıdır.
/// info | Bilgi
Bu davranış ve yapılandırma FastAPI 0.132.0’da eklendi.
///
================================================
FILE: docs/tr/docs/advanced/sub-applications.md
================================================
# Alt Uygulamalar - Mount İşlemi { #sub-applications-mounts }
Kendi bağımsız OpenAPI şemaları ve kendi dokümantasyon arayüzleri olan iki bağımsız FastAPI uygulamasına ihtiyacınız varsa, bir ana uygulama oluşturup bir (veya daha fazla) alt uygulamayı "mount" edebilirsiniz.
## Bir **FastAPI** uygulamasını mount etmek { #mounting-a-fastapi-application }
"Mount" etmek, belirli bir path altında tamamen "bağımsız" bir uygulamayı eklemek anlamına gelir. Ardından o path’in altındaki her şeyi, alt uygulamada tanımlanan _path operation_’lar ile o alt uygulama yönetir.
### Üst seviye uygulama { #top-level-application }
Önce ana, üst seviye **FastAPI** uygulamasını ve onun *path operation*’larını oluşturun:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Alt uygulama { #sub-application }
Sonra alt uygulamanızı ve onun *path operation*’larını oluşturun.
Bu alt uygulama da standart bir FastAPI uygulamasıdır; ancak "mount" edilecek olan budur:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Alt uygulamayı mount edin { #mount-the-sub-application }
Üst seviye uygulamanızda (`app`), alt uygulama `subapi`’yi mount edin.
Bu örnekte `/subapi` path’ine mount edilecektir:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Otomatik API dokümanlarını kontrol edin { #check-the-automatic-api-docs }
Şimdi `fastapi` komutunu çalıştırın:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Ve dokümanları [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresinden açın.
Ana uygulama için otomatik API dokümanlarını göreceksiniz; yalnızca onun kendi _path operation_’larını içerir:
Sonra alt uygulamanın dokümanlarını [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs) adresinden açın.
Alt uygulama için otomatik API dokümanlarını göreceksiniz; yalnızca onun kendi _path operation_’larını içerir ve hepsi doğru alt-path öneki `/subapi` altında yer alır:
İki arayüzden herhangi biriyle etkileşime girmeyi denerseniz doğru şekilde çalıştıklarını görürsünüz; çünkü tarayıcı her bir uygulama ya da alt uygulama ile ayrı ayrı iletişim kurabilir.
### Teknik Detaylar: `root_path` { #technical-details-root-path }
Yukarıda anlatıldığı gibi bir alt uygulamayı mount ettiğinizde FastAPI, ASGI spesifikasyonundaki `root_path` adlı bir mekanizmayı kullanarak alt uygulamaya mount path’ini iletmeyi otomatik olarak yönetir.
Bu sayede alt uygulama, dokümantasyon arayüzü için o path önekini kullanması gerektiğini bilir.
Ayrıca alt uygulamanın kendi mount edilmiş alt uygulamaları da olabilir; FastAPI tüm bu `root_path`’leri otomatik olarak yönettiği için her şey doğru şekilde çalışır.
`root_path` hakkında daha fazlasını ve bunu açıkça nasıl kullanacağınızı [Proxy Arkasında](behind-a-proxy.md) bölümünde öğreneceksiniz.
================================================
FILE: docs/tr/docs/advanced/templates.md
================================================
# Şablonlar { #templates }
**FastAPI** ile istediğiniz herhangi bir template engine'i kullanabilirsiniz.
Yaygın bir tercih, Flask ve diğer araçların da kullandığı Jinja2'dir.
Bunu kolayca yapılandırmak için, doğrudan **FastAPI** uygulamanızda kullanabileceğiniz yardımcı araçlar vardır (Starlette tarafından sağlanır).
## Bağımlılıkları Yükleme { #install-dependencies }
Bir [sanal ortam](../virtual-environments.md) oluşturduğunuzdan, etkinleştirdiğinizden ve `jinja2`'yi yüklediğinizden emin olun:
/// info | Bilgi
Paket bağımlılıklarını tanımlamak ve yüklemek için başka formatlar ve araçlar da vardır.
///
### **FastAPI** Kodunu Oluşturun { #create-the-fastapi-code }
* Bir `app` dizini oluşturun ve içine girin.
* Boş bir `__init__.py` dosyası oluşturun.
* Aşağıdakilerle bir `main.py` dosyası oluşturun:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Şimdi aynı proje dizininde `Dockerfile` adlı bir dosya oluşturun ve içine şunları yazın:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Resmi Python base image'ından başlayın.
2. Geçerli çalışma dizinini `/code` olarak ayarlayın.
`requirements.txt` dosyasını ve `app` dizinini buraya koyacağız.
3. Gereksinimleri içeren dosyayı `/code` dizinine kopyalayın.
Önce kodun tamamını değil, **sadece** gereksinim dosyasını kopyalayın.
Bu dosya **çok sık değişmediği** için Docker bunu tespit eder ve bu adımda **cache** kullanır; böylece bir sonraki adım için de cache devreye girer.
4. Gereksinim dosyasındaki paket bağımlılıklarını yükleyin.
`--no-cache-dir` seçeneği, indirilen paketlerin yerel olarak kaydedilmemesini `pip`'e söyler. Bu kayıt, `pip` aynı paketleri tekrar yüklemek için yeniden çalıştırılacaksa işe yarar; ancak container'larla çalışırken genelde bu durum geçerli değildir.
/// note | Not
`--no-cache-dir` yalnızca `pip` ile ilgilidir; Docker veya container'larla ilgili değildir.
///
`--upgrade` seçeneği, paketler zaten yüklüyse `pip`'e onları yükseltmesini söyler.
Bir önceki adım (dosyayı kopyalama) **Docker cache** tarafından tespit edilebildiği için, bu adım da uygun olduğunda **Docker cache'i kullanır**.
Bu adımda cache kullanmak, geliştirme sırasında image'ı tekrar tekrar build ederken size çok **zaman** kazandırır; her seferinde bağımlılıkları **indirip yüklemek** zorunda kalmazsınız.
5. `./app` dizinini `/code` dizininin içine kopyalayın.
Burada en sık değişen şey olan kodun tamamı bulunduğundan, bu adım (ve genelde bundan sonraki adımlar) için Docker **cache**'i kolay kolay kullanılamaz.
Bu yüzden, container image build sürelerini optimize etmek için bunu `Dockerfile`'ın **sonlarına yakın** koymak önemlidir.
6. Altta Uvicorn kullanan `fastapi run` komutunu **command** olarak ayarlayın.
`CMD` bir string listesi alır; bu string'lerin her biri komut satırında boşlukla ayrılmış şekilde yazacağınız parçaları temsil eder.
Bu komut, yukarıda `WORKDIR /code` ile ayarladığınız `/code` dizininden çalıştırılır.
/// tip | İpucu
Kod içindeki her numara balonuna tıklayarak her satırın ne yaptığını gözden geçirin. 👆
///
/// warning | Uyarı
Aşağıda açıklandığı gibi `CMD` talimatının **her zaman** **exec form**'unu kullandığınızdan emin olun.
///
#### `CMD` Kullanımı - Exec Form { #use-cmd-exec-form }
[`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) Docker talimatı iki formda yazılabilir:
✅ **Exec** form:
```Dockerfile
# ✅ Do this
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ **Shell** form:
```Dockerfile
# ⛔️ Don't do this
CMD fastapi run app/main.py --port 80
```
FastAPI'nin düzgün şekilde kapanabilmesi ve [lifespan event](../advanced/events.md)'lerinin tetiklenmesi için her zaman **exec** formunu kullanın.
Detaylar için [shell ve exec form için Docker dokümanlarına](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form) bakabilirsiniz.
Bu durum `docker compose` kullanırken oldukça belirgin olabilir. Daha teknik detaylar için şu Docker Compose FAQ bölümüne bakın: [Hizmetlerimin yeniden oluşturulması veya durması neden 10 saniye sürüyor?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Dizin Yapısı { #directory-structure }
Artık dizin yapınız şöyle olmalı:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### TLS Termination Proxy Arkasında { #behind-a-tls-termination-proxy }
Container'ınızı Nginx veya Traefik gibi bir TLS Termination Proxy (load balancer) arkasında çalıştırıyorsanız `--proxy-headers` seçeneğini ekleyin. Bu, Uvicorn'a (FastAPI CLI üzerinden) uygulamanın HTTPS arkasında çalıştığını söyleyen proxy header'larına güvenmesini söyler.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Docker Cache { #docker-cache }
Bu `Dockerfile` içinde önemli bir numara var: önce kodun geri kalanını değil, **sadece bağımlılık dosyasını** kopyalıyoruz. Nedenini anlatayım.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker ve benzeri araçlar bu container image'larını **artımlı (incremental)** olarak **build** eder; `Dockerfile`'ın en üstünden başlayıp her talimatın oluşturduğu dosyaları ekleyerek **katman katman (layer)** ilerler.
Docker ve benzeri araçlar image build ederken ayrıca bir **internal cache** kullanır. Son build'den beri bir dosya değişmediyse, dosyayı tekrar kopyalayıp sıfırdan yeni bir layer oluşturmak yerine, daha önce oluşturulan **aynı layer**'ı yeniden kullanır.
Sadece dosya kopyalamayı azaltmak her zaman büyük fark yaratmaz. Ancak o adımda cache kullanıldığı için, **bir sonraki adımda da cache kullanılabilir**. Örneğin bağımlılıkları yükleyen şu talimat için:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Paket gereksinimleri dosyası **sık sık değişmez**. Bu yüzden sadece bu dosyayı kopyalayınca, Docker bu adımda **cache** kullanabilir.
Sonra Docker, bağımlılıkları indirip yükleyen **bir sonraki adımda** da cache kullanabilir. Asıl **çok zaman kazandığımız** yer de burasıdır. ✨ ...ve beklerken sıkılmayı engeller. 😪😆
Bağımlılıkları indirip yüklemek **dakikalar sürebilir**, fakat **cache** kullanmak en fazla **saniyeler** alır.
Geliştirme sırasında kod değişikliklerinizin çalıştığını kontrol etmek için container image'ı tekrar tekrar build edeceğinizden, bu ciddi birikimli zaman kazancı sağlar.
Sonra `Dockerfile`'ın sonlarına doğru tüm kodu kopyalarız. En sık değişen kısım bu olduğu için sona koyarız; çünkü neredeyse her zaman bu adımdan sonra gelen adımlar cache kullanamaz.
```Dockerfile
COPY ./app /code/app
```
### Docker Image'ını Build Edin { #build-the-docker-image }
Tüm dosyalar hazır olduğuna göre container image'ı build edelim.
* Proje dizinine gidin (`Dockerfile`'ınızın olduğu ve `app` dizininizi içeren dizin).
* FastAPI image'ınızı build edin:
/// tip | İpucu
Sondaki `.` ifadesine dikkat edin; `./` ile aynı anlama gelir ve Docker'a container image build etmek için hangi dizini kullanacağını söyler.
Bu örnekte, mevcut dizindir (`.`).
///
### Docker Container'ını Başlatın { #start-the-docker-container }
* Image'ınızdan bir container çalıştırın:
## Kontrol Edin { #check-it }
Docker container'ınızın URL'inden kontrol edebilmelisiniz. Örneğin: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) veya [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (ya da Docker host'unuzu kullanarak eşdeğeri).
Şuna benzer bir şey görürsünüz:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Etkileşimli API Dokümanları { #interactive-api-docs }
Şimdi [http://192.168.99.100/docs](http://192.168.99.100/docs) veya [http://127.0.0.1/docs](http://127.0.0.1/docs) adresine gidebilirsiniz (ya da Docker host'unuzla eşdeğeri).
Otomatik etkileşimli API dokümantasyonunu görürsünüz ( [Swagger UI](https://github.com/swagger-api/swagger-ui) tarafından sağlanır):

## Alternatif API Dokümanları { #alternative-api-docs }
Ayrıca [http://192.168.99.100/redoc](http://192.168.99.100/redoc) veya [http://127.0.0.1/redoc](http://127.0.0.1/redoc) adresine de gidebilirsiniz (ya da Docker host'unuzla eşdeğeri).
Alternatif otomatik dokümantasyonu görürsünüz ([ReDoc](https://github.com/Rebilly/ReDoc) tarafından sağlanır):

## Tek Dosyalık FastAPI ile Docker Image Oluşturma { #build-a-docker-image-with-a-single-file-fastapi }
FastAPI uygulamanız tek bir dosyaysa; örneğin `./app` dizini olmadan sadece `main.py` varsa, dosya yapınız şöyle olabilir:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Bu durumda `Dockerfile` içinde dosyayı kopyaladığınız path'leri buna göre değiştirmeniz yeterlidir:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. `main.py` dosyasını doğrudan `/code` dizinine kopyalayın (herhangi bir `./app` dizini olmadan).
2. Tek dosya olan `main.py` içindeki uygulamanızı sunmak için `fastapi run` kullanın.
Dosyayı `fastapi run`'a verdiğinizde, bunun bir package'ın parçası değil tek bir dosya olduğunu otomatik olarak algılar; nasıl import edip FastAPI uygulamanızı nasıl serve edeceğini bilir. 😎
## Deployment Kavramları { #deployment-concepts }
Aynı [Deployment Kavramları](concepts.md)nı bu kez container'lar açısından tekrar konuşalım.
Container'lar, bir uygulamayı **build etme ve deploy etme** sürecini basitleştiren bir araçtır. Ancak bu **deployment kavramları**nı ele almak için belirli bir yaklaşımı zorunlu kılmazlar; birkaç farklı strateji mümkündür.
**İyi haber** şu: Hangi stratejiyi seçerseniz seçin, deployment kavramlarının tamamını kapsayacak bir yol vardır. 🎉
Bu **deployment kavramları**nı container'lar açısından gözden geçirelim:
* HTTPS
* Startup'ta çalıştırma
* Restart'lar
* Replication (çalışan process sayısı)
* Memory
* Başlatmadan önceki adımlar
## HTTPS { #https }
Bir FastAPI uygulamasının sadece **container image**'ına (ve sonra çalışan **container**'a) odaklanırsak, HTTPS genellikle **haricen** başka bir araçla ele alınır.
Örneğin [Traefik](https://traefik.io/) kullanan başka bir container olabilir; **HTTPS** ve **sertifika**ların **otomatik** alınmasını o yönetebilir.
/// tip | İpucu
Traefik; Docker, Kubernetes ve diğerleriyle entegre çalışır. Bu sayede container'larınız için HTTPS'i kurup yapılandırmak oldukça kolaydır.
///
Alternatif olarak HTTPS, bir cloud provider'ın sunduğu servislerden biri tarafından da yönetilebilir (uygulama yine container içinde çalışırken).
## Startup'ta Çalıştırma ve Restart'lar { #running-on-startup-and-restarts }
Container'ınızı **başlatıp çalıştırmaktan** sorumlu genellikle başka bir araç olur.
Bu; doğrudan **Docker**, **Docker Compose**, **Kubernetes**, bir **cloud service** vb. olabilir.
Çoğu (veya tüm) durumda, container'ı startup'ta çalıştırmayı ve hata durumlarında restart'ları etkinleştirmeyi sağlayan basit bir seçenek vardır. Örneğin Docker'da bu, `--restart` komut satırı seçeneğidir.
Container kullanmadan, uygulamaları startup'ta çalıştırmak ve restart mekanizması eklemek zahmetli ve zor olabilir. Ancak **container'larla çalışırken** çoğu zaman bu işlevler varsayılan olarak hazır gelir. ✨
## Replication - Process Sayısı { #replication-number-of-processes }
Eğer bir küme (cluster) olarak yapılandırılmış makineler grubunuz varsa ve bunları **Kubernetes**, Docker Swarm Mode, Nomad veya benzeri, birden çok makinede dağıtık container'ları yöneten karmaşık bir sistemle yönetiyorsanız, replication'ı her container içinde bir **process manager** (ör. worker'lı Uvicorn) kullanarak yönetmek yerine, muhtemelen **küme seviyesinde (cluster level)** ele almak istersiniz.
Kubernetes gibi dağıtık container yönetim sistemleri, gelen request'ler için **load balancing** desteği sunarken aynı zamanda **container replication**'ını yönetmek için entegre mekanizmalara sahiptir. Hepsi **cluster seviyesinde**.
Bu tür durumlarda, yukarıda [anlatıldığı gibi](#dockerfile) bağımlılıkları yükleyip **sıfırdan bir Docker image** build etmek ve birden fazla Uvicorn worker kullanmak yerine **tek bir Uvicorn process** çalıştırmak istersiniz.
### Load Balancer { #load-balancer }
Container'lar kullanırken, genellikle ana port'ta dinleyen bir bileşen olur. Bu, **HTTPS**'i ele almak için bir **TLS Termination Proxy** olan başka bir container da olabilir ya da benzeri bir araç olabilir.
Bu bileşen request'lerin **yükünü** alıp worker'lar arasında (umarım) **dengeli** şekilde dağıttığı için yaygın olarak **Load Balancer** diye de adlandırılır.
/// tip | İpucu
HTTPS için kullanılan aynı **TLS Termination Proxy** bileşeni muhtemelen bir **Load Balancer** olarak da çalışır.
///
Container'larla çalışırken, onları başlatıp yönettiğiniz sistem; bu **load balancer**'dan (aynı zamanda **TLS Termination Proxy** de olabilir) uygulamanızın bulunduğu container(lar)a **network communication**'ı (ör. HTTP request'leri) iletmek için zaten dahili araçlar sunar.
### Tek Load Balancer - Çoklu Worker Container { #one-load-balancer-multiple-worker-containers }
**Kubernetes** veya benzeri dağıtık container yönetim sistemleriyle çalışırken, dahili ağ mekanizmaları sayesinde ana **port**'u dinleyen tek bir **load balancer**, uygulamanızı çalıştıran muhtemelen **birden fazla container**'a request'leri iletebilir.
Uygulamanızı çalıştıran bu container'ların her birinde normalde **tek bir process** olur (ör. FastAPI uygulamanızı çalıştıran bir Uvicorn process). Hepsi aynı şeyi çalıştıran **özdeş container**'lardır; ancak her birinin kendi process'i, memory'si vb. vardır. Böylece CPU'nun **farklı core**'larında, hatta **farklı makinelerde** paralelleştirmeden yararlanırsınız.
Load balancer'lı dağıtık sistem, request'leri uygulamanızın bulunduğu container'ların her birine sırayla **dağıtır**. Böylece her request, uygulamanızın birden fazla **replicated container**'ından biri tarafından işlenebilir.
Ve bu **load balancer** normalde cluster'ınızdaki *diğer* uygulamalara giden request'leri de (ör. farklı bir domain ya da farklı bir URL path prefix altında) yönetebilir ve iletişimi o *diğer* uygulamanın doğru container'larına iletir.
### Container Başına Tek Process { #one-process-per-container }
Bu senaryoda, replication'ı zaten cluster seviyesinde yaptığınız için, muhtemelen **container başına tek bir (Uvicorn) process** istersiniz.
Dolayısıyla bu durumda container içinde `--workers` gibi bir komut satırı seçeneğiyle çoklu worker istemezsiniz. Container başına sadece **tek bir Uvicorn process** istersiniz (ama muhtemelen birden fazla container).
Container içine ekstra bir process manager koymak (çoklu worker gibi) çoğu zaman zaten cluster sisteminizle çözdüğünüz şeye ek **gereksiz karmaşıklık** katar.
### Birden Fazla Process'li Container'lar ve Özel Durumlar { #containers-with-multiple-processes-and-special-cases }
Elbette bazı **özel durumlarda** bir container içinde birden fazla **Uvicorn worker process** çalıştırmak isteyebilirsiniz.
Bu durumlarda çalıştırmak istediğiniz worker sayısını `--workers` komut satırı seçeneğiyle ayarlayabilirsiniz:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Burada worker sayısını 4 yapmak için `--workers` komut satırı seçeneğini kullanıyoruz.
Bunun mantıklı olabileceği birkaç örnek:
#### Basit Bir Uygulama { #a-simple-app }
Uygulamanız tek bir server üzerinde (cluster değil) çalışacak kadar **basitse**, container içinde bir process manager isteyebilirsiniz.
#### Docker Compose { #docker-compose }
**Docker Compose** ile **tek bir server**'a (cluster değil) deploy ediyor olabilirsiniz. Bu durumda, paylaşılan ağı ve **load balancing**'i koruyarak container replication'ını (Docker Compose ile) yönetmenin kolay bir yolu olmayabilir.
Bu durumda, tek bir container içinde **bir process manager** ile **birden fazla worker process** başlatmak isteyebilirsiniz.
---
Ana fikir şu: Bunların **hiçbiri** körü körüne uymanız gereken **değişmez kurallar** değildir. Bu fikirleri, kendi kullanım senaryonuzu **değerlendirmek** ve sisteminiz için en iyi yaklaşımı seçmek için kullanabilirsiniz. Şu kavramları nasıl yöneteceğinize bakarak karar verin:
* Güvenlik - HTTPS
* Startup'ta çalıştırma
* Restart'lar
* Replication (çalışan process sayısı)
* Memory
* Başlatmadan önceki adımlar
## Memory { #memory }
**Container başına tek process** çalıştırırsanız, her container'ın tüketeceği memory miktarı aşağı yukarı tanımlı, stabil ve sınırlı olur (replication varsa birden fazla container için).
Sonra aynı memory limit ve gereksinimlerini container yönetim sisteminizin (ör. **Kubernetes**) konfigürasyonlarında belirleyebilirsiniz. Böylece sistem; ihtiyaç duyulan memory miktarını ve cluster'daki makinelerde mevcut memory'yi dikkate alarak **uygun makinelerde container'ları replicate edebilir**.
Uygulamanız **basitse**, muhtemelen bu **bir sorun olmaz** ve katı memory limitleri belirlemeniz gerekmeyebilir. Ancak **çok memory kullanıyorsanız** (ör. **machine learning** modelleriyle), ne kadar memory tükettiğinizi kontrol edip **her makinede** çalışacak **container sayısını** ayarlamalısınız (ve gerekirse cluster'a daha fazla makine eklemelisiniz).
**Container başına birden fazla process** çalıştırırsanız, başlatılan process sayısının mevcut olandan **fazla memory tüketmediğinden** emin olmanız gerekir.
## Başlatmadan Önceki Adımlar ve Container'lar { #previous-steps-before-starting-and-containers }
Container kullanıyorsanız (örn. Docker, Kubernetes), temelde iki yaklaşım vardır.
### Birden Fazla Container { #multiple-containers }
**Birden fazla container**'ınız varsa ve muhtemelen her biri **tek process** çalıştırıyorsa (ör. bir **Kubernetes** cluster'ında), replication yapılan worker container'lar çalışmadan **önce**, **başlatmadan önceki adımlar**ın işini yapan **ayrı bir container** kullanmak isteyebilirsiniz (tek container, tek process).
/// info | Bilgi
Kubernetes kullanıyorsanız, bu muhtemelen bir [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) olur.
///
Kullanım senaryonuzda bu adımları **paralel olarak birden fazla kez** çalıştırmak sorun değilse (örneğin veritabanı migration çalıştırmıyor, sadece veritabanı hazır mı diye kontrol ediyorsanız), o zaman her container'da ana process başlamadan hemen önce de çalıştırabilirsiniz.
### Tek Container { #single-container }
Basit bir kurulumda; **tek bir container** olup onun içinde birden fazla **worker process** (ya da sadece bir process) başlatıyorsanız, bu adımları aynı container içinde, uygulama process'ini başlatmadan hemen önce çalıştırabilirsiniz.
### Base Docker Image { #base-docker-image }
Eskiden resmi bir FastAPI Docker image'ı vardı: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Ancak artık kullanımdan kaldırıldı (deprecated). ⛔️
Muhtemelen bu base Docker image'ını (veya benzeri başka bir image'ı) kullanmamalısınız.
**Kubernetes** (veya diğerleri) kullanıyor ve cluster seviyesinde birden fazla **container** ile **replication** ayarlıyorsanız, bu durumda yukarıda anlatıldığı gibi **sıfırdan bir image build etmek** daha iyi olur: [FastAPI için Docker Image Oluşturalım](#build-a-docker-image-for-fastapi).
Ve birden fazla worker gerekiyorsa, sadece `--workers` komut satırı seçeneğini kullanabilirsiniz.
/// note | Teknik Detaylar
Bu Docker image, Uvicorn dead worker'ları yönetmeyi ve yeniden başlatmayı desteklemediği dönemde oluşturulmuştu. Bu yüzden Uvicorn ile birlikte Gunicorn kullanmak gerekiyordu; sırf Gunicorn, Uvicorn worker process'lerini yönetip yeniden başlatsın diye oldukça fazla karmaşıklık ekleniyordu.
Ancak artık Uvicorn (ve `fastapi` komutu) `--workers` kullanımını desteklediğine göre, kendi image'ınızı build etmek yerine bir base Docker image kullanmanın bir nedeni kalmadı (kod miktarı da hemen hemen aynı 😅).
///
## Container Image'ı Deploy Etme { #deploy-the-container-image }
Bir Container (Docker) Image'ınız olduktan sonra bunu deploy etmenin birkaç yolu vardır.
Örneğin:
* Tek bir server'da **Docker Compose** ile
* Bir **Kubernetes** cluster'ı ile
* Docker Swarm Mode cluster'ı ile
* Nomad gibi başka bir araçla
* Container image'ınızı alıp deploy eden bir cloud servisiyle
## `uv` ile Docker Image { #docker-image-with-uv }
Projenizi yüklemek ve yönetmek için [uv](https://github.com/astral-sh/uv) kullanıyorsanız, onların [uv Docker rehberini](https://docs.astral.sh/uv/guides/integration/docker/) takip edebilirsiniz.
## Özet { #recap }
Container sistemleri (örn. **Docker** ve **Kubernetes** ile) kullanınca tüm **deployment kavramları**nı ele almak oldukça kolaylaşır:
* HTTPS
* Startup'ta çalıştırma
* Restart'lar
* Replication (çalışan process sayısı)
* Memory
* Başlatmadan önceki adımlar
Çoğu durumda bir base image kullanmak istemezsiniz; bunun yerine resmi Python Docker image'ını temel alarak **sıfırdan bir container image** build edersiniz.
`Dockerfile` içindeki talimatların **sırasına** ve **Docker cache**'ine dikkat ederek **build sürelerini minimize edebilir**, üretkenliğinizi artırabilirsiniz (ve beklerken sıkılmayı önlersiniz). 😎
================================================
FILE: docs/tr/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
FastAPI uygulamanızı [FastAPI Cloud](https://fastapicloud.com)'a **tek bir komutla** deploy edebilirsiniz. Henüz yapmadıysanız gidip bekleme listesine katılın. 🚀
## Giriş Yapma { #login }
Önceden bir **FastAPI Cloud** hesabınız olduğundan emin olun (sizi bekleme listesinden davet ettik 😉).
Ardından giriş yapın:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Deploy { #deploy }
Şimdi uygulamanızı **tek bir komutla** deploy edin:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Hepsi bu! Artık uygulamanıza o URL üzerinden erişebilirsiniz. ✨
## FastAPI Cloud Hakkında { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**, **FastAPI**'nin arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir.
Bir API'yi minimum eforla **geliştirme**, **deploy etme** ve **erişilebilir kılma** sürecini sadeleştirir.
FastAPI ile uygulama geliştirirken elde ettiğiniz aynı **developer experience**'ı, onları buluta **deploy etmeye** de taşır. 🎉
Ayrıca bir uygulamayı deploy ederken ihtiyaç duyacağınız pek çok şeyi de sizin için halleder; örneğin:
* HTTPS
* Replication (çoğaltma), request'lere göre autoscaling ile
* vb.
FastAPI Cloud, *FastAPI and friends* açık kaynak projelerinin birincil sponsoru ve finansman sağlayıcısıdır. ✨
## Diğer cloud sağlayıcılarına deploy etme { #deploy-to-other-cloud-providers }
FastAPI açık kaynaklıdır ve standartlara dayanır. FastAPI uygulamalarını seçtiğiniz herhangi bir cloud sağlayıcısına deploy edebilirsiniz.
FastAPI uygulamalarını deploy etmek için cloud sağlayıcınızın kendi kılavuzlarını takip edin. 🤓
## Kendi server'ınıza deploy etme { #deploy-your-own-server }
Bu **Deployment** kılavuzunun ilerleyen bölümlerinde tüm detayları da ele alacağız; böylece neler olduğunu, nelerin gerçekleşmesi gerektiğini ve FastAPI uygulamalarını kendi başınıza (kendi server'larınızla da) nasıl deploy edebileceğinizi anlayacaksınız. 🤓
================================================
FILE: docs/tr/docs/deployment/https.md
================================================
# HTTPS Hakkında { #about-https }
HTTPS’in sadece "açık" ya da "kapalı" olan bir şey olduğunu düşünmek kolaydır.
Ancak bundan çok daha karmaşıktır.
/// tip | İpucu
Aceleniz varsa veya çok da önemsemiyorsanız, her şeyi farklı tekniklerle adım adım kurmak için sonraki bölümlere geçin.
///
Bir kullanıcı gözüyle **HTTPS’in temellerini öğrenmek** için [https://howhttps.works/](https://howhttps.works/) adresine bakın.
Şimdi de **geliştirici perspektifinden**, HTTPS hakkında düşünürken akılda tutulması gereken birkaç nokta:
* HTTPS için **server**’ın, **üçüncü bir taraf** tarafından verilen **"sertifikalara"** sahip olması gerekir.
* Bu sertifikalar aslında üçüncü tarafça "üretilmez", üçüncü taraftan **temin edilir**.
* Sertifikaların bir **geçerlilik süresi** vardır.
* Süresi **dolar**.
* Sonrasında **yenilenmeleri**, üçüncü taraftan **yeniden temin edilmeleri** gerekir.
* Bağlantının şifrelenmesi **TCP seviyesinde** gerçekleşir.
* Bu, **HTTP’nin bir katman altıdır**.
* Dolayısıyla **sertifika ve şifreleme** işlemleri **HTTP’den önce** yapılır.
* **TCP "domain"leri bilmez**. Yalnızca IP adreslerini bilir.
* İstenen **spesifik domain** bilgisi **HTTP verisinin** içindedir.
* **HTTPS sertifikaları** belirli bir **domain**’i "sertifikalandırır"; ancak protokol ve şifreleme TCP seviyesinde, hangi domain ile çalışıldığı **henüz bilinmeden** gerçekleşir.
* **Varsayılan olarak** bu, IP adresi başına yalnızca **bir HTTPS sertifikası** olabileceği anlamına gelir.
* Server’ınız ne kadar büyük olursa olsun ya da üzerindeki her uygulama ne kadar küçük olursa olsun.
* Ancak bunun bir **çözümü** vardır.
* **TLS** protokolüne (TCP seviyesinde, HTTP’den önce şifrelemeyi yapan) eklenen **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)** adlı bir **extension** vardır.
* Bu SNI extension’ı, tek bir server’ın (tek bir **IP adresiyle**) **birden fazla HTTPS sertifikası** kullanmasına ve **birden fazla HTTPS domain/uygulama** sunmasına izin verir.
* Bunun çalışması için server üzerinde, **public IP adresini** dinleyen tek bir bileşenin (programın) server’daki **tüm HTTPS sertifikalarına** sahip olması gerekir.
* Güvenli bir bağlantı elde edildikten **sonra**, iletişim protokolü **hâlâ HTTP**’dir.
* İçerikler, **HTTP protokolü** ile gönderiliyor olsa bile **şifrelenmiştir**.
Yaygın yaklaşım, server’da (makine, host vb.) çalışan **tek bir program/HTTP server** bulundurup **HTTPS ile ilgili tüm kısımları** yönetmektir: **şifreli HTTPS request**’leri almak, aynı server’da çalışan gerçek HTTP uygulamasına (bu örnekte **FastAPI** uygulaması) **şifresi çözülmüş HTTP request**’leri iletmek, uygulamadan gelen **HTTP response**’u almak, uygun **HTTPS sertifikası** ile **şifrelemek** ve **HTTPS** ile client’a geri göndermek. Bu server’a çoğu zaman **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)** denir.
TLS Termination Proxy olarak kullanabileceğiniz seçeneklerden bazıları:
* Traefik (sertifika yenilemelerini de yönetebilir)
* Caddy (sertifika yenilemelerini de yönetebilir)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
Let's Encrypt’ten önce bu **HTTPS sertifikaları**, güvenilen üçüncü taraflar tarafından satılırdı.
Bu sertifikalardan birini temin etme süreci zahmetliydi, epey evrak işi gerektirirdi ve sertifikalar oldukça pahalıydı.
Sonra **[Let's Encrypt](https://letsencrypt.org/)** ortaya çıktı.
Linux Foundation’ın bir projesidir. **HTTPS sertifikalarını ücretsiz** ve otomatik bir şekilde sağlar. Bu sertifikalar tüm standart kriptografik güvenliği kullanır ve kısa ömürlüdür (yaklaşık 3 ay). Bu yüzden, ömürleri kısa olduğu için **güvenlik aslında daha iyidir**.
Domain’ler güvenli şekilde doğrulanır ve sertifikalar otomatik üretilir. Bu sayede sertifikaların yenilenmesini otomatikleştirmek de mümkün olur.
Amaç, bu sertifikaların temin edilmesi ve yenilenmesini otomatikleştirerek **ücretsiz, kalıcı olarak güvenli HTTPS** sağlamaktır.
## Geliştiriciler İçin HTTPS { #https-for-developers }
Burada, bir HTTPS API’nin adım adım nasıl görünebileceğine dair, özellikle geliştiriciler için önemli fikirlere odaklanan bir örnek var.
### Domain Adı { #domain-name }
Muhtemelen her şey, bir **domain adı** **temin etmenizle** başlar. Sonra bunu bir DNS server’ında (muhtemelen aynı cloud provider’ınızda) yapılandırırsınız.
Muhtemelen bir cloud server (virtual machine) ya da benzeri bir şey alırsınız ve bunun sabit bir **public IP adresi** olur.
DNS server(lar)ında, bir kaydı ("`A record`") **domain**’inizi server’ınızın **public IP adresine** yönlendirecek şekilde yapılandırırsınız.
Bunu büyük olasılıkla ilk kurulumda, sadece bir kez yaparsınız.
/// tip | İpucu
Bu Domain Adı kısmı HTTPS’ten çok daha önce gelir. Ancak her şey domain ve IP adresine bağlı olduğu için burada bahsetmeye değer.
///
### DNS { #dns }
Şimdi gerçek HTTPS parçalarına odaklanalım.
Önce tarayıcı, bu örnekte `someapp.example.com` olan domain için **IP**’nin ne olduğunu **DNS server**’larına sorar.
DNS server’ları tarayıcıya belirli bir **IP adresini** kullanmasını söyler. Bu, DNS server’larında yapılandırdığınız ve server’ınızın kullandığı public IP adresidir.
### TLS Handshake Başlangıcı { #tls-handshake-start }
Tarayıcı daha sonra bu IP adresiyle **443 portu** (HTTPS portu) üzerinden iletişim kurar.
İletişimin ilk kısmı, client ile server arasında bağlantıyı kurmak ve hangi kriptografik anahtarların kullanılacağına karar vermek vb. içindir.
Client ile server arasındaki, TLS bağlantısını kurmaya yönelik bu etkileşime **TLS handshake** denir.
### SNI Extension’ı ile TLS { #tls-with-sni-extension }
Server’da, belirli bir **IP adresindeki** belirli bir **portu** dinleyen **yalnızca bir process** olabilir. Aynı IP adresinde başka portları dinleyen başka process’ler olabilir, ancak IP+port kombinasyonu başına yalnızca bir tane olur.
TLS (HTTPS) varsayılan olarak `443` portunu kullanır. Yani ihtiyaç duyacağımız port budur.
Bu portu yalnızca bir process dinleyebileceği için, bunu yapacak process **TLS Termination Proxy** olur.
TLS Termination Proxy, bir ya da daha fazla **TLS sertifikasına** (HTTPS sertifikası) erişebilir.
Yukarıda bahsettiğimiz **SNI extension**’ını kullanarak TLS Termination Proxy, bu bağlantı için elindeki TLS (HTTPS) sertifikalarından hangisini kullanacağını kontrol eder; client’ın beklediği domain ile eşleşen sertifikayı seçer.
Bu örnekte `someapp.example.com` sertifikasını kullanır.
Client, bu TLS sertifikasını üreten kuruluşa zaten **güvenir** (bu örnekte Let's Encrypt; birazdan ona da geleceğiz). Bu sayede sertifikanın geçerli olduğunu **doğrulayabilir**.
Ardından client ve TLS Termination Proxy, sertifikayı kullanarak **TCP iletişiminin geri kalanını nasıl şifreleyeceklerine** karar verir. Böylece **TLS Handshake** kısmı tamamlanır.
Bundan sonra client ve server arasında **şifreli bir TCP bağlantısı** vardır; TLS’in sağladığı şey budur. Sonra bu bağlantıyı kullanarak gerçek **HTTP iletişimini** başlatabilirler.
Ve **HTTPS** de tam olarak budur: şifrelenmemiş bir TCP bağlantısı yerine, **güvenli bir TLS bağlantısının içinde** düz **HTTP**’dir.
/// tip | İpucu
Şifrelemenin HTTP seviyesinde değil, **TCP seviyesinde** gerçekleştiğine dikkat edin.
///
### HTTPS Request { #https-request }
Artık client ile server (özellikle tarayıcı ile TLS Termination Proxy) arasında **şifreli bir TCP bağlantısı** olduğuna göre, **HTTP iletişimi** başlayabilir.
Dolayısıyla client bir **HTTPS request** gönderir. Bu, şifreli bir TLS bağlantısı üzerinden giden bir HTTP request’tir.
### Request’in Şifresini Çözme { #decrypt-the-request }
TLS Termination Proxy, üzerinde anlaşılan şifrelemeyi kullanarak **request’in şifresini çözer** ve **düz (şifresi çözülmüş) HTTP request**’i uygulamayı çalıştıran process’e iletir (ör. FastAPI uygulamasını çalıştıran Uvicorn process’i).
### HTTP Response { #http-response }
Uygulama request’i işler ve TLS Termination Proxy’ye **düz (şifrelenmemiş) bir HTTP response** gönderir.
### HTTPS Response { #https-response }
TLS Termination Proxy daha sonra response’u, daha önce üzerinde anlaşılan kriptografi ile (başlangıcı `someapp.example.com` sertifikasına dayanan) **şifreler** ve tarayıcıya geri gönderir.
Sonrasında tarayıcı response’un geçerli olduğunu ve doğru kriptografik anahtarla şifrelendiğini doğrular vb. Ardından **response’un şifresini çözer** ve işler.
Client (tarayıcı), response’un doğru server’dan geldiğini bilir; çünkü daha önce **HTTPS sertifikası** ile üzerinde anlaştıkları kriptografiyi kullanmaktadır.
### Birden Fazla Uygulama { #multiple-applications }
Aynı server’da (veya server’larda) örneğin başka API programları ya da bir veritabanı gibi **birden fazla uygulama** olabilir.
Belirli IP ve port kombinasyonunu yalnızca bir process yönetebilir (örneğimizde TLS Termination Proxy). Ancak diğer uygulamalar/process’ler, aynı **public IP + port kombinasyonunu** kullanmaya çalışmadıkları sürece server(lar)da çalışabilir.
Bu şekilde TLS Termination Proxy, birden fazla uygulama için **birden fazla domain**’in HTTPS ve sertifika işlerini yönetebilir ve her durumda request’leri doğru uygulamaya iletebilir.
### Sertifika Yenileme { #certificate-renewal }
Gelecekte bir noktada, her sertifikanın süresi **dolar** (temin edildikten yaklaşık 3 ay sonra).
Ardından başka bir program (bazı durumlarda ayrı bir programdır, bazı durumlarda aynı TLS Termination Proxy olabilir) Let's Encrypt ile konuşup sertifika(ları) yeniler.
**TLS sertifikaları** bir IP adresiyle değil, **domain adıyla ilişkilidir**.
Bu yüzden sertifikaları yenilemek için, yenileme programı otoriteye (Let's Encrypt) gerçekten o domain’i **"sahiplendiğini" ve kontrol ettiğini** **kanıtlamalıdır**.
Bunu yapmak ve farklı uygulama ihtiyaçlarını karşılamak için birden fazla yöntem vardır. Yaygın yöntemlerden bazıları:
* Bazı **DNS kayıtlarını değiştirmek**.
* Bunun için yenileme programının DNS provider API’lerini desteklemesi gerekir. Dolayısıyla kullandığınız DNS provider’a bağlı olarak bu seçenek mümkün de olabilir, olmayabilir de.
* Domain ile ilişkili public IP adresinde **server olarak çalışmak** (en azından sertifika temin sürecinde).
* Yukarıda söylediğimiz gibi, belirli bir IP ve portu yalnızca bir process dinleyebilir.
* Bu, aynı TLS Termination Proxy’nin sertifika yenileme sürecini de yönetmesinin neden çok faydalı olduğunun sebeplerinden biridir.
* Aksi halde TLS Termination Proxy’yi kısa süreliğine durdurmanız, sertifikaları temin etmek için yenileme programını başlatmanız, sonra bunları TLS Termination Proxy ile yapılandırmanız ve ardından TLS Termination Proxy’yi tekrar başlatmanız gerekebilir. Bu ideal değildir; çünkü TLS Termination Proxy kapalıyken uygulama(lar)ınıza erişilemez.
Uygulamayı servis etmeye devam ederken tüm bu yenileme sürecini yönetebilmek, TLS sertifikalarını doğrudan uygulama server’ıyla (örn. Uvicorn) kullanmak yerine, TLS Termination Proxy ile HTTPS’i yönetecek **ayrı bir sistem** istemenizin başlıca nedenlerinden biridir.
## Proxy Forwarded Headers { #proxy-forwarded-headers }
HTTPS’i bir proxy ile yönetirken, **application server**’ınız (örneğin FastAPI CLI üzerinden Uvicorn) HTTPS süreci hakkında hiçbir şey bilmez; **TLS Termination Proxy** ile düz HTTP üzerinden iletişim kurar.
Bu **proxy** normalde request’i **application server**’a iletmeden önce, request’in proxy tarafından **forward** edildiğini application server’a bildirmek için bazı HTTP header’larını anlık olarak ekler.
/// note | Teknik Detaylar
Proxy header’ları şunlardır:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Buna rağmen **application server**, güvenilen bir **proxy** arkasında olduğunu bilmediği için varsayılan olarak bu header’lara güvenmez.
Ancak **application server**’ı, **proxy**’nin gönderdiği *forwarded* header’larına güvenecek şekilde yapılandırabilirsiniz. FastAPI CLI kullanıyorsanız, hangi IP’lerden gelen *forwarded* header’lara güvenmesi gerektiğini söylemek için *CLI Option* `--forwarded-allow-ips` seçeneğini kullanabilirsiniz.
Örneğin **application server** yalnızca güvenilen **proxy**’den iletişim alıyorsa, yalnızca **proxy**’nin kullandığı IP’den request alacağı için `--forwarded-allow-ips="*"` ayarlayıp gelen tüm IP’lere güvenmesini sağlayabilirsiniz.
Bu sayede uygulama kendi public URL’inin ne olduğunu, HTTPS kullanıp kullanmadığını, domain’i vb. bilebilir.
Bu, örneğin redirect’leri doğru şekilde yönetmek için faydalıdır.
/// tip | İpucu
Bununla ilgili daha fazlasını [Bir Proxy Arkasında - Proxy Forwarded Headers'ı Etkinleştir](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers) dokümantasyonunda öğrenebilirsiniz.
///
## Özet { #recap }
**HTTPS** kullanmak çok önemlidir ve çoğu durumda oldukça **kritiktir**. Geliştirici olarak HTTPS etrafında harcadığınız çabanın büyük kısmı, aslında **bu kavramları** ve nasıl çalıştıklarını **anlamaktır**.
Ancak **geliştiriciler için HTTPS**’in temel bilgilerini öğrendikten sonra, her şeyi basitçe yönetmek için farklı araçları kolayca birleştirip yapılandırabilirsiniz.
Sonraki bölümlerin bazılarında, **FastAPI** uygulamaları için **HTTPS**’i nasıl kuracağınıza dair birkaç somut örnek göstereceğim. 🔒
================================================
FILE: docs/tr/docs/deployment/index.md
================================================
# Dağıtım { #deployment }
**FastAPI** uygulamasını deploy etmek nispeten kolaydır.
## Dağıtım Ne Anlama Gelir? { #what-does-deployment-mean }
Bir uygulamayı **deploy** etmek, onu **kullanıcılara erişilebilir hale getirmek** için gerekli adımları gerçekleştirmek anlamına gelir.
Bir **web API** için bu süreç normalde uygulamayı **uzak bir makineye** yerleştirmeyi, iyi performans, kararlılık vb. özellikler sağlayan bir **sunucu programı** ile **kullanıcılarınızın** uygulamaya etkili ve kesintisiz bir şekilde, sorun yaşamadan **erişebilmesini** kapsar.
Bu, kodu sürekli olarak değiştirdiğiniz, bozup düzelttiğiniz, geliştirme sunucusunu durdurup yeniden başlattığınız vb. **geliştirme** aşamalarının tam tersidir.
## Dağıtım Stratejileri { #deployment-strategies }
Kullanım durumunuza ve kullandığınız araçlara bağlı olarak bunu yapmanın birkaç yolu vardır.
Bir dizi araç kombinasyonunu kullanarak kendiniz **bir sunucu deploy edebilirsiniz**, yayınlama sürecinin bir kısmını sizin için gerçekleştiren bir **bulut hizmeti** veya diğer olası seçenekleri kullanabilirsiniz.
Örneğin, FastAPI'nin arkasındaki ekip olarak, FastAPI uygulamalarını buluta mümkün olduğunca akıcı şekilde deploy etmeyi sağlamak için, FastAPI ile çalışmanın aynı geliştirici deneyimini sunarak [**FastAPI Cloud**](https://fastapicloud.com)'u oluşturduk.
**FastAPI** uygulamasını yayınlarken aklınızda bulundurmanız gereken ana kavramlardan bazılarını size göstereceğim (ancak bunların çoğu diğer web uygulamaları için de geçerlidir).
Sonraki bölümlerde akılda tutulması gereken diğer ayrıntıları ve bunu yapmaya yönelik bazı teknikleri göreceksiniz. ✨
================================================
FILE: docs/tr/docs/deployment/manually.md
================================================
# Bir Sunucuyu Manuel Olarak Çalıştırın { #run-a-server-manually }
## `fastapi run` Komutunu Kullanın { #use-the-fastapi-run-command }
Kısacası, FastAPI uygulamanızı sunmak için `fastapi run` kullanın:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Bu, çoğu durumda işinizi görür. 😎
Örneğin bu komutu, **FastAPI** app'inizi bir container içinde, bir sunucuda vb. başlatmak için kullanabilirsiniz.
## ASGI Sunucuları { #asgi-servers }
Şimdi biraz daha detaya inelim.
FastAPI, Python web framework'leri ve sunucularını inşa etmek için kullanılan ASGI adlı bir standardı kullanır. FastAPI bir ASGI web framework'üdür.
Uzak bir sunucu makinesinde **FastAPI** uygulamasını (veya herhangi bir ASGI uygulamasını) çalıştırmak için gereken ana şey, **Uvicorn** gibi bir ASGI server programıdır. `fastapi` komutuyla varsayılan olarak gelen de budur.
Buna alternatif birkaç seçenek daha vardır, örneğin:
* [Uvicorn](https://www.uvicorn.dev/): yüksek performanslı bir ASGI server.
* [Hypercorn](https://hypercorn.readthedocs.io/): diğer özelliklerin yanında HTTP/2 ve Trio ile uyumlu bir ASGI server.
* [Daphne](https://github.com/django/daphne): Django Channels için geliştirilmiş ASGI server.
* [Granian](https://github.com/emmett-framework/granian): Python uygulamaları için bir Rust HTTP server.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit, hafif ve çok yönlü bir web uygulaması runtime'ıdır.
## Sunucu Makinesi ve Sunucu Programı { #server-machine-and-server-program }
İsimlendirme konusunda akılda tutulması gereken küçük bir detay var. 💡
"**server**" kelimesi yaygın olarak hem uzak/bulut bilgisayarı (fiziksel veya sanal makine) hem de o makinede çalışan programı (ör. Uvicorn) ifade etmek için kullanılır.
Dolayısıyla genel olarak "server" dendiğinde, bu iki şeyden birini kast ediyor olabilir.
Uzak makineden bahsederken genelde **server** denir; ayrıca **machine**, **VM** (virtual machine), **node** ifadeleri de kullanılır. Bunların hepsi, genellikle Linux çalıştıran ve üzerinde programlarınızı çalıştırdığınız bir tür uzak makineyi ifade eder.
## Sunucu Programını Yükleyin { #install-the-server-program }
FastAPI'yi kurduğunuzda, production sunucusu olarak Uvicorn da beraberinde gelir ve bunu `fastapi run` komutuyla başlatabilirsiniz.
Ancak bir ASGI server'ı manuel olarak da kurabilirsiniz.
Bir [sanal ortam](../virtual-environments.md) oluşturduğunuzdan, etkinleştirdiğinizden emin olun; ardından server uygulamasını kurabilirsiniz.
Örneğin Uvicorn'u kurmak için:
Benzer bir süreç, diğer ASGI server programlarının tamamı için de geçerlidir.
/// tip | İpucu
`standard` eklediğinizde Uvicorn, önerilen bazı ek bağımlılıkları kurar ve kullanır.
Bunlara, `asyncio` için yüksek performanslı bir drop-in replacement olan ve concurrency performansını ciddi şekilde artıran `uvloop` da dahildir.
FastAPI'yi `pip install "fastapi[standard]"` gibi bir şekilde kurduğunuzda `uvicorn[standard]` da zaten kurulmuş olur.
///
## Sunucu Programını Çalıştırın { #run-the-server-program }
Bir ASGI server'ı manuel olarak kurduysanız, FastAPI uygulamanızı import edebilmesi için genellikle özel bir formatta bir import string geçirmeniz gerekir:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Not
`uvicorn main:app` komutu şunları ifade eder:
* `main`: `main.py` dosyası (Python "module").
* `app`: `main.py` içinde `app = FastAPI()` satırıyla oluşturulan nesne.
Şununla eşdeğerdir:
```Python
from main import app
```
///
Her alternatif ASGI server programı için benzer bir komut bulunur; daha fazlası için ilgili dokümantasyonlarına bakabilirsiniz.
/// warning | Uyarı
Uvicorn ve diğer sunucular, geliştirme sırasında faydalı olan `--reload` seçeneğini destekler.
`--reload` seçeneği çok daha fazla kaynak tüketir, daha kararsızdır vb.
**Geliştirme** sırasında çok yardımcı olur, ancak **production** ortamında kullanmamalısınız.
///
## Deployment Kavramları { #deployment-concepts }
Bu örnekler server programını (ör. Uvicorn) çalıştırır; **tek bir process** başlatır, tüm IP'lerde (`0.0.0.0`) ve önceden belirlenmiş bir port'ta (ör. `80`) dinler.
Temel fikir budur. Ancak muhtemelen şunlar gibi bazı ek konularla da ilgilenmek isteyeceksiniz:
* Güvenlik - HTTPS
* Açılışta çalıştırma
* Yeniden başlatmalar
* Replikasyon (çalışan process sayısı)
* Bellek
* Başlatmadan önceki adımlar
Sonraki bölümlerde bu kavramların her birini nasıl düşünmeniz gerektiğini ve bunlarla başa çıkmak için kullanabileceğiniz somut örnekleri/stratejileri anlatacağım. 🚀
================================================
FILE: docs/tr/docs/deployment/server-workers.md
================================================
# Server Workers - Worker'larla Uvicorn { #server-workers-uvicorn-with-workers }
Önceki bölümlerde bahsettiğimiz deployment kavramlarına tekrar bakalım:
* Güvenlik - HTTPS
* Başlangıçta çalıştırma
* Yeniden başlatmalar
* **Replikasyon (çalışan process sayısı)**
* Bellek
* Başlatmadan önceki adımlar
Bu noktaya kadar, dokümantasyondaki tüm tutorial'larla muhtemelen bir server programı çalıştırıyordunuz; örneğin Uvicorn'u çalıştıran `fastapi` komutunu kullanarak ve tek bir process ile.
Uygulamaları deploy ederken, çok çekirdekten (multiple cores) faydalanmak ve daha fazla request'i karşılayabilmek için büyük olasılıkla process replikasyonu (birden fazla process) isteyeceksiniz.
[Daha önceki Deployment Concepts](concepts.md) bölümünde gördüğünüz gibi, kullanabileceğiniz birden fazla strateji var.
Burada, `fastapi` komutunu kullanarak ya da `uvicorn` komutunu doğrudan çalıştırarak worker process'lerle Uvicorn'u nasıl kullanacağınızı göstereceğim.
/// info | Bilgi
Container kullanıyorsanız (örneğin Docker veya Kubernetes ile), bununla ilgili daha fazlasını bir sonraki bölümde anlatacağım: [Container'larda FastAPI - Docker](docker.md).
Özellikle Kubernetes üzerinde çalıştırırken, büyük olasılıkla worker kullanmak istemeyeceksiniz; bunun yerine container başına tek bir Uvicorn process çalıştırmak daha uygundur. Ancak bunu da o bölümde detaylandıracağım.
///
## Birden Fazla Worker { #multiple-workers }
Komut satırında `--workers` seçeneğiyle birden fazla worker başlatabilirsiniz:
//// tab | `fastapi`
`fastapi` komutunu kullanıyorsanız:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
`uvicorn` komutunu doğrudan kullanmayı tercih ederseniz:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
Buradaki tek yeni seçenek `--workers`; bu seçenek Uvicorn'a 4 adet worker process başlatmasını söyler.
Ayrıca her process'in PID'inin gösterildiğini de görebilirsiniz: parent process için `27365` (bu process manager), her worker process için de bir PID: `27368`, `27369`, `27370` ve `27367`.
## Deployment Kavramları { #deployment-concepts }
Burada, uygulamanın çalışmasını paralelleştirmek, CPU'daki çok çekirdekten yararlanmak ve daha fazla request karşılayabilmek için birden fazla worker'ı nasıl kullanacağınızı gördünüz.
Yukarıdaki deployment kavramları listesinden, worker kullanımı ağırlıklı olarak replikasyon kısmına yardımcı olur, ayrıca yeniden başlatmalar konusunda da az da olsa katkı sağlar. Ancak diğerlerini yine sizin yönetmeniz gerekir:
* **Güvenlik - HTTPS**
* **Başlangıçta çalıştırma**
* ***Yeniden başlatmalar***
* Replikasyon (çalışan process sayısı)
* **Bellek**
* **Başlatmadan önceki adımlar**
## Container'lar ve Docker { #containers-and-docker }
Bir sonraki bölümde, [Container'larda FastAPI - Docker](docker.md) üzerinden diğer deployment kavramlarını ele almak için kullanabileceğiniz bazı stratejileri anlatacağım.
Tek bir Uvicorn process çalıştıracak şekilde sıfırdan kendi image'ınızı oluşturmayı göstereceğim. Bu oldukça basit bir süreçtir ve Kubernetes gibi dağıtık bir container yönetim sistemi kullanırken büyük olasılıkla yapmak isteyeceğiniz şey de budur.
## Özet { #recap }
Çok çekirdekli CPU'lardan faydalanmak ve birden fazla process'i paralel çalıştırmak için `fastapi` veya `uvicorn` komutlarıyla `--workers` CLI seçeneğini kullanarak birden fazla worker process çalıştırabilirsiniz.
Diğer deployment kavramlarını da kendiniz ele alarak kendi deployment sisteminizi kuruyorsanız, bu araçları ve fikirleri kullanabilirsiniz.
Container'larla (örn. Docker ve Kubernetes) FastAPI'yi öğrenmek için bir sonraki bölüme göz atın. Bu araçların, diğer deployment kavramlarını çözmek için de basit yöntemleri olduğunu göreceksiniz. ✨
================================================
FILE: docs/tr/docs/deployment/versions.md
================================================
# FastAPI Sürümleri Hakkında { #about-fastapi-versions }
**FastAPI** hâlihazırda birçok uygulama ve sistemde production ortamında kullanılmaktadır. Ayrıca test kapsamı %100 seviyesinde tutulmaktadır. Ancak geliştirme süreci hâlâ hızlı şekilde ilerlemektedir.
Yeni özellikler sık sık eklenir, bug'lar düzenli olarak düzeltilir ve kod sürekli iyileştirilmektedir.
Bu yüzden mevcut sürümler hâlâ `0.x.x` şeklindedir; bu da her sürümde breaking change olma ihtimalini yansıtır. Bu yaklaşım [Anlamsal Sürümleme](https://semver.org/) kurallarını takip eder.
Şu anda **FastAPI** ile production uygulamaları geliştirebilirsiniz (muhtemelen bir süredir yapıyorsunuz da); sadece kodunuzun geri kalanıyla doğru çalışan bir sürüm kullandığınızdan emin olmanız gerekir.
## `fastapi` sürümünü sabitleyin { #pin-your-fastapi-version }
İlk yapmanız gereken, kullandığınız **FastAPI** sürümünü uygulamanızla doğru çalıştığını bildiğiniz belirli bir güncel sürüme "sabitlemek" (pinlemek) olmalı.
Örneğin, uygulamanızda `0.112.0` sürümünü kullandığınızı varsayalım.
`requirements.txt` dosyası kullanıyorsanız sürümü şöyle belirtebilirsiniz:
```txt
fastapi[standard]==0.112.0
```
Bu, tam olarak `0.112.0` sürümünü kullanacağınız anlamına gelir.
Ya da şu şekilde de sabitleyebilirsiniz:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
Bu da `0.112.0` ve üzeri, ama `0.113.0` altındaki sürümleri kullanacağınız anlamına gelir; örneğin `0.112.2` gibi bir sürüm de kabul edilir.
Kurulumları yönetmek için `uv`, Poetry, Pipenv gibi başka bir araç (veya benzerleri) kullanıyorsanız, bunların hepsinde paketler için belirli sürümler tanımlamanın bir yolu vardır.
## Mevcut sürümler { #available-versions }
Mevcut sürümleri (ör. en güncel son sürümün hangisi olduğunu kontrol etmek için) [Sürüm Notları](../release-notes.md) sayfasında görebilirsiniz.
## Sürümler Hakkında { #about-versions }
Semantic Versioning kurallarına göre, `1.0.0` altındaki herhangi bir sürüm breaking change içerebilir.
FastAPI ayrıca "PATCH" sürüm değişikliklerinin bug fix'ler ve breaking olmayan değişiklikler için kullanılması kuralını da takip eder.
/// tip | İpucu
"PATCH" son sayıdır. Örneğin `0.2.3` içinde PATCH sürümü `3`'tür.
///
Dolayısıyla şu şekilde bir sürüme sabitleyebilmelisiniz:
```txt
fastapi>=0.45.0,<0.46.0
```
Breaking change'ler ve yeni özellikler "MINOR" sürümlerde eklenir.
/// tip | İpucu
"MINOR" ortadaki sayıdır. Örneğin `0.2.3` içinde MINOR sürümü `2`'dir.
///
## FastAPI Sürümlerini Yükseltme { #upgrading-the-fastapi-versions }
Uygulamanız için test'ler eklemelisiniz.
**FastAPI** ile bu çok kolaydır (Starlette sayesinde). Dokümantasyona bakın: [Testler](../tutorial/testing.md)
Test'leriniz olduktan sonra **FastAPI** sürümünü daha yeni bir sürüme yükseltebilir ve test'lerinizi çalıştırarak tüm kodunuzun doğru çalıştığından emin olabilirsiniz.
Her şey çalışıyorsa (ya da gerekli değişiklikleri yaptıktan sonra) ve tüm test'leriniz geçiyorsa, `fastapi` sürümünü o yeni sürüme sabitleyebilirsiniz.
## Starlette Hakkında { #about-starlette }
`starlette` sürümünü sabitlememelisiniz.
**FastAPI**'nin farklı sürümleri, Starlette'in belirli (daha yeni) bir sürümünü kullanır.
Bu yüzden **FastAPI**'nin doğru Starlette sürümünü kullanmasına izin verebilirsiniz.
## Pydantic Hakkında { #about-pydantic }
Pydantic, **FastAPI** için olan test'leri kendi test'lerinin içine dahil eder; bu yüzden Pydantic'in yeni sürümleri (`1.0.0` üzeri) her zaman FastAPI ile uyumludur.
Pydantic'i sizin için çalışan `1.0.0` üzerindeki herhangi bir sürüme sabitleyebilirsiniz.
Örneğin:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/tr/docs/editor-support.md
================================================
# Editör Desteği { #editor-support }
Resmi [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode), FastAPI geliştirme akışınızı iyileştirir: *path operation* keşfi, gezinme, FastAPI Cloud’a deploy ve canlı log akışı.
Daha fazla ayrıntı için, GitHub deposundaki README’ye bakın: [GitHub repository](https://github.com/fastapi/fastapi-vscode).
## Kurulum ve Yükleme { #setup-and-installation }
**FastAPI Extension**, hem [VS Code](https://code.visualstudio.com/) hem de [Cursor](https://www.cursor.com/) için mevcuttur. Her editörde Extensions panelinden "FastAPI" aratıp **FastAPI Labs** tarafından yayımlanan eklentiyi seçerek doğrudan kurabilirsiniz. Eklenti [vscode.dev](https://vscode.dev) ve [github.dev](https://github.dev) gibi tarayıcı tabanlı editörlerde de çalışır.
### Uygulama Keşfi { #application-discovery }
Varsayılan olarak, eklenti çalışma alanınızda `FastAPI()` örnekleyen dosyaları tarayarak FastAPI uygulamalarını otomatik olarak keşfeder. Proje yapınız nedeniyle otomatik algılama çalışmazsa, `pyproject.toml` içindeki `[tool.fastapi]` ile veya VS Code ayarı `fastapi.entryPoint` üzerinden modül gösterimiyle (ör. `myapp.main:app`) bir entrypoint belirtebilirsiniz.
## Özellikler { #features }
- **Path Operation Explorer** - Uygulamanızdaki tüm *path operation*'lar için yan panelde bir ağaç görünümü. Herhangi bir route veya router tanımına tıklayarak atlayın.
- **Route Search** - Ctrl + Shift + E (macOS: Cmd + Shift + E) ile path, method veya ada göre arama.
- **CodeLens Navigation** - Test client çağrılarının (ör. `client.get('/items')`) üzerinde, ilgili *path operation*’a atlayan tıklanabilir bağlantılar; testlerle implementasyon arasında hızlı gezinme sağlar.
- **Deploy to FastAPI Cloud** - Uygulamanızı tek tıkla [FastAPI Cloud](https://fastapicloud.com/)'a deploy edin.
- **Stream Application Logs** - FastAPI Cloud’a deploy ettiğiniz uygulamadan, seviye filtreleme ve metin arama ile gerçek zamanlı log akışı.
Eklentinin özelliklerine hızlıca aşina olmak isterseniz, Komut Paleti’ni açın (Ctrl + Shift + P veya macOS: Cmd + Shift + P), "Welcome: Open walkthrough..." öğesini seçin ve ardından "Get started with FastAPI" walkthrough’unu açın.
================================================
FILE: docs/tr/docs/environment-variables.md
================================================
# Ortam Değişkenleri { #environment-variables }
/// tip | İpucu
"Ortam değişkenleri"nin ne olduğunu ve nasıl kullanılacağını zaten biliyorsanız, bu bölümü atlayabilirsiniz.
///
Ortam değişkeni (genelde "**env var**" olarak da anılır), Python kodunun **dışında**, **işletim sistemi** seviyesinde bulunan ve Python kodunuz (veya diğer programlar) tarafından okunabilen bir değişkendir.
Ortam değişkenleri; uygulama **ayarları**nı yönetmek, Python’un **kurulumu**nun bir parçası olarak konfigürasyon yapmak vb. durumlarda işe yarar.
## Env Var Oluşturma ve Kullanma { #create-and-use-env-vars }
Python’a ihtiyaç duymadan, **shell (terminal)** içinde ortam değişkenleri **oluşturabilir** ve kullanabilirsiniz:
//// tab | Linux, macOS, Windows Bash
```console
// You could create an env var MY_NAME with
$ export MY_NAME="Wade Wilson"
// Then you could use it with other programs, like
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Create an env var MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Use it with other programs, like
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Python’da Env Var Okuma { #read-env-vars-in-python }
Ortam değişkenlerini Python’un **dışında** (terminalde veya başka bir yöntemle) oluşturup daha sonra **Python’da okuyabilirsiniz**.
Örneğin `main.py` adında bir dosyanız şöyle olabilir:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | İpucu
[`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) fonksiyonunun ikinci argümanı, bulunamadığında döndürülecek varsayılan (default) değerdir.
Verilmezse varsayılan olarak `None` olur; burada varsayılan değer olarak `"World"` verdik.
///
Sonrasında bu Python programını çalıştırabilirsiniz:
//// tab | Linux, macOS, Windows Bash
```console
// Here we don't set the env var yet
$ python main.py
// As we didn't set the env var, we get the default value
Hello World from Python
// But if we create an environment variable first
$ export MY_NAME="Wade Wilson"
// And then call the program again
$ python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Here we don't set the env var yet
$ python main.py
// As we didn't set the env var, we get the default value
Hello World from Python
// But if we create an environment variable first
$ $Env:MY_NAME = "Wade Wilson"
// And then call the program again
$ python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
```
////
Ortam değişkenleri kodun dışında ayarlanabildiği, ama kod tarafından okunabildiği ve dosyalarla birlikte saklanmasının (ör. `git`’e commit edilmesinin) gerekmediği için, konfigürasyon veya **ayarlar** için sıkça kullanılır.
Ayrıca, bir ortam değişkenini yalnızca **belirli bir program çalıştırımı** için oluşturabilirsiniz; bu değişken sadece o program tarafından, sadece o çalıştırma süresince kullanılabilir.
Bunu yapmak için, program komutunun hemen öncesinde ve aynı satırda tanımlayın:
```console
// Create an env var MY_NAME in line for this program call
$ MY_NAME="Wade Wilson" python main.py
// Now it can read the environment variable
Hello Wade Wilson from Python
// The env var no longer exists afterwards
$ python main.py
Hello World from Python
```
/// tip | İpucu
Bu konuyla ilgili daha fazlasını [Twelve-Factor Uygulaması: Config](https://12factor.net/config) bölümünde okuyabilirsiniz.
///
## Türler ve Doğrulama { #types-and-validation }
Bu ortam değişkenleri yalnızca **metin string**’lerini taşıyabilir. Çünkü Python’un dışındadırlar ve diğer programlarla, sistemin geri kalanıyla (hatta Linux, Windows, macOS gibi farklı işletim sistemleriyle) uyumlu olmak zorundadırlar.
Bu, Python’da bir ortam değişkeninden okunan **her değerin `str` olacağı** anlamına gelir. Farklı bir tipe dönüştürme veya doğrulama işlemleri kod içinde yapılmalıdır.
Uygulama **ayarları**nı yönetmek için ortam değişkenlerini kullanmayı, [İleri Seviye Kullanıcı Rehberi - Ayarlar ve Ortam Değişkenleri](./advanced/settings.md) bölümünde daha detaylı öğreneceksiniz.
## `PATH` Ortam Değişkeni { #path-environment-variable }
İşletim sistemlerinin (Linux, macOS, Windows) çalıştırılacak programları bulmak için kullandığı **özel** bir ortam değişkeni vardır: **`PATH`**.
`PATH` değişkeninin değeri uzun bir string’dir; Linux ve macOS’te dizinler iki nokta üst üste `:` ile, Windows’ta ise noktalı virgül `;` ile ayrılır.
Örneğin `PATH` ortam değişkeni şöyle görünebilir:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Bu, sistemin şu dizinlerde program araması gerektiği anlamına gelir:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Bu, sistemin şu dizinlerde program araması gerektiği anlamına gelir:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Terminalde bir **komut** yazdığınızda, işletim sistemi `PATH` ortam değişkeninde listelenen **bu dizinlerin her birinde** programı **arar**.
Örneğin terminalde `python` yazdığınızda, işletim sistemi bu listedeki **ilk dizinde** `python` adlı bir program arar.
Bulursa **onu kullanır**. Bulamazsa **diğer dizinlerde** aramaya devam eder.
### Python Kurulumu ve `PATH`’in Güncellenmesi { #installing-python-and-updating-the-path }
Python’u kurarken, `PATH` ortam değişkenini güncellemek isteyip istemediğiniz sorulabilir.
//// tab | Linux, macOS
Diyelim ki Python’u kurdunuz ve `/opt/custompython/bin` dizinine yüklendi.
`PATH` ortam değişkenini güncellemeyi seçerseniz, kurulum aracı `/opt/custompython/bin` yolunu `PATH` ortam değişkenine ekler.
Şöyle görünebilir:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Böylece terminalde `python` yazdığınızda, sistem `/opt/custompython/bin` (son dizin) içindeki Python programını bulur ve onu kullanır.
////
//// tab | Windows
Diyelim ki Python’u kurdunuz ve `C:\opt\custompython\bin` dizinine yüklendi.
`PATH` ortam değişkenini güncellemeyi seçerseniz, kurulum aracı `C:\opt\custompython\bin` yolunu `PATH` ortam değişkenine ekler.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Böylece terminalde `python` yazdığınızda, sistem `C:\opt\custompython\bin` (son dizin) içindeki Python programını bulur ve onu kullanır.
////
Yani şunu yazarsanız:
```console
$ python
```
//// tab | Linux, macOS
Sistem `python` programını `/opt/custompython/bin` içinde **bulur** ve çalıştırır.
Bu, kabaca şunu yazmaya denktir:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
Sistem `python` programını `C:\opt\custompython\bin\python` içinde **bulur** ve çalıştırır.
Bu, kabaca şunu yazmaya denktir:
```console
$ C:\opt\custompython\bin\python
```
////
Bu bilgiler, [Sanal Ortamlar](virtual-environments.md) konusunu öğrenirken işinize yarayacak.
## Sonuç { #conclusion }
Buraya kadar **ortam değişkenleri**nin ne olduğuna ve Python’da nasıl kullanılacağına dair temel bir fikir edinmiş olmalısınız.
Ayrıca [Ortam Değişkeni için Wikipedia](https://en.wikipedia.org/wiki/Environment_variable) sayfasından daha fazlasını da okuyabilirsiniz.
Çoğu zaman ortam değişkenlerinin hemen nasıl işe yarayacağı ilk bakışta çok net olmayabilir. Ancak geliştirme yaparken birçok farklı senaryoda tekrar tekrar karşınıza çıkarlar; bu yüzden bunları bilmek faydalıdır.
Örneğin bir sonraki bölümde, [Sanal Ortamlar](virtual-environments.md) konusunda bu bilgilere ihtiyaç duyacaksınız.
================================================
FILE: docs/tr/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI**, FastAPI uygulamanızı servis etmek, FastAPI projenizi yönetmek ve daha fazlası için kullanabileceğiniz bir komut satırı programıdır.
FastAPI'yi kurduğunuzda (ör. `pip install "fastapi[standard]"`), terminalde çalıştırabileceğiniz bir komut satırı programı birlikte gelir.
FastAPI uygulamanızı geliştirme için çalıştırmak üzere `fastapi dev` komutunu kullanabilirsiniz:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | İpucu
Production için `fastapi dev` yerine `fastapi run` kullanırsınız. 🚀
///
İçeride, **FastAPI CLI**, yüksek performanslı, production'a hazır bir ASGI server olan [Uvicorn](https://www.uvicorn.dev)'u kullanır. 😎
`fastapi` CLI, çalıştırılacak FastAPI app'ini otomatik olarak tespit etmeye çalışır; `main.py` dosyasında `app` adlı bir nesne olduğunu varsayar (veya birkaç başka varyant).
Ancak, kullanılacak app'i açıkça yapılandırabilirsiniz.
## Uygulama `entrypoint`'ini `pyproject.toml` İçinde Yapılandırma { #configure-the-app-entrypoint-in-pyproject-toml }
Uygulamanızın nerede olduğunu aşağıdaki gibi bir `pyproject.toml` dosyasında yapılandırabilirsiniz:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Bu `entrypoint`, `fastapi` komutuna app'i şu şekilde import etmesi gerektiğini söyler:
```python
from main import app
```
Kodunuz şu şekilde yapılandırılmışsa:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
O zaman `entrypoint`'i şu şekilde ayarlarsınız:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
Bu da şu koda eşdeğerdir:
```python
from backend.main import app
```
### path ile `fastapi dev` { #fastapi-dev-with-path }
Ayrıca `fastapi dev` komutuna dosya path'ini de verebilirsiniz; hangi FastAPI app nesnesinin kullanılacağını tahmin eder:
```console
$ fastapi dev main.py
```
Ancak `fastapi` komutunu her çağırdığınızda doğru path'i geçmeyi hatırlamanız gerekir.
Ayrıca, [VS Code Extension](editor-support.md) veya [FastAPI Cloud](https://fastapicloud.com) gibi diğer araçlar da bunu bulamayabilir; bu yüzden `pyproject.toml` içindeki `entrypoint`'i kullanmanız önerilir.
## `fastapi dev` { #fastapi-dev }
`fastapi dev` çalıştırmak, geliştirme modunu başlatır.
Varsayılan olarak **auto-reload** etkindir; kodunuzda değişiklik yaptığınızda server'ı otomatik olarak yeniden yükler. Bu, kaynak tüketimi yüksek bir özelliktir ve kapalı olduğuna kıyasla daha az stabil olabilir. Sadece geliştirme sırasında kullanmalısınız. Ayrıca yalnızca `127.0.0.1` IP adresini dinler; bu, makinenizin sadece kendisiyle iletişim kurması için kullanılan IP'dir (`localhost`).
## `fastapi run` { #fastapi-run }
`fastapi run` çalıştırmak, varsayılan olarak FastAPI'yi production modunda başlatır.
Varsayılan olarak **auto-reload** kapalıdır. Ayrıca `0.0.0.0` IP adresini dinler; bu, kullanılabilir tüm IP adresleri anlamına gelir. Böylece makineyle iletişim kurabilen herkes tarafından genel erişime açık olur. Bu, normalde production'da çalıştırma şeklidir; örneğin bir container içinde.
Çoğu durumda (ve genellikle yapmanız gereken şekilde) üst tarafta sizin yerinize HTTPS'i yöneten bir "termination proxy" bulunur. Bu, uygulamanızı nasıl deploy ettiğinize bağlıdır; sağlayıcınız bunu sizin için yapabilir ya da sizin ayrıca kurmanız gerekebilir.
/// tip | İpucu
Bununla ilgili daha fazla bilgiyi [deployment dokümantasyonunda](deployment/index.md) bulabilirsiniz.
///
================================================
FILE: docs/tr/docs/features.md
================================================
# Özellikler { #features }
## FastAPI Özellikleri { #fastapi-features }
**FastAPI** size şunları sağlar:
### Açık Standartlara Dayalı { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) ile API oluşturma; buna path operasyonları, parametreler, request body'leri, güvenlik vb. deklarasyonları dahildir.
* [**JSON Schema**](https://json-schema.org/) ile otomatik veri modeli dokümantasyonu (OpenAPI zaten JSON Schema'ya dayanır).
* Bu standartlar etrafında, titiz bir çalışmanın ardından tasarlandı; sonradan eklenmiş bir katman değil.
* Bu sayede birçok dilde otomatik **client code generation** da kullanılabilir.
### Otomatik Dokümantasyon { #automatic-docs }
Etkileşimli API dokümantasyonu ve keşif için web arayüzleri. Framework OpenAPI’ye dayandığından, birden fazla seçenek vardır; varsayılan olarak 2’si dahildir.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui) ile etkileşimli keşif; API’nizi tarayıcıdan doğrudan çağırıp test edin.

* [**ReDoc**](https://github.com/Rebilly/ReDoc) ile alternatif API dokümantasyonu.

### Sadece Modern Python { #just-modern-python }
Her şey standart **Python type** deklarasyonlarına dayanır (Pydantic sayesinde). Öğrenilecek yeni bir söz dizimi yok. Sadece standart, modern Python.
Python type’larını nasıl kullanacağınıza dair 2 dakikalık bir hatırlatmaya ihtiyacınız varsa (FastAPI kullanmasanız bile) kısa eğitime göz atın: [Python Types](python-types.md).
Türleriyle standart Python yazarsınız:
```Python
from datetime import date
from pydantic import BaseModel
# Bir değişkeni str olarak belirt
# ve fonksiyon içinde editör desteği al
def main(user_id: str):
return user_id
# Bir Pydantic modeli
class User(BaseModel):
id: int
name: str
joined: date
```
Sonra şöyle kullanabilirsiniz:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info
`**second_user_data` şu anlama gelir:
`second_user_data` dict’indeki anahtar ve değerleri doğrudan anahtar-değer argümanları olarak geç; şu ifadeye eşdeğerdir: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Editör Desteği { #editor-support }
Tüm framework, kullanımı kolay ve sezgisel olacak şekilde tasarlandı; en iyi geliştirme deneyimini sağlamak için geliştirmeye başlamadan önce bile alınan kararlar birden çok editörde test edildi.
Python geliştirici anketlerinde açıkça görülüyor ki [en çok kullanılan özelliklerden biri "otomatik tamamlama"](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Tüm **FastAPI** bunun tatmin edilmesi üzerine kuruldu. Otomatik tamamlama her yerde çalışır.
Dokümana geri dönmeniz nadiren gerekecek.
Editörünüz şöyle yardımcı olabilir:
* [Visual Studio Code](https://code.visualstudio.com/) ile:

* [PyCharm](https://www.jetbrains.com/pycharm/) ile:

Daha önce imkânsız olduğunu düşünebileceğiniz yerlerde bile tamamlama alırsınız. Örneğin, bir request’ten gelen (iç içe de olabilir) JSON body içindeki `price` anahtarı için.
Artık anahtar adlarını yanlış yazmak, dokümana gidip gelmek ya da sonunda `username` mi `user_name` mi kullandığınızı bulmak için sayfayı yukarı aşağı kaydırmak yok.
### Kısa { #short }
Her şey için mantıklı **varsayılanlar** ve her yerde isteğe bağlı yapılandırmalar vardır. Tüm parametreler, ihtiyacınızı karşılayacak şekilde ince ayar yapılarak tanımlamak istediğiniz API’yi oluşturabilir.
Ancak varsayılan hâliyle hepsi **“hemen çalışır”**.
### Doğrulama { #validation }
* Çoğu (veya hepsi?) Python **veri tipi** için doğrulama, şunlar dâhil:
* JSON nesneleri (`dict`).
* Eleman tipleri tanımlanan JSON dizileri (`list`).
* Minimum ve maksimum uzunlukları tanımlanan String (`str`) alanları.
* Min ve max değerleri olan sayılar (`int`, `float`) vb.
* Daha “egzotik” tipler için doğrulama:
* URL.
* Email.
* UUID.
* ...ve diğerleri.
Tüm doğrulama köklü ve sağlam **Pydantic** tarafından yapılır.
### Güvenlik ve Kimlik Doğrulama { #security-and-authentication }
Güvenlik ve kimlik doğrulama entegredir. Veritabanları veya veri modelleriyle ilgili hiçbir taviz yoktur.
OpenAPI’da tanımlanan tüm güvenlik şemaları, şunlar dâhil:
* HTTP Basic.
* **OAuth2** (ayrıca **JWT token**’larla). Şu eğitime göz atın: [OAuth2 with JWT](tutorial/security/oauth2-jwt.md).
* API anahtarları:
* Header’larda.
* Query parametrelerinde.
* Cookie’lerde vb.
Buna ek olarak Starlette’in tüm güvenlik özellikleri (**session cookies** dâhil).
Tümü, sistemleriniz, veri depolarınız, ilişkisel ve NoSQL veritabanlarınız vb. ile kolayca entegre edilebilen, yeniden kullanılabilir araçlar ve bileşenler olarak inşa edilmiştir.
### Dependency Injection { #dependency-injection }
FastAPI, son derece kolay kullanımlı ama son derece güçlü bir Dependency Injection sistemine sahiptir.
* Bağımlılıkların da kendi bağımlılıkları olabilir; böylece bir hiyerarşi veya **bağımlılıklar "grafı"** oluşur.
* Tüm süreç framework tarafından **otomatik olarak yönetilir**.
* Tüm bağımlılıklar, request’lerden veri talep edebilir ve *path operation* kısıtlarını ve otomatik dokümantasyonu **zenginleştirebilir**.
* Bağımlılıklarda tanımlanan *path operation* parametreleri için bile **otomatik doğrulama**.
* Karmaşık kullanıcı kimlik doğrulama sistemleri, **veritabanı bağlantıları** vb. için destek.
* Veritabanları, frontend’ler vb. ile **taviz yok**; ancak hepsiyle kolay entegrasyon.
### Sınırsız "Plug-in" { #unlimited-plug-ins }
Başka bir deyişle, onlara gerek yok; ihtiyaç duyduğunuz kodu import edin ve kullanın.
Her entegrasyon (bağımlılıklar ile) o kadar basit olacak şekilde tasarlanmıştır ki, uygulamanız için, *path operations* ile kullandığınız aynı yapı ve söz dizimiyle sadece 2 satırda bir “plug-in” yazabilirsiniz.
### Test Edildi { #tested }
* %100 test kapsayıcılığı.
* %100 type annotated kod tabanı.
* Üretimde kullanılan uygulamalarda kullanılıyor.
## Starlette Özellikleri { #starlette-features }
**FastAPI**, [**Starlette**](https://www.starlette.dev/) ile tamamen uyumludur (ve onun üzerine kuruludur). Dolayısıyla elinizdeki ek Starlette kodları da çalışır.
`FastAPI` aslında `Starlette`’in bir alt sınıfıdır. Starlette’i zaten biliyor veya kullanıyorsanız, işlevlerin çoğu aynı şekilde çalışır.
**FastAPI** ile **Starlette**’in tüm özelliklerini elde edersiniz (FastAPI, steroid basılmış Starlette gibidir):
* Cidden etkileyici performans. [Mevcut en hızlı Python frameworklerinden biridir; **NodeJS** ve **Go** ile aynı seviyededir](https://github.com/encode/starlette#performance).
* **WebSocket** desteği.
* Süreç içi arka plan görevleri.
* Başlatma ve kapatma olayları.
* HTTPX üzerine kurulu test istemcisi.
* **CORS**, GZip, Static Files, Streaming response’lar.
* **Session** ve **Cookie** desteği.
* %100 test kapsayıcılığı.
* %100 type annotated kod tabanı.
## Pydantic Özellikleri { #pydantic-features }
**FastAPI**, [**Pydantic**](https://docs.pydantic.dev/) ile tamamen uyumludur (ve onun üzerine kuruludur). Dolayısıyla elinizdeki ek Pydantic kodları da çalışır.
Pydantic’e dayanan harici kütüphaneler de dâhildir; veritabanları için ORM’ler, ODM’ler gibi.
Bu aynı zamanda, birçok durumda request’ten aldığınız nesneyi **doğrudan veritabanına** iletebileceğiniz anlamına gelir; zira her şey otomatik olarak doğrulanır.
Tersi yönde de geçerlidir; birçok durumda veritabanından aldığınız nesneyi **doğrudan client**’a gönderebilirsiniz.
**FastAPI** ile **Pydantic**’in tüm özelliklerini elde edersiniz (FastAPI, tüm veri işlemede Pydantic’e dayanır):
* **Kafa karıştırmaz**:
* Öğrenmeniz gereken yeni bir şema tanımlama mikro-dili yok.
* Python type’larını biliyorsanız Pydantic’i nasıl kullanacağınızı da biliyorsunuz.
* **IDE/linter/beyin**’inizle iyi anlaşır:
* Pydantic veri yapıları, sizin tanımladığınız sınıfların örnekleridir; bu nedenle doğrulanmış verilerinizle otomatik tamamlama, linting ve mypy sorunsuz çalışır, sezgileriniz de yol gösterir.
* **Karmaşık yapıları** doğrulayın:
* Hiyerarşik Pydantic modelleri, Python `typing`’in `List` ve `Dict`’i vb. kullanımı.
* Doğrulayıcılar (validators), karmaşık veri şemalarının net ve kolay şekilde tanımlanmasını, kontrol edilmesini ve JSON Schema olarak dokümante edilmesini sağlar.
* Derinlemesine iç içe **JSON** nesnelerine sahip olabilir, hepsinin doğrulanmasını ve anotasyonlanmasını sağlayabilirsiniz.
* **Genişletilebilir**:
* Pydantic, özel veri tiplerinin tanımlanmasına izin verir; ayrıca validator decorator’üyle bir modeldeki metodlarla doğrulamayı genişletebilirsiniz.
* %100 test kapsayıcılığı.
================================================
FILE: docs/tr/docs/help-fastapi.md
================================================
# FastAPI'ye Yardım Et - Yardım Al { #help-fastapi-get-help }
**FastAPI**'yi seviyor musunuz?
FastAPI'ye, diğer kullanıcılara ve yazara yardım etmek ister misiniz?
Yoksa **FastAPI** ile ilgili yardım mı almak istiyorsunuz?
Yardım etmenin çok basit yolları var (bazıları sadece bir-iki tıklama gerektirir).
Yardım almanın da birkaç yolu var.
## Bültene abone olun { #subscribe-to-the-newsletter }
Şunlardan haberdar olmak için (seyrek yayımlanan) [**FastAPI and friends** bültenine](newsletter.md) abone olabilirsiniz:
* FastAPI ve friends ile ilgili haberler 🚀
* Rehberler 📝
* Özellikler ✨
* Geriye dönük uyumsuz değişiklikler 🚨
* İpuçları ve püf noktaları ✅
## X (Twitter) üzerinden FastAPI'yi takip edin { #follow-fastapi-on-x-twitter }
[**X (Twitter)** üzerinde @fastapi hesabını takip edin](https://x.com/fastapi) ve **FastAPI** ile ilgili en güncel haberleri alın. 🐦
## GitHub'da **FastAPI**'ye yıldız verin { #star-fastapi-in-github }
GitHub'da FastAPI'ye "star" verebilirsiniz (sağ üstteki yıldız butonuna tıklayarak): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Yıldız verince, diğer kullanıcılar projeyi daha kolay bulabilir ve başkaları için de faydalı olduğunu görebilir.
## GitHub repository'sini release'ler için izleyin { #watch-the-github-repository-for-releases }
GitHub'da FastAPI'yi "watch" edebilirsiniz (sağ üstteki "watch" butonuna tıklayarak): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Orada "Releases only" seçebilirsiniz.
Böylece **FastAPI**'nin bug fix'ler ve yeni özelliklerle gelen her yeni release'inde (yeni versiyonunda) email ile bildirim alırsınız.
## Yazarla bağlantı kurun { #connect-with-the-author }
Yazar olan [benimle (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com) bağlantı kurabilirsiniz.
Şunları yapabilirsiniz:
* [Beni **GitHub**'da takip edin](https://github.com/tiangolo).
* Size yardımcı olabilecek oluşturduğum diğer Open Source projelere göz atın.
* Yeni bir Open Source proje oluşturduğumda haberdar olmak için beni takip edin.
* [Beni **X (Twitter)** üzerinde](https://x.com/tiangolo) veya [Mastodon](https://fosstodon.org/@tiangolo)'da takip edin.
* FastAPI'yi nasıl kullandığınızı anlatın (bunu duymayı seviyorum).
* Duyuru yaptığımda veya yeni araçlar yayınladığımda haberdar olun.
* Ayrıca (ayrı bir hesap olan) [X (Twitter) üzerinde @fastapi hesabını da takip edebilirsiniz](https://x.com/fastapi).
* [Beni **LinkedIn**'de takip edin](https://www.linkedin.com/in/tiangolo/).
* Duyuru yaptığımda veya yeni araçlar yayınladığımda haberdar olun (gerçi X (Twitter)'ı daha sık kullanıyorum 🤷♂).
* [**Dev.to**](https://dev.to/tiangolo) veya [**Medium**](https://medium.com/@tiangolo) üzerinde yazdıklarımı okuyun (ya da beni takip edin).
* Diğer fikirleri, yazıları ve oluşturduğum araçlarla ilgili içerikleri okuyun.
* Yeni bir şey yayınladığımda görmek için beni takip edin.
## **FastAPI** hakkında tweet atın { #tweet-about-fastapi }
[**FastAPI** hakkında tweet atın](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) ve neden sevdiğinizi bana ve diğerlerine söyleyin. 🎉
**FastAPI**'nin nasıl kullanıldığını, nelerini sevdiğinizi, hangi projede/şirkette kullandığınızı vb. duymayı seviyorum.
## FastAPI için oy verin { #vote-for-fastapi }
* [Slant'ta **FastAPI** için oy verin](https://www.slant.co/options/34241/~fastapi-review).
* [AlternativeTo'da **FastAPI** için oy verin](https://alternativeto.net/software/fastapi/about/).
* [StackShare'de **FastAPI** kullandığınızı belirtin](https://stackshare.io/pypi-fastapi).
## GitHub'da sorularla başkalarına yardım edin { #help-others-with-questions-in-github }
Şuralarda insanların sorularına yardımcı olmayı deneyebilirsiniz:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
Birçok durumda bu soruların cevabını zaten biliyor olabilirsiniz. 🤓
Eğer insanların sorularına çok yardım ederseniz, resmi bir [FastAPI Expert](fastapi-people.md#fastapi-experts) olabilirsiniz. 🎉
Şunu unutmayın: en önemli nokta, nazik olmaya çalışmak. İnsanlar çoğu zaman biriken stresle geliyor ve birçok durumda soruyu en iyi şekilde sormuyor; yine de elinizden geldiğince nazik olmaya çalışın. 🤗
Amaç, **FastAPI** topluluğunun nazik ve kapsayıcı olması. Aynı zamanda başkalarına zorbalık ya da saygısız davranışları da kabul etmeyin. Birbirimizi kollamalıyız.
---
Sorularda (discussions veya issues içinde) başkalarına yardım etmek için şunları yapabilirsiniz:
### Soruyu anlayın { #understand-the-question }
* Soru soran kişinin **amacının** ve kullanım senaryosunun ne olduğunu anlayabiliyor musunuz, kontrol edin.
* Sonra sorunun (büyük çoğunluğu soru olur) **net** olup olmadığına bakın.
* Birçok durumda kullanıcı kafasında hayali bir çözüm kurup onu sorar; ancak **daha iyi** bir çözüm olabilir. Problemi ve kullanım senaryosunu daha iyi anladıysanız daha iyi bir **alternatif çözüm** önerebilirsiniz.
* Soruyu anlayamıyorsanız daha fazla **detay** isteyin.
### Problemi yeniden üretin { #reproduce-the-problem }
Çoğu durumda ve çoğu soruda, kişinin **orijinal kodu** ile ilgili bir şey vardır.
Birçok kişi sadece kodun bir parçasını kopyalar, ama bu **problemi yeniden üretmek** için yeterli olmaz.
* Çalıştırıp aynı hatayı/davranışı görebileceğiniz veya kullanım senaryosunu daha iyi anlayabileceğiniz, yerelde **kopyala-yapıştır** yaparak çalıştırılabilen bir [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) paylaşmalarını isteyebilirsiniz.
* Çok cömert hissediyorsanız, problemi anlatan açıklamadan yola çıkarak kendiniz de böyle bir **örnek oluşturmayı** deneyebilirsiniz. Ancak bunun çok zaman alabileceğini unutmayın; çoğu zaman önce problemi netleştirmelerini istemek daha iyidir.
### Çözüm önerin { #suggest-solutions }
* Soruyu anlayabildikten sonra olası bir **cevap** verebilirsiniz.
* Çoğu durumda, yapmak istediklerinden ziyade alttaki **asıl problemi veya kullanım senaryosunu** anlamak daha iyidir; çünkü denedikleri yöntemden daha iyi bir çözüm yolu olabilir.
### Kapatılmasını isteyin { #ask-to-close }
Eğer yanıt verirlerse, büyük ihtimalle problemi çözmüşsünüzdür, tebrikler, **kahramansınız**! 🦸
* Eğer çözüm işe yaradıysa şunları yapmalarını isteyebilirsiniz:
* GitHub Discussions'ta: ilgili yorumu **answer** olarak işaretlemeleri.
* GitHub Issues'ta: issue'yu **close** etmeleri.
## GitHub repository'sini izleyin { #watch-the-github-repository }
GitHub'da FastAPI'yi "watch" edebilirsiniz (sağ üstteki "watch" butonuna tıklayarak): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
"Releases only" yerine "Watching" seçerseniz biri yeni bir issue veya soru oluşturduğunda bildirim alırsınız. Ayrıca sadece yeni issue'lar, ya da discussions, ya da PR'lar vb. için bildirim almak istediğinizi belirtebilirsiniz.
Sonra da bu soruları çözmelerine yardımcı olmayı deneyebilirsiniz.
## Soru Sorun { #ask-questions }
GitHub repository'sinde örneğin şunlar için [yeni bir soru oluşturabilirsiniz](https://github.com/fastapi/fastapi/discussions/new?category=questions):
* Bir **soru** sorun veya bir **problem** hakkında danışın.
* Yeni bir **feature** önerin.
**Not**: Bunu yaparsanız, ben de sizden başkalarına yardım etmenizi isteyeceğim. 😉
## Pull Request'leri İnceleyin { #review-pull-requests }
Başkalarının gönderdiği pull request'leri incelememde bana yardımcı olabilirsiniz.
Yine, lütfen elinizden geldiğince nazik olmaya çalışın. 🤗
---
Bir pull request'i incelerken akılda tutmanız gerekenler:
### Problemi anlayın { #understand-the-problem }
* Önce, pull request'in çözmeye çalıştığı **problemi anladığınızdan** emin olun. GitHub Discussion veya issue içinde daha uzun bir tartışması olabilir.
* Pull request'in aslında hiç gerekmiyor olma ihtimali de yüksektir; çünkü problem **farklı bir şekilde** çözülebilir. Bu durumda bunu önerebilir veya bununla ilgili soru sorabilirsiniz.
### Style konusunda çok dert etmeyin { #dont-worry-about-style }
* Commit message tarzı gibi şeyleri çok dert etmeyin; ben commit'leri manuel olarak düzenleyerek squash and merge yapacağım.
* Style kuralları için de endişelenmeyin; bunları kontrol eden otomatik araçlar zaten var.
Ek bir style veya tutarlılık ihtiyacı olursa, bunu doğrudan isterim ya da gerekli değişikliklerle üstüne commit eklerim.
### Kodu kontrol edin { #check-the-code }
* Kodu okuyup kontrol edin; mantıklı mı bakın, **yerelde çalıştırın** ve gerçekten problemi çözüyor mu görün.
* Ardından bunu yaptığınızı belirten bir **yorum** yazın; böylece gerçekten kontrol ettiğinizi anlarım.
/// info | Bilgi
Ne yazık ki sadece birkaç onayı olan PR'lara körü körüne güvenemem.
Defalarca, 3, 5 veya daha fazla onayı olan PR'lar oldu; muhtemelen açıklaması çekici olduğu için onay aldılar. Ama PR'lara baktığımda aslında bozuk olduklarını, bug içerdiğini veya iddia ettikleri problemi çözmediklerini gördüm. 😅
Bu yüzden kodu gerçekten okuyup çalıştırmanız ve bunu yorumlarda bana bildirmeniz çok önemli. 🤓
///
* PR bir şekilde basitleştirilebiliyorsa bunu isteyebilirsiniz. Ancak çok didik didik etmeye gerek yok; konuya göre birçok öznel bakış açısı olabilir (benim de olacaktır 🙈). Bu yüzden temel noktalara odaklanmak daha iyi.
### Testler { #tests }
* PR'da **testler** olduğunu kontrol etmemde bana yardımcı olun.
* PR'dan önce testlerin **fail** ettiğini kontrol edin. 🚨
* PR'dan sonra testlerin **pass** ettiğini kontrol edin. ✅
* Birçok PR test içermez; test eklemelerini **hatırlatabilirsiniz** veya hatta kendiniz bazı testler **önerebilirsiniz**. Bu, en çok zaman alan işlerden biridir ve burada çok yardımcı olabilirsiniz.
* Ayrıca neleri denediğinizi yorumlara yazın; böylece kontrol ettiğinizi anlarım. 🤓
## Pull Request Oluşturun { #create-a-pull-request }
Örneğin şunlar için Pull Request'lerle kaynak koda [katkıda bulunabilirsiniz](contributing.md):
* Dokümantasyonda bulduğunuz bir yazım hatasını düzeltmek.
* FastAPI hakkında oluşturduğunuz veya bulduğunuz bir makaleyi, videoyu ya da podcast'i [bu dosyayı düzenleyerek](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml) paylaşmak.
* Link'inizi ilgili bölümün başına eklediğinizden emin olun.
* Dokümantasyonu kendi dilinize [çevirmeye yardımcı olmak](contributing.md#translations).
* Başkalarının yaptığı çevirileri gözden geçirmeye de yardımcı olabilirsiniz.
* Yeni dokümantasyon bölümleri önermek.
* Mevcut bir issue/bug'ı düzeltmek.
* Test eklediğinizden emin olun.
* Yeni bir feature eklemek.
* Test eklediğinizden emin olun.
* İlgiliyse dokümantasyon da eklediğinizden emin olun.
## FastAPI'nin Bakımına Yardım Edin { #help-maintain-fastapi }
**FastAPI**'nin bakımını yapmama yardımcı olun! 🤓
Yapılacak çok iş var ve bunların çoğunu **SİZ** yapabilirsiniz.
Şu anda yapabileceğiniz ana işler:
* [GitHub'da sorularla başkalarına yardım edin](#help-others-with-questions-in-github) (yukarıdaki bölüme bakın).
* [Pull Request'leri inceleyin](#review-pull-requests) (yukarıdaki bölüme bakın).
Bu iki iş, **en çok zamanı alan** işlerdir. FastAPI bakımının ana yükü buradadır.
Burada yardımcı olursanız, **FastAPI'nin bakımını yapmama yardım etmiş** ve daha **hızlı ve daha iyi ilerlemesini** sağlamış olursunuz. 🚀
## Sohbete katılın { #join-the-chat }
FastAPI topluluğundan diğer kişilerle takılmak için 👥 [Discord chat server](https://discord.gg/VQjSZaeJmf) 👥 sohbetine katılın.
/// tip | İpucu
Sorular için [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions)'a sorun; [FastAPI Experts](fastapi-people.md#fastapi-experts) tarafından yardım alma ihtimaliniz çok daha yüksektir.
Chat'i sadece genel sohbetler için kullanın.
///
### Sorular için chat'i kullanmayın { #dont-use-the-chat-for-questions }
Chat sistemleri daha "serbest sohbet"e izin verdiği için, çok genel ve yanıtlaması daha zor sorular sormak kolaylaşır; bu nedenle cevap alamayabilirsiniz.
GitHub'da ise şablon (template) doğru soruyu yazmanız için sizi yönlendirir; böylece daha kolay iyi bir cevap alabilir, hatta bazen sormadan önce problemi kendiniz çözebilirsiniz. Ayrıca GitHub'da (zaman alsa bile) her şeye mutlaka cevap verdiğimden emin olabilirim. Chat sistemlerinde bunu kişisel olarak yapamam. 😅
Chat sistemlerindeki konuşmalar GitHub kadar kolay aranabilir değildir; bu yüzden soru ve cevaplar sohbet içinde kaybolabilir. Ayrıca [FastAPI Expert](fastapi-people.md#fastapi-experts) olmak için sadece GitHub'daki katkılar sayılır; dolayısıyla büyük olasılıkla GitHub'da daha fazla ilgi görürsünüz.
Öte yandan chat sistemlerinde binlerce kullanıcı vardır; bu yüzden neredeyse her zaman konuşacak birini bulma ihtimaliniz yüksektir. 😄
## Yazara sponsor olun { #sponsor-the-author }
Eğer **ürününüz/şirketiniz** **FastAPI**'ye bağlıysa veya onunla ilişkiliyse ve FastAPI kullanıcılarına ulaşmak istiyorsanız, [GitHub sponsors](https://github.com/sponsors/tiangolo) üzerinden yazara (bana) sponsor olabilirsiniz. Tier'a göre dokümantasyonda bir rozet gibi ek faydalar elde edebilirsiniz. 🎁
---
Teşekkürler! 🚀
================================================
FILE: docs/tr/docs/history-design-future.md
================================================
# Geçmişi, Tasarımı ve Geleceği { #history-design-and-future }
Bir süre önce, [bir **FastAPI** kullanıcısı sordu](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> Bu projenin geçmişi nedir? Birkaç hafta içinde hiçbir yerden harika bir şeye dönüşmüş gibi görünüyor [...]
İşte o geçmişin bir kısmı.
## Alternatifler { #alternatives }
Bir süredir karmaşık gereksinimlere sahip API'lar oluşturuyor (Makine Öğrenimi, dağıtık sistemler, asenkron işler, NoSQL veritabanları vb.) ve farklı geliştirici ekiplerini yönetiyorum.
Bu süreçte birçok alternatifi araştırmak, test etmek ve kullanmak zorunda kaldım.
**FastAPI**'ın geçmişi, büyük ölçüde önceden geliştirilen araçların geçmişini kapsıyor.
[Alternatifler](alternatives.md) bölümünde belirtildiği gibi:
Başkalarının daha önceki çalışmaları olmasaydı, **FastAPI** var olmazdı.
Geçmişte oluşturulan pek çok araç **FastAPI**'a ilham kaynağı olmuştur.
Yıllardır yeni bir framework oluşturmaktan kaçınıyordum. Başlangıçta **FastAPI**'ın çözdüğü sorunları çözebilmek için pek çok farklı framework, eklenti ve araç kullanmayı denedim.
Ancak bir noktada, geçmişteki diğer araçlardan en iyi fikirleri alarak bütün bu çözümleri kapsayan, ayrıca bütün bunları Python'ın daha önce mevcut olmayan özelliklerini (Python 3.6+ ile gelen tip belirteçleri) kullanarak yapan bir şey üretmekten başka bir seçenek kalmamıştı.
## Araştırma { #investigation }
Önceki alternatifleri kullanarak hepsinden bir şeyler öğrenip, fikirler alıp, bunları kendim ve çalıştığım geliştirici ekipler için en iyi şekilde birleştirebilme şansım oldu.
Mesela, ideal olarak standart Python tip belirteçlerine dayanması gerektiği açıktı.
Ayrıca, en iyi yaklaşım zaten mevcut olan standartları kullanmaktı.
Sonuç olarak, **FastAPI**'ı kodlamaya başlamadan önce, birkaç ay boyunca OpenAPI, JSON Schema, OAuth2 ve benzerlerinin tanımlamalarını inceledim. İlişkilerini, örtüştükleri noktaları ve farklılıklarını anlamaya çalıştım.
## Tasarım { #design }
Sonrasında, (**FastAPI** kullanan bir geliştirici olarak) sahip olmak istediğim "API"ı tasarlamak için biraz zaman harcadım.
Çeşitli fikirleri en popüler Python editörlerinde test ettim: PyCharm, VS Code, Jedi tabanlı editörler.
Bu test, en son [Python Geliştirici Anketi](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools)'ine göre, kullanıcıların yaklaşık %80'inin kullandığı editörleri kapsıyor.
Bu da demek oluyor ki **FastAPI**, Python geliştiricilerinin %80'inin kullandığı editörlerle test edildi. Ve diğer editörlerin çoğu benzer şekilde çalıştığından, avantajları neredeyse tüm editörlerde çalışacaktır.
Bu şekilde, kod tekrarını mümkün olduğunca azaltmak, her yerde otomatik tamamlama, tip ve hata kontrollerine sahip olmak için en iyi yolları bulabildim.
Hepsi, tüm geliştiriciler için en iyi geliştirme deneyimini sağlayacak şekilde.
## Gereksinimler { #requirements }
Çeşitli alternatifleri test ettikten sonra, avantajlarından dolayı [**Pydantic**](https://docs.pydantic.dev/)'i kullanmaya karar verdim.
Sonra, JSON Schema ile tamamen uyumlu olmasını sağlamak, kısıtlama bildirimlerini tanımlamanın farklı yollarını desteklemek ve birkaç editördeki testlere dayanarak editör desteğini (tip kontrolleri, otomatik tamamlama) geliştirmek için katkıda bulundum.
Geliştirme sırasında, diğer ana gereksinim olan [**Starlette**](https://www.starlette.dev/)'e de katkıda bulundum.
## Geliştirme { #development }
**FastAPI**'ı oluşturmaya başladığımda, parçaların çoğu zaten yerindeydi, tasarım tanımlanmıştı, gereksinimler ve araçlar hazırdı, standartlar ve tanımlamalar hakkındaki bilgi net ve tazeydi.
## Gelecek { #future }
Şimdiye kadar, **FastAPI**'ın fikirleriyle birçok kişiye faydalı olduğu apaçık ortada.
Birçok kullanım durumuna daha iyi uyduğu için, önceki alternatiflerin yerine seçiliyor.
Ben ve ekibim dahil, birçok geliştirici ve ekip projelerinde **FastAPI**'ya bağlı.
Tabi, geliştirilecek birçok özellik ve iyileştirme mevcut.
**FastAPI**'ın önünde harika bir gelecek var.
[Yardımlarınız](help-fastapi.md) çok değerlidir.
================================================
FILE: docs/tr/docs/how-to/authentication-error-status-code.md
================================================
# Eski 403 Kimlik Doğrulama Hata Durum Kodlarını Kullanma { #use-old-403-authentication-error-status-codes }
FastAPI `0.122.0` sürümünden önce, entegre security yardımcı araçları başarısız bir kimlik doğrulama (authentication) sonrasında client'a bir hata döndüğünde HTTP durum kodu olarak `403 Forbidden` kullanıyordu.
FastAPI `0.122.0` sürümünden itibaren ise daha uygun olan HTTP durum kodu `401 Unauthorized` kullanılmakta ve HTTP spesifikasyonlarına uygun olarak response içinde anlamlı bir `WWW-Authenticate` header'ı döndürülmektedir, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Ancak herhangi bir nedenle client'larınız eski davranışa bağlıysa, security class'larınızda `make_not_authenticated_error` metodunu override ederek eski davranışa geri dönebilirsiniz.
Örneğin, varsayılan `401 Unauthorized` hatası yerine `403 Forbidden` hatası döndüren bir `HTTPBearer` alt sınıfı oluşturabilirsiniz:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | İpucu
Fonksiyonun exception instance'ını döndürdüğüne dikkat edin; exception'ı raise etmiyor. Raise işlemi internal kodun geri kalan kısmında yapılıyor.
///
================================================
FILE: docs/tr/docs/how-to/conditional-openapi.md
================================================
# Koşullu OpenAPI { #conditional-openapi }
Gerekirse, ayarlar ve environment variable'ları kullanarak OpenAPI'yi ortama göre koşullu şekilde yapılandırabilir, hatta tamamen devre dışı bırakabilirsiniz.
## Güvenlik, API'ler ve dokümantasyon hakkında { #about-security-apis-and-docs }
Production ortamında dokümantasyon arayüzlerini gizlemek, API'nizi korumanın yolu olmamalıdır.
Bu, API'nize ekstra bir güvenlik katmanı eklemez; path operation'lar bulundukları yerde yine erişilebilir olacaktır.
Kodunuzda bir güvenlik açığı varsa, o açık yine var olmaya devam eder.
Dokümantasyonu gizlemek, API'nizle nasıl etkileşime geçileceğini anlamayı zorlaştırır ve production'da debug etmeyi de daha zor hale getirebilir. Bu yaklaşım, basitçe [Security through obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity) olarak değerlendirilebilir.
API'nizi güvence altına almak istiyorsanız, yapabileceğiniz daha iyi birçok şey var; örneğin:
* request body'leriniz ve response'larınız için iyi tanımlanmış Pydantic model'larına sahip olduğunuzdan emin olun.
* dependencies kullanarak gerekli izinleri ve rolleri yapılandırın.
* Asla düz metin (plaintext) şifre saklamayın, yalnızca password hash'leri saklayın.
* pwdlib ve JWT token'ları gibi, iyi bilinen kriptografik araçları uygulayın ve kullanın.
* Gerektiğinde OAuth2 scope'ları ile daha ayrıntılı izin kontrolleri ekleyin.
* ...vb.
Yine de, bazı ortamlarda (örn. production) veya environment variable'lardan gelen konfigürasyonlara bağlı olarak API docs'u gerçekten devre dışı bırakmanız gereken çok spesifik bir use case'iniz olabilir.
## Ayarlar ve env var'lar ile koşullu OpenAPI { #conditional-openapi-from-settings-and-env-vars }
Üretilen OpenAPI'yi ve docs UI'larını yapılandırmak için aynı Pydantic settings'i kolayca kullanabilirsiniz.
Örneğin:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Burada `openapi_url` ayarını, varsayılanı `"/openapi.json"` olacak şekilde tanımlıyoruz.
Ardından `FastAPI` app'ini oluştururken bunu kullanıyoruz.
Sonrasında, environment variable `OPENAPI_URL`'i boş string olarak ayarlayarak OpenAPI'yi (UI docs dahil) devre dışı bırakabilirsiniz; örneğin:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Böylece `/openapi.json`, `/docs` veya `/redoc` URL'lerine giderseniz, aşağıdaki gibi bir `404 Not Found` hatası alırsınız:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/tr/docs/how-to/configure-swagger-ui.md
================================================
# Swagger UI'yi Yapılandırın { #configure-swagger-ui }
Bazı ek [Swagger UI parametrelerini](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) yapılandırabilirsiniz.
Bunları yapılandırmak için, `FastAPI()` uygulama nesnesini oluştururken ya da `get_swagger_ui_html()` fonksiyonuna `swagger_ui_parameters` argümanını verin.
`swagger_ui_parameters`, Swagger UI'ye doğrudan iletilecek yapılandırmaları içeren bir `dict` alır.
FastAPI, Swagger UI'nin ihtiyaç duyduğu şekilde JavaScript ile uyumlu olsun diye bu yapılandırmaları **JSON**'a dönüştürür.
## Syntax Highlighting'i Devre Dışı Bırakın { #disable-syntax-highlighting }
Örneğin, Swagger UI'de syntax highlighting'i devre dışı bırakabilirsiniz.
Ayarları değiştirmeden bırakırsanız, syntax highlighting varsayılan olarak etkindir:
Ancak `syntaxHighlight` değerini `False` yaparak devre dışı bırakabilirsiniz:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...ve ardından Swagger UI artık syntax highlighting'i göstermeyecektir:
## Temayı Değiştirin { #change-the-theme }
Aynı şekilde, `"syntaxHighlight.theme"` anahtarıyla (ortasında bir nokta olduğuna dikkat edin) syntax highlighting temasını ayarlayabilirsiniz:
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Bu yapılandırma, syntax highlighting renk temasını değiştirir:
## Varsayılan Swagger UI Parametrelerini Değiştirin { #change-default-swagger-ui-parameters }
FastAPI, çoğu kullanım senaryosu için uygun bazı varsayılan yapılandırma parametreleriyle gelir.
Şu varsayılan yapılandırmaları içerir:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
`swagger_ui_parameters` argümanında farklı bir değer vererek bunların herhangi birini ezebilirsiniz (override).
Örneğin `deepLinking`'i devre dışı bırakmak için `swagger_ui_parameters`'a şu ayarları geçebilirsiniz:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Diğer Swagger UI Parametreleri { #other-swagger-ui-parameters }
Kullanabileceğiniz diğer tüm olası yapılandırmaları görmek için, resmi [Swagger UI parametreleri dokümantasyonunu](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) okuyun.
## Yalnızca JavaScript Ayarları { #javascript-only-settings }
Swagger UI ayrıca bazı yapılandırmaların **yalnızca JavaScript** nesneleri olmasına izin verir (örneğin JavaScript fonksiyonları).
FastAPI, bu yalnızca JavaScript olan `presets` ayarlarını da içerir:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Bunlar string değil, **JavaScript** nesneleridir; dolayısıyla bunları Python kodundan doğrudan geçemezsiniz.
Böyle yalnızca JavaScript yapılandırmalarına ihtiyacınız varsa, yukarıdaki yöntemlerden birini kullanabilirsiniz: Swagger UI'nin tüm *path operation*'larını override edin ve ihtiyaç duyduğunuz JavaScript'i elle yazın.
================================================
FILE: docs/tr/docs/how-to/custom-docs-ui-assets.md
================================================
# Özel Docs UI Statik Varlıkları (Self-Hosting) { #custom-docs-ui-static-assets-self-hosting }
API dokümanları **Swagger UI** ve **ReDoc** kullanır ve bunların her biri bazı JavaScript ve CSS dosyalarına ihtiyaç duyar.
Varsayılan olarak bu dosyalar bir CDN üzerinden servis edilir.
Ancak bunu özelleştirmek mümkündür; belirli bir CDN ayarlayabilir veya dosyaları kendiniz servis edebilirsiniz.
## JavaScript ve CSS için Özel CDN { #custom-cdn-for-javascript-and-css }
Diyelim ki farklı bir CDN kullanmak istiyorsunuz; örneğin `https://unpkg.com/` kullanmak istiyorsunuz.
Bu, örneğin bazı URL'leri kısıtlayan bir ülkede yaşıyorsanız faydalı olabilir.
### Otomatik dokümanları devre dışı bırakın { #disable-the-automatic-docs }
İlk adım, otomatik dokümanları devre dışı bırakmaktır; çünkü varsayılan olarak bunlar varsayılan CDN'i kullanır.
Bunları devre dışı bırakmak için `FastAPI` uygulamanızı oluştururken URL'lerini `None` olarak ayarlayın:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Özel dokümanları ekleyin { #include-the-custom-docs }
Şimdi özel dokümanlar için *path operation*'ları oluşturabilirsiniz.
Dokümanlar için HTML sayfalarını üretmek üzere FastAPI'nin dahili fonksiyonlarını yeniden kullanabilir ve gerekli argümanları iletebilirsiniz:
* `openapi_url`: Dokümanların HTML sayfasının API'niz için OpenAPI şemasını alacağı URL. Burada `app.openapi_url` niteliğini kullanabilirsiniz.
* `title`: API'nizin başlığı.
* `oauth2_redirect_url`: varsayılanı kullanmak için burada `app.swagger_ui_oauth2_redirect_url` kullanabilirsiniz.
* `swagger_js_url`: Swagger UI dokümanlarınızın HTML'inin **JavaScript** dosyasını alacağı URL. Bu, özel CDN URL'idir.
* `swagger_css_url`: Swagger UI dokümanlarınızın HTML'inin **CSS** dosyasını alacağı URL. Bu, özel CDN URL'idir.
ReDoc için de benzer şekilde...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | İpucu
`swagger_ui_redirect` için olan *path operation*, OAuth2 kullandığınızda yardımcı olması için vardır.
API'nizi bir OAuth2 sağlayıcısıyla entegre ederseniz kimlik doğrulaması yapabilir, aldığınız kimlik bilgileriyle API dokümanlarına geri dönebilir ve gerçek OAuth2 kimlik doğrulamasını kullanarak onunla etkileşime geçebilirsiniz.
Swagger UI bunu arka planda sizin için yönetir, ancak bu "redirect" yardımcısına ihtiyaç duyar.
///
### Test etmek için bir *path operation* oluşturun { #create-a-path-operation-to-test-it }
Şimdi her şeyin çalıştığını test edebilmek için bir *path operation* oluşturun:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Test edin { #test-it }
Artık [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresinden dokümanlarınıza gidebilmeli ve sayfayı yenilediğinizde bu varlıkların yeni CDN'den yüklendiğini görebilmelisiniz.
## Dokümanlar için JavaScript ve CSS'i Self-Hosting ile barındırma { #self-hosting-javascript-and-css-for-docs }
JavaScript ve CSS'i self-hosting ile barındırmak, örneğin uygulamanızın İnternet erişimi olmadan (offline), açık İnternet olmadan veya bir lokal ağ içinde bile çalışmaya devam etmesi gerekiyorsa faydalı olabilir.
Burada bu dosyaları aynı FastAPI uygulamasında nasıl kendiniz servis edeceğinizi ve dokümanların bunları kullanacak şekilde nasıl yapılandırılacağını göreceksiniz.
### Proje dosya yapısı { #project-file-structure }
Diyelim ki projenizin dosya yapısı şöyle:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Şimdi bu statik dosyaları saklamak için bir dizin oluşturun.
Yeni dosya yapınız şöyle olabilir:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Dosyaları indirin { #download-the-files }
Dokümanlar için gereken statik dosyaları indirin ve `static/` dizinine koyun.
Muhtemelen her bir linke sağ tıklayıp "Save link as..." benzeri bir seçenek seçebilirsiniz.
**Swagger UI** şu dosyaları kullanır:
* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
**ReDoc** ise şu dosyayı kullanır:
* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Bundan sonra dosya yapınız şöyle görünebilir:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Statik dosyaları servis edin { #serve-the-static-files }
* `StaticFiles` içe aktarın.
* Belirli bir path'te bir `StaticFiles()` instance'ını "mount" edin.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Statik dosyaları test edin { #test-the-static-files }
Uygulamanızı başlatın ve [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js) adresine gidin.
**ReDoc** için çok uzun bir JavaScript dosyası görmelisiniz.
Şuna benzer bir şekilde başlayabilir:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Bu, uygulamanızdan statik dosyaları servis edebildiğinizi ve dokümanlar için statik dosyaları doğru yere koyduğunuzu doğrular.
Şimdi uygulamayı, dokümanlar için bu statik dosyaları kullanacak şekilde yapılandırabiliriz.
### Statik dosyalar için otomatik dokümanları devre dışı bırakın { #disable-the-automatic-docs-for-static-files }
Özel CDN kullanırken olduğu gibi, ilk adım otomatik dokümanları devre dışı bırakmaktır; çünkü bunlar varsayılan olarak CDN kullanır.
Bunları devre dışı bırakmak için `FastAPI` uygulamanızı oluştururken URL'lerini `None` olarak ayarlayın:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Statik dosyalar için özel dokümanları ekleyin { #include-the-custom-docs-for-static-files }
Özel CDN'de olduğu gibi, artık özel dokümanlar için *path operation*'ları oluşturabilirsiniz.
Yine FastAPI'nin dahili fonksiyonlarını kullanarak dokümanlar için HTML sayfalarını oluşturabilir ve gerekli argümanları geçebilirsiniz:
* `openapi_url`: Dokümanların HTML sayfasının API'niz için OpenAPI şemasını alacağı URL. Burada `app.openapi_url` niteliğini kullanabilirsiniz.
* `title`: API'nizin başlığı.
* `oauth2_redirect_url`: varsayılanı kullanmak için burada `app.swagger_ui_oauth2_redirect_url` kullanabilirsiniz.
* `swagger_js_url`: Swagger UI dokümanlarınızın HTML'inin **JavaScript** dosyasını alacağı URL. **Artık bunu sizin kendi uygulamanız servis ediyor**.
* `swagger_css_url`: Swagger UI dokümanlarınızın HTML'inin **CSS** dosyasını alacağı URL. **Artık bunu sizin kendi uygulamanız servis ediyor**.
ReDoc için de benzer şekilde...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | İpucu
`swagger_ui_redirect` için olan *path operation*, OAuth2 kullandığınızda yardımcı olması için vardır.
API'nizi bir OAuth2 sağlayıcısıyla entegre ederseniz kimlik doğrulaması yapabilir, aldığınız kimlik bilgileriyle API dokümanlarına geri dönebilir ve gerçek OAuth2 kimlik doğrulamasını kullanarak onunla etkileşime geçebilirsiniz.
Swagger UI bunu arka planda sizin için yönetir, ancak bu "redirect" yardımcısına ihtiyaç duyar.
///
### Statik dosyaları test etmek için bir *path operation* oluşturun { #create-a-path-operation-to-test-static-files }
Şimdi her şeyin çalıştığını test edebilmek için bir *path operation* oluşturun:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Statik Dosyalar UI'ını Test Edin { #test-static-files-ui }
Artık WiFi bağlantınızı kesip [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresindeki dokümanlarınıza gidebilmeli ve sayfayı yenileyebilmelisiniz.
Ve İnternet olmasa bile API dokümanlarınızı görebilir ve onunla etkileşime geçebilirsiniz.
================================================
FILE: docs/tr/docs/how-to/custom-request-and-route.md
================================================
# Özel Request ve APIRoute sınıfı { #custom-request-and-apiroute-class }
Bazı durumlarda, `Request` ve `APIRoute` sınıflarının kullandığı mantığı override etmek isteyebilirsiniz.
Özellikle bu yaklaşım, bir middleware içindeki mantığa iyi bir alternatif olabilir.
Örneğin, request body uygulamanız tarafından işlenmeden önce okumak veya üzerinde değişiklik yapmak istiyorsanız.
/// danger | Uyarı
Bu "ileri seviye" bir özelliktir.
**FastAPI**'ye yeni başlıyorsanız bu bölümü atlamak isteyebilirsiniz.
///
## Kullanım senaryoları { #use-cases }
Bazı kullanım senaryoları:
* JSON olmayan request body'leri JSON'a dönüştürmek (örn. [`msgpack`](https://msgpack.org/index.html)).
* gzip ile sıkıştırılmış request body'leri açmak (decompress).
* Tüm request body'lerini otomatik olarak loglamak.
## Özel request body encoding'lerini ele alma { #handling-custom-request-body-encodings }
Gzip request'lerini açmak için özel bir `Request` alt sınıfını nasıl kullanabileceğimize bakalım.
Ayrıca, o özel request sınıfını kullanmak için bir `APIRoute` alt sınıfı da oluşturacağız.
### Özel bir `GzipRequest` sınıfı oluşturun { #create-a-custom-gziprequest-class }
/// tip | İpucu
Bu, nasıl çalıştığını göstermek için hazırlanmış basit bir örnektir; Gzip desteğine ihtiyacınız varsa sağlanan [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) bileşenini kullanabilirsiniz.
///
Önce, uygun bir header mevcut olduğunda body'yi açmak için `Request.body()` metodunu overwrite edecek bir `GzipRequest` sınıfı oluşturuyoruz.
Header'da `gzip` yoksa body'yi açmayı denemez.
Böylece aynı route sınıfı, gzip ile sıkıştırılmış veya sıkıştırılmamış request'leri handle edebilir.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Özel bir `GzipRoute` sınıfı oluşturun { #create-a-custom-gziproute-class }
Sonra, `GzipRequest`'i kullanacak `fastapi.routing.APIRoute` için özel bir alt sınıf oluşturuyoruz.
Bu kez `APIRoute.get_route_handler()` metodunu overwrite edeceğiz.
Bu metot bir fonksiyon döndürür. Bu fonksiyon da request'i alır ve response döndürür.
Burada bu fonksiyonu, orijinal request'ten bir `GzipRequest` oluşturmak için kullanıyoruz.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Teknik Detaylar
Bir `Request`'in, request ile ilgili metadata'yı içeren bir Python `dict` olan `request.scope` attribute'u vardır.
Bir `Request` ayrıca `request.receive` içerir; bu, request'in body'sini "almak" (receive etmek) için kullanılan bir fonksiyondur.
`scope` `dict`'i ve `receive` fonksiyonu, ASGI spesifikasyonunun parçalarıdır.
Ve bu iki şey, `scope` ve `receive`, yeni bir `Request` instance'ı oluşturmak için gerekenlerdir.
`Request` hakkında daha fazla bilgi için [Starlette'ın Request dokümantasyonu](https://www.starlette.dev/requests/) bölümüne bakın.
///
`GzipRequest.get_route_handler` tarafından döndürülen fonksiyonun farklı yaptığı tek şey, `Request`'i bir `GzipRequest`'e dönüştürmektir.
Bunu yaptığımızda `GzipRequest`, veriyi (gerekliyse) *path operations*'larımıza geçirmeden önce açma (decompress) işini üstlenir.
Bundan sonra tüm işleme mantığı aynıdır.
Ancak `GzipRequest.body` içindeki değişikliklerimiz sayesinde, request body gerektiğinde **FastAPI** tarafından yüklendiğinde otomatik olarak decompress edilir.
## Bir exception handler içinde request body'ye erişme { #accessing-the-request-body-in-an-exception-handler }
/// tip | İpucu
Aynı problemi çözmek için, muhtemelen `RequestValidationError` için özel bir handler içinde `body` kullanmak çok daha kolaydır ([Hataları Ele Alma](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Yine de bu örnek geçerlidir ve dahili bileşenlerle nasıl etkileşime geçileceğini gösterir.
///
Aynı yaklaşımı bir exception handler içinde request body'ye erişmek için de kullanabiliriz.
Tek yapmamız gereken, request'i bir `try`/`except` bloğu içinde handle etmek:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Bir exception oluşursa, `Request` instance'ı hâlâ scope içinde olacağı için, hatayı handle ederken request body'yi okuyup kullanabiliriz:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Bir router içinde özel `APIRoute` sınıfı { #custom-apiroute-class-in-a-router }
Bir `APIRouter` için `route_class` parametresini de ayarlayabilirsiniz:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
Bu örnekte, `router` altındaki *path operations*'lar özel `TimedRoute` sınıfını kullanır ve response'u üretmek için geçen süreyi içeren ekstra bir `X-Response-Time` header'ı response'ta bulunur:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/tr/docs/how-to/extending-openapi.md
================================================
# OpenAPI'yi Genişletme { #extending-openapi }
Oluşturulan OpenAPI şemasını değiştirmeniz gereken bazı durumlar olabilir.
Bu bölümde bunun nasıl yapılacağını göreceksiniz.
## Normal Süreç { #the-normal-process }
Normal (varsayılan) süreç şöyledir.
Bir `FastAPI` uygulamasının (instance) OpenAPI şemasını döndürmesi beklenen bir `.openapi()` metodu vardır.
Uygulama nesnesi oluşturulurken, `/openapi.json` (ya da `openapi_url` için ne ayarladıysanız o) için bir *path operation* kaydedilir.
Bu path operation, uygulamanın `.openapi()` metodunun sonucunu içeren bir JSON response döndürür.
Varsayılan olarak `.openapi()` metodunun yaptığı şey, `.openapi_schema` özelliğinde içerik olup olmadığını kontrol etmek ve varsa onu döndürmektir.
Eğer yoksa, `fastapi.openapi.utils.get_openapi` konumundaki yardımcı (utility) fonksiyonu kullanarak şemayı üretir.
Ve `get_openapi()` fonksiyonu şu parametreleri alır:
* `title`: Dokümanlarda gösterilen OpenAPI başlığı.
* `version`: API'nizin sürümü, örn. `2.5.0`.
* `openapi_version`: Kullanılan OpenAPI specification sürümü. Varsayılan olarak en günceli: `3.1.0`.
* `summary`: API'nin kısa özeti.
* `description`: API'nizin açıklaması; markdown içerebilir ve dokümanlarda gösterilir.
* `routes`: route'ların listesi; bunların her biri kayıtlı *path operations*'lardır. `app.routes` içinden alınırlar.
/// info | Bilgi
`summary` parametresi OpenAPI 3.1.0 ve üzeri sürümlerde vardır; FastAPI 0.99.0 ve üzeri tarafından desteklenmektedir.
///
## Varsayılanları Ezme { #overriding-the-defaults }
Yukarıdaki bilgileri kullanarak aynı yardımcı fonksiyonla OpenAPI şemasını üretebilir ve ihtiyacınız olan her parçayı override edebilirsiniz.
Örneğin, [özel bir logo eklemek için ReDoc'un OpenAPI extension'ını](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo) ekleyelim.
### Normal **FastAPI** { #normal-fastapi }
Önce, tüm **FastAPI** uygulamanızı her zamanki gibi yazın:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### OpenAPI Şemasını Üretme { #generate-the-openapi-schema }
Ardından, bir `custom_openapi()` fonksiyonunun içinde aynı yardımcı fonksiyonu kullanarak OpenAPI şemasını üretin:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### OpenAPI Şemasını Değiştirme { #modify-the-openapi-schema }
Artık OpenAPI şemasındaki `info` "object"'ine özel bir `x-logo` ekleyerek ReDoc extension'ını ekleyebilirsiniz:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### OpenAPI Şemasını Cache'leme { #cache-the-openapi-schema }
Ürettiğiniz şemayı saklamak için `.openapi_schema` özelliğini bir "cache" gibi kullanabilirsiniz.
Böylece bir kullanıcı API docs'larınızı her açtığında uygulamanız şemayı tekrar tekrar üretmek zorunda kalmaz.
Şema yalnızca bir kez üretilecektir; sonraki request'ler için de aynı cache'lenmiş şema kullanılacaktır.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Metodu Override Etme { #override-the-method }
Şimdi `.openapi()` metodunu yeni fonksiyonunuzla değiştirebilirsiniz.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Kontrol Edin { #check-it }
[http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) adresine gittiğinizde, özel logonuzu kullandığınızı göreceksiniz (bu örnekte **FastAPI**'nin logosu):
================================================
FILE: docs/tr/docs/how-to/general.md
================================================
# Genel - Nasıl Yapılır - Tarifler { #general-how-to-recipes }
Bu sayfada genel veya sık sorulan sorular için dokümantasyonun diğer bölümlerine çeşitli yönlendirmeler bulunmaktadır.
## Veri Filtreleme - Güvenlik { #filter-data-security }
Döndürmeniz gerekenden daha fazla veri döndürmediğinizden emin olmak için, [Tutorial - Response Model - Return Type](../tutorial/response-model.md) dokümantasyonunu okuyun.
## Response Performansını Optimize Etme - Response Model - Return Type { #optimize-response-performance-response-model-return-type }
JSON veri döndürürken performansı optimize etmek için bir return type veya response model kullanın; böylece Pydantic serileştirmeyi Python'dan geçmeden Rust tarafında gerçekleştirir. Ayrıntılar için [Tutorial - Response Model - Return Type](../tutorial/response-model.md) dokümantasyonunu okuyun.
## Dokümantasyon Etiketleri - OpenAPI { #documentation-tags-openapi }
*path operation*'larınıza etiketler eklemek ve dokümantasyon arayüzünde gruplamak için, [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags) dokümantasyonunu okuyun.
## Dokümantasyon Özeti ve Açıklaması - OpenAPI { #documentation-summary-and-description-openapi }
*path operation*'larınıza özet ve açıklama eklemek ve bunları dokümantasyon arayüzünde göstermek için, [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description) dokümantasyonunu okuyun.
## Dokümantasyon Response Açıklaması - OpenAPI { #documentation-response-description-openapi }
Dokümantasyon arayüzünde gösterilen response açıklamasını tanımlamak için, [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description) dokümantasyonunu okuyun.
## Dokümantasyonda Bir *Path Operation*'ı Kullanımdan Kaldırma - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Bir *path operation*'ı kullanımdan kaldırmak ve bunu dokümantasyon arayüzünde göstermek için, [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation) dokümantasyonunu okuyun.
## Herhangi Bir Veriyi JSON Uyumlu Hale Getirme { #convert-any-data-to-json-compatible }
Herhangi bir veriyi JSON uyumlu hale getirmek için, [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md) dokümantasyonunu okuyun.
## OpenAPI Meta Verileri - Dokümantasyon { #openapi-metadata-docs }
Lisans, sürüm, iletişim vb. dahil olmak üzere OpenAPI şemanıza meta veriler eklemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md) dokümantasyonunu okuyun.
## OpenAPI Özel URL { #openapi-custom-url }
OpenAPI URL'ini özelleştirmek (veya kaldırmak) için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url) dokümantasyonunu okuyun.
## OpenAPI Dokümantasyon URL'leri { #openapi-docs-urls }
Otomatik olarak oluşturulan dokümantasyon kullanıcı arayüzlerinde kullanılan URL'leri güncellemek için, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls) dokümantasyonunu okuyun.
================================================
FILE: docs/tr/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
**FastAPI**, **ASGI** standardını temel aldığı için ASGI ile uyumlu herhangi bir **GraphQL** kütüphanesini entegre etmek oldukça kolaydır.
Aynı uygulama içinde normal FastAPI *path operation*'larını GraphQL ile birlikte kullanabilirsiniz.
/// tip | İpucu
**GraphQL** bazı çok özel kullanım senaryolarını çözer.
Yaygın **web API**'lerle karşılaştırıldığında **avantajları** ve **dezavantajları** vardır.
Kendi senaryonuz için **faydaların**, **olumsuz yönleri** telafi edip etmediğini mutlaka değerlendirin. 🤓
///
## GraphQL Kütüphaneleri { #graphql-libraries }
Aşağıda **ASGI** desteği olan bazı **GraphQL** kütüphaneleri var. Bunları **FastAPI** ile kullanabilirsiniz:
* [Strawberry](https://strawberry.rocks/) 🍓
* [FastAPI dokümantasyonu](https://strawberry.rocks/docs/integrations/fastapi) ile
* [Ariadne](https://ariadnegraphql.org/)
* [FastAPI dokümantasyonu](https://ariadnegraphql.org/docs/fastapi-integration) ile
* [Tartiflette](https://tartiflette.io/)
* ASGI entegrasyonu sağlamak için [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) ile
* [Graphene](https://graphene-python.org/)
* [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3) ile
## Strawberry ile GraphQL { #graphql-with-strawberry }
**GraphQL** ile çalışmanız gerekiyorsa ya da bunu istiyorsanız, [**Strawberry**](https://strawberry.rocks/) önerilen kütüphanedir; çünkü tasarımı **FastAPI**'nin tasarımına en yakındır ve her şey **type annotation**'lar üzerine kuruludur.
Kullanım senaryonuza göre farklı bir kütüphaneyi tercih edebilirsiniz; ancak bana sorarsanız muhtemelen **Strawberry**'yi denemenizi önerirdim.
Strawberry'yi FastAPI ile nasıl entegre edebileceğinize dair küçük bir ön izleme:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Strawberry hakkında daha fazlasını [Strawberry dokümantasyonunda](https://strawberry.rocks/) öğrenebilirsiniz.
Ayrıca [FastAPI ile Strawberry](https://strawberry.rocks/docs/integrations/fastapi) dokümanlarına da göz atın.
## Starlette'teki Eski `GraphQLApp` { #older-graphqlapp-from-starlette }
Starlette'in önceki sürümlerinde [Graphene](https://graphene-python.org/) ile entegrasyon için bir `GraphQLApp` sınıfı vardı.
Bu sınıf Starlette'te kullanımdan kaldırıldı (deprecated). Ancak bunu kullanan bir kodunuz varsa, aynı kullanım senaryosunu kapsayan ve **neredeyse aynı bir interface** sağlayan [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)'e kolayca **migrate** edebilirsiniz.
/// tip | İpucu
GraphQL'e ihtiyacınız varsa, custom class ve type'lar yerine type annotation'lara dayandığı için yine de [Strawberry](https://strawberry.rocks/)'yi incelemenizi öneririm.
///
## Daha Fazlasını Öğrenin { #learn-more }
**GraphQL** hakkında daha fazlasını [resmi GraphQL dokümantasyonunda](https://graphql.org/) öğrenebilirsiniz.
Ayrıca yukarıda bahsedilen kütüphanelerin her biri hakkında, kendi bağlantılarından daha fazla bilgi okuyabilirsiniz.
================================================
FILE: docs/tr/docs/how-to/index.md
================================================
# Nasıl Yapılır - Tarifler { #how-to-recipes }
Burada **çeşitli konular** hakkında farklı tarifler veya "nasıl yapılır" kılavuzları göreceksiniz.
Bu fikirlerin büyük bir kısmı aşağı yukarı **bağımsız** olacaktır ve çoğu durumda bunları yalnızca doğrudan **projenize** uygulanıyorsa incelemeniz yeterli olacaktır.
Projeniz için ilginç ve yararlı görünen bir şey varsa devam edin ve inceleyin; aksi halde muhtemelen bunları atlayabilirsiniz.
/// tip | İpucu
**FastAPI**'yi yapılandırılmış bir şekilde (önerilir) **öğrenmek** istiyorsanız bunun yerine [Öğretici - Kullanıcı Rehberi](../tutorial/index.md)'ni bölüm bölüm okuyun.
///
================================================
FILE: docs/tr/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Pydantic v1'den Pydantic v2'ye Geçiş { #migrate-from-pydantic-v1-to-pydantic-v2 }
Eski bir FastAPI uygulamanız varsa, Pydantic'in 1. sürümünü kullanıyor olabilirsiniz.
FastAPI 0.100.0 sürümü, Pydantic v1 veya v2 ile çalışmayı destekliyordu. Hangisi kuruluysa onu kullanıyordu.
FastAPI 0.119.0 sürümü, v2'ye geçişi kolaylaştırmak için, Pydantic v2’nin içinden Pydantic v1’e (`pydantic.v1` olarak) kısmi destek ekledi.
FastAPI 0.126.0 sürümü Pydantic v1 desteğini kaldırdı, ancak bir süre daha `pydantic.v1` desteğini sürdürdü.
/// warning | Uyarı
Pydantic ekibi, Python'ın en yeni sürümleri için Pydantic v1 desteğini, **Python 3.14** ile başlayarak sonlandırdı.
Buna `pydantic.v1` de dahildir; Python 3.14 ve üzeri sürümlerde artık desteklenmemektedir.
Python'ın en yeni özelliklerini kullanmak istiyorsanız, Pydantic v2 kullandığınızdan emin olmanız gerekir.
///
Pydantic v1 kullanan eski bir FastAPI uygulamanız varsa, burada onu Pydantic v2'ye nasıl taşıyacağınızı ve kademeli geçişi kolaylaştıran **FastAPI 0.119.0 özelliklerini** göstereceğim.
## Resmi Kılavuz { #official-guide }
Pydantic'in v1'den v2'ye resmi bir [Migration Guide](https://docs.pydantic.dev/latest/migration/)'ı vardır.
Ayrıca nelerin değiştiğini, validasyonların artık nasıl daha doğru ve katı olduğunu, olası dikkat edilmesi gereken noktaları (caveat) vb. de içerir.
Nelerin değiştiğini daha iyi anlamak için okuyabilirsiniz.
## Testler { #tests }
Uygulamanız için [testleriniz](../tutorial/testing.md) olduğundan ve bunları continuous integration (CI) üzerinde çalıştırdığınızdan emin olun.
Bu şekilde yükseltmeyi yapabilir ve her şeyin hâlâ beklendiği gibi çalıştığını doğrulayabilirsiniz.
## `bump-pydantic` { #bump-pydantic }
Birçok durumda, özel özelleştirmeler olmadan standart Pydantic modelleri kullanıyorsanız, Pydantic v1'den Pydantic v2'ye geçiş sürecinin büyük kısmını otomatikleştirebilirsiniz.
Aynı Pydantic ekibinin geliştirdiği [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) aracını kullanabilirsiniz.
Bu araç, değişmesi gereken kodun büyük bir kısmını otomatik olarak dönüştürmenize yardımcı olur.
Bundan sonra testleri çalıştırıp her şeyin çalışıp çalışmadığını kontrol edebilirsiniz. Çalışıyorsa işiniz biter. 😎
## v2 İçinde Pydantic v1 { #pydantic-v1-in-v2 }
Pydantic v2, `pydantic.v1` adlı bir alt modül olarak Pydantic v1'in tamamını içerir. Ancak bu yapı, Python 3.13'ün üzerindeki sürümlerde artık desteklenmemektedir.
Bu da şu anlama gelir: Pydantic v2'nin en güncel sürümünü kurup, bu alt modülden eski Pydantic v1 bileşenlerini import ederek, sanki eski Pydantic v1 kuruluymuş gibi kullanabilirsiniz.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### v2 İçinde Pydantic v1 için FastAPI Desteği { #fastapi-support-for-pydantic-v1-in-v2 }
FastAPI 0.119.0'dan itibaren, v2'ye geçişi kolaylaştırmak için Pydantic v2’nin içinden Pydantic v1 kullanımına yönelik kısmi destek de vardır.
Dolayısıyla Pydantic'i en güncel 2 sürümüne yükseltip import'ları `pydantic.v1` alt modülünü kullanacak şekilde değiştirebilirsiniz; çoğu durumda bu doğrudan çalışır.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Uyarı
Pydantic ekibi Python 3.14'ten itibaren yeni Python sürümlerinde Pydantic v1'i artık desteklemediği için, `pydantic.v1` kullanımı da Python 3.14 ve üzeri sürümlerde desteklenmez.
///
### Aynı Uygulamada Pydantic v1 ve v2 { #pydantic-v1-and-v2-on-the-same-app }
Pydantic açısından, alanları (field) Pydantic v1 modelleriyle tanımlanmış bir Pydantic v2 modeli (ya da tersi) kullanmak **desteklenmez**.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...ancak aynı uygulamada Pydantic v1 ve v2 kullanarak **ayrı** modeller tanımlayabilirsiniz.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
Bazı durumlarda, FastAPI uygulamanızda aynı **path operation** içinde hem Pydantic v1 hem de v2 modellerini kullanmak bile mümkündür:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
Yukarıdaki örnekte input modeli bir Pydantic v1 modelidir; output modeli ( `response_model=ItemV2` ile tanımlanan) ise bir Pydantic v2 modelidir.
### Pydantic v1 Parametreleri { #pydantic-v1-parameters }
Pydantic v1 modelleriyle `Body`, `Query`, `Form` vb. parametreler için FastAPI'ye özgü bazı araçları kullanmanız gerekiyorsa, Pydantic v2'ye geçişi tamamlayana kadar bunları `fastapi.temp_pydantic_v1_params` içinden import edebilirsiniz:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Adım Adım Geçiş { #migrate-in-steps }
/// tip | İpucu
Önce `bump-pydantic` ile deneyin; testleriniz geçerse ve bu yol çalışırsa tek komutla işi bitirmiş olursunuz. ✨
///
`bump-pydantic` sizin senaryonuz için uygun değilse, aynı uygulamada hem Pydantic v1 hem de v2 modellerini birlikte kullanma desteğinden yararlanarak Pydantic v2'ye kademeli şekilde geçebilirsiniz.
Önce Pydantic'i en güncel 2 sürümüne yükseltip tüm modelleriniz için import'ları `pydantic.v1` kullanacak şekilde değiştirebilirsiniz.
Ardından modellerinizi Pydantic v1'den v2'ye gruplar hâlinde, adım adım taşımaya başlayabilirsiniz. 🚶
================================================
FILE: docs/tr/docs/how-to/separate-openapi-schemas.md
================================================
# Input ve Output için Ayrı OpenAPI Schema'ları (Ya da Değil) { #separate-openapi-schemas-for-input-and-output-or-not }
**Pydantic v2** yayınlandığından beri, üretilen OpenAPI eskisine göre biraz daha net ve **doğru**. 😎
Hatta bazı durumlarda, aynı Pydantic model için OpenAPI içinde input ve output tarafında, **default değerler** olup olmamasına bağlı olarak **iki farklı JSON Schema** bile görebilirsiniz.
Bunun nasıl çalıştığına ve gerekirse nasıl değiştirebileceğinize bir bakalım.
## Input ve Output için Pydantic Modelleri { #pydantic-models-for-input-and-output }
Default değerleri olan bir Pydantic modeliniz olduğunu varsayalım; örneğin şöyle:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Input için Model { #model-for-input }
Bu modeli şöyle input olarak kullanırsanız:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
...`description` alanı **zorunlu olmaz**. Çünkü `None` default değerine sahiptir.
### Dokümanlarda Input Modeli { #input-model-in-docs }
Bunu dokümanlarda da doğrulayabilirsiniz; `description` alanında **kırmızı yıldız** yoktur, yani required olarak işaretlenmemiştir:
### Output için Model { #model-for-output }
Ancak aynı modeli output olarak şöyle kullanırsanız:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
...`description` default değere sahip olduğu için, o alan için **hiçbir şey döndürmeseniz** bile yine de **o default değeri** alır.
### Output Response Verisi için Model { #model-for-output-response-data }
Dokümanlarla etkileşip response'u kontrol ederseniz, kod `description` alanlarından birine bir şey eklememiş olsa bile, JSON response default değeri (`null`) içerir:
Bu, alanın **her zaman bir değeri olacağı** anlamına gelir; sadece bazen bu değer `None` olabilir (JSON'da `null`).
Dolayısıyla API'nizi kullanan client'ların bu değerin var olup olmadığını kontrol etmesine gerek yoktur; **alanın her zaman mevcut olacağını varsayabilirler**, sadece bazı durumlarda default değer olan `None` gelecektir.
Bunu OpenAPI'de ifade etmenin yolu, bu alanı **required** olarak işaretlemektir; çünkü her zaman yer alacaktır.
Bu nedenle, bir modelin JSON Schema'sı **input mu output mu** kullanıldığına göre farklı olabilir:
* **input** için `description` **required olmaz**
* **output** için **required olur** (ve `None` olabilir; JSON açısından `null`)
### Dokümanlarda Output Modeli { #model-for-output-in-docs }
Dokümanlarda output modelini de kontrol edebilirsiniz; **hem** `name` **hem de** `description` alanları **kırmızı yıldız** ile **required** olarak işaretlenmiştir:
### Dokümanlarda Input ve Output Modelleri { #model-for-input-and-output-in-docs }
OpenAPI içindeki tüm kullanılabilir Schema'lara (JSON Schema'lara) bakarsanız, iki tane olduğunu göreceksiniz: biri `Item-Input`, diğeri `Item-Output`.
`Item-Input` için `description` **required değildir**, kırmızı yıldız yoktur.
Ama `Item-Output` için `description` **required**'dır, kırmızı yıldız vardır.
**Pydantic v2**'nin bu özelliğiyle API dokümantasyonunuz daha **hassas** olur; ayrıca autogenerated client'lar ve SDK'lar kullanıyorsanız, onlar da daha tutarlı ve daha iyi bir **developer experience** ile daha doğru üretilir. 🎉
## Schema'ları Ayırma { #do-not-separate-schemas }
Bazı durumlarda **input ve output için aynı schema'yı** kullanmak isteyebilirsiniz.
Bunun muhtemelen en yaygın nedeni, halihazırda autogenerated client kodlarınız/SDK'larınızın olması ve henüz bunların hepsini güncellemek istememenizdir. Büyük ihtimalle bir noktada güncellemek isteyeceksiniz, ama belki şu an değil.
Bu durumda **FastAPI**'de bu özelliği `separate_input_output_schemas=False` parametresiyle kapatabilirsiniz.
/// info | Bilgi
`separate_input_output_schemas` desteği FastAPI `0.102.0` sürümünde eklendi. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Dokümanlarda Input ve Output Modelleri için Aynı Schema { #same-schema-for-input-and-output-models-in-docs }
Artık model için input ve output tarafında tek bir schema olur: sadece `Item`. Ayrıca `description` alanı **required değildir**:
================================================
FILE: docs/tr/docs/how-to/testing-database.md
================================================
# Bir Veritabanını Test Etmek { #testing-a-database }
Veritabanları, SQL ve SQLModel hakkında [SQLModel dokümantasyonundan](https://sqlmodel.tiangolo.com/) öğrenebilirsiniz. 🤓
Ayrıca SQLModel'i FastAPI ile kullanmaya dair mini bir [öğretici](https://sqlmodel.tiangolo.com/tutorial/fastapi/) da var. ✨
Bu tutorial içinde [SQL veritabanlarını test etme](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/) hakkında bir bölüm de bulunuyor. 😎
================================================
FILE: docs/tr/docs/index.md
================================================
# FastAPI { #fastapi }
FastAPI framework, yüksek performanslı, öğrenmesi kolay, kodlaması hızlı, production'a hazır
---
**Dokümantasyon**: [https://fastapi.tiangolo.com/tr](https://fastapi.tiangolo.com/tr)
**Kaynak Kod**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI, Python'un standart type hints'lerine dayalı olarak Python ile API'lar oluşturmak için kullanılan modern ve hızlı (yüksek performanslı) bir web framework'üdür.
Temel özellikleri şunlardır:
* **Hızlı**: Çok yüksek performanslı, **NodeJS** ve **Go** ile eşit düzeyde (Starlette ve Pydantic sayesinde). [Mevcut en hızlı Python framework'lerinden biri](#performance).
* **Kodlaması Hızlı**: Özellik geliştirme hızını yaklaşık %200 ile %300 aralığında artırır. *
* **Daha az hata**: İnsan (geliştirici) kaynaklı hataları yaklaşık %40 azaltır. *
* **Sezgisel**: Harika bir editör desteği. Her yerde Tamamlama. Hata ayıklamaya daha az zaman.
* **Kolay**: Kullanımı ve öğrenmesi kolay olacak şekilde tasarlandı. Doküman okumaya daha az zaman.
* **Kısa**: Kod tekrarını minimize eder. Her parametre tanımından birden fazla özellik. Daha az hata.
* **Sağlam**: Production'a hazır kod elde edersiniz. Otomatik etkileşimli dokümantasyon ile birlikte.
* **Standardlara dayalı**: API'lar için açık standartlara dayalıdır (ve tamamen uyumludur); [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (önceden Swagger olarak biliniyordu) ve [JSON Schema](https://json-schema.org/).
* tahmin, production uygulamalar geliştiren dahili bir geliştirme ekibinin yaptığı testlere dayanmaktadır.
## Sponsorlar { #sponsors }
### Keystone Sponsor { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Gold and Silver Sponsors { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Diğer sponsorlar](https://fastapi.tiangolo.com/tr/fastapi-people/#sponsors)
## Görüşler { #opinions }
"_[...] Bugünlerde **FastAPI**'ı çok fazla kullanıyorum. [...] Aslında bunu ekibimin **Microsoft'taki ML servislerinin** tamamında kullanmayı planlıyorum. Bunlardan bazıları ana **Windows** ürününe ve bazı **Office** ürünlerine entegre ediliyor._"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_**FastAPI** için ayın üzerindeymişcesine heyecanlıyım. Çok eğlenceli!_"
Brian Okken - [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855) podcast host(ref)
---
"_Dürüst olmak gerekirse, inşa ettiğiniz şey gerçekten sağlam ve profesyonel görünüyor. Birçok açıdan, **Hug**'ın olmasını istediğim şey tam da bu - böyle bir şeyi inşa eden birini görmek gerçekten ilham verici._"
---
"_REST API'lar geliştirmek için **modern bir framework** öğrenmek istiyorsanız, **FastAPI**'a bir göz atın [...] Hızlı, kullanımı ve öğrenmesi kolay [...]_"
"_**API**'larımız için **FastAPI**'a geçtik [...] Bence hoşunuza gidecek [...]_"
---
"_Production'da Python API geliştirmek isteyen herkese **FastAPI**'ı şiddetle tavsiye ederim. **Harika tasarlanmış**, **kullanımı kolay** ve **yüksek ölçeklenebilir**; API-first geliştirme stratejimizin **kilit bir bileşeni** haline geldi ve Virtual TAC Engineer gibi birçok otomasyon ve servise güç veriyor._"
---
## FastAPI mini belgeseli { #fastapi-mini-documentary }
2025'in sonunda yayınlanan bir [FastAPI mini belgeseli](https://www.youtube.com/watch?v=mpR8ngthqiE) var, online olarak izleyebilirsiniz:
## CLI'ların FastAPI'ı: **Typer** { #typer-the-fastapi-of-clis }
Web API yerine terminalde kullanılacak bir CLI uygulaması geliştiriyorsanız [**Typer**](https://typer.tiangolo.com/)'a göz atın.
**Typer**, FastAPI'ın küçük kardeşi. Ve hedefi CLI'ların **FastAPI'ı** olmak. ⌨️ 🚀
## Gereksinimler { #requirements }
FastAPI iki devin omuzları üstünde duruyor:
* Web kısımları için [Starlette](https://www.starlette.dev/).
* Data kısımları için [Pydantic](https://docs.pydantic.dev/).
## Kurulum { #installation }
Bir [virtual environment](https://fastapi.tiangolo.com/tr/virtual-environments/) oluşturup etkinleştirelim ve ardından FastAPI'ı yükleyelim:
**Not**: Tüm terminallerde çalıştığından emin olmak için `"fastapi[standard]"` ifadesini tırnak içinde yazdığınızdan emin olun.
## Örnek { #example }
### Oluşturalım { #create-it }
Şu içerikle `main.py` adında bir dosya oluşturalım:
```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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Ya da async def kullanalım...
Eğer kodunuz `async` / `await` kullanıyorsa, `async def` kullanın:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Not**:
Eğer bilmiyorsanız, dokümanlardaki [`async` ve `await`](https://fastapi.tiangolo.com/tr/async/#in-a-hurry) hakkında _"Aceleniz mi var?"_ bölümüne bakın.
### Çalıştıralım { #run-it }
Sunucuyu şu komutla çalıştıralım:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
fastapi dev komutu hakkında...
`fastapi dev` komutu, `main.py` dosyanızı okur, içindeki **FastAPI** uygulamasını algılar ve [Uvicorn](https://www.uvicorn.dev) kullanarak bir server başlatır.
Varsayılan olarak `fastapi dev`, local geliştirme için auto-reload etkin şekilde başlar.
Daha fazla bilgi için [FastAPI CLI dokümantasyonu](https://fastapi.tiangolo.com/tr/fastapi-cli/)'nu okuyabilirsiniz.
### Kontrol Edelim { #check-it }
Tarayıcınızda şu bağlantıyı açın: [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Şu JSON response'unu göreceksiniz:
```JSON
{"item_id": 5, "q": "somequery"}
```
Artık şunları yapan bir API oluşturdunuz:
* `/` ve `/items/{item_id}` _path_'lerinde HTTP request'leri alır.
* Her iki _path_ de `GET` operasyonlarını (HTTP _method_'ları olarak da bilinir) kabul eder.
* `/items/{item_id}` _path_'i, `int` olması gereken `item_id` adlı bir _path parameter_'a sahiptir.
* `/items/{item_id}` _path_'i, opsiyonel `str` bir _query parameter_ olan `q`'ya sahiptir.
### Etkileşimli API dokümantasyonu { #interactive-api-docs }
Şimdi [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresine gidin.
Otomatik etkileşimli API dokümantasyonunu göreceksiniz ([Swagger UI](https://github.com/swagger-api/swagger-ui) tarafından sağlanır):

### Alternatif API dokümantasyonu { #alternative-api-docs }
Ve şimdi [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) adresine gidin.
Alternatif otomatik dokümantasyonu göreceksiniz ([ReDoc](https://github.com/Rebilly/ReDoc) tarafından sağlanır):

## Örneği Güncelleyelim { #example-upgrade }
Şimdi `main.py` dosyasını, `PUT` request'iyle gelen bir body alacak şekilde değiştirelim.
Body'yi Pydantic sayesinde standart Python tiplerini kullanarak tanımlayalım.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
`fastapi dev` server'ı otomatik olarak yeniden yüklemelidir.
### Etkileşimli API dokümantasyonu güncellemesi { #interactive-api-docs-upgrade }
Şimdi [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresine gidin.
* Etkileşimli API dokümantasyonu, yeni body dahil olacak şekilde otomatik olarak güncellenecek:

* "Try it out" butonuna tıklayın; parametreleri doldurmanıza ve API ile doğrudan etkileşime girmenize olanak sağlar:

* Sonra "Execute" butonuna tıklayın; kullanıcı arayüzü API'nız ile iletişim kuracak, parametreleri gönderecek, sonuçları alacak ve ekranda gösterecek:

### Alternatif API dokümantasyonu güncellemesi { #alternative-api-docs-upgrade }
Ve şimdi [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) adresine gidin.
* Alternatif dokümantasyon da yeni query parameter ve body'yi yansıtacak:

### Özet { #recap }
Özetle, parametrelerin, body'nin vb. type'larını fonksiyon parametreleri olarak **bir kere** tanımlarsınız.
Bunu standart modern Python tipleriyle yaparsınız.
Yeni bir syntax, belirli bir kütüphanenin method'larını ya da class'larını vb. öğrenmeniz gerekmez.
Sadece standart **Python**.
Örneğin bir `int` için:
```Python
item_id: int
```
ya da daha karmaşık bir `Item` modeli için:
```Python
item: Item
```
...ve bu tek tanımla şunları elde edersiniz:
* Şunlar dahil editör desteği:
* Completion.
* Type kontrolleri.
* Verinin doğrulanması:
* Veri geçersiz olduğunda otomatik ve anlaşılır hatalar.
* Çok derin iç içe JSON nesneleri için bile doğrulama.
* Girdi verisinin Dönüşümü: network'ten gelen veriyi Python verisine ve type'larına çevirir. Şunlardan okur:
* JSON.
* Path parameter'lar.
* Query parameter'lar.
* Cookie'ler.
* Header'lar.
* Form'lar.
* File'lar.
* Çıktı verisinin Dönüşümü: Python verisini ve type'larını network verisine çevirir (JSON olarak):
* Python type'larını dönüştürür (`str`, `int`, `float`, `bool`, `list`, vb.).
* `datetime` nesneleri.
* `UUID` nesneleri.
* Veritabanı modelleri.
* ...ve daha fazlası.
* 2 alternatif kullanıcı arayüzü dahil otomatik etkileşimli API dokümantasyonu:
* Swagger UI.
* ReDoc.
---
Önceki kod örneğine dönersek, **FastAPI** şunları yapacaktır:
* `GET` ve `PUT` request'leri için path'te `item_id` olduğunu doğrular.
* `GET` ve `PUT` request'leri için `item_id`'nin type'ının `int` olduğunu doğrular.
* Değilse, client faydalı ve anlaşılır bir hata görür.
* `GET` request'leri için `q` adlı opsiyonel bir query parameter olup olmadığını kontrol eder (`http://127.0.0.1:8000/items/foo?q=somequery` örneğindeki gibi).
* `q` parametresi `= None` ile tanımlandığı için opsiyoneldir.
* `None` olmasaydı zorunlu olurdu (tıpkı `PUT` örneğindeki body gibi).
* `/items/{item_id}`'ye yapılan `PUT` request'leri için body'yi JSON olarak okur:
* `str` olması gereken, zorunlu `name` alanı olduğunu kontrol eder.
* `float` olması gereken, zorunlu `price` alanı olduğunu kontrol eder.
* Varsa, `bool` olması gereken opsiyonel `is_offer` alanını kontrol eder.
* Bunların hepsi çok derin iç içe JSON nesneleri için de çalışır.
* JSON'a ve JSON'dan dönüşümü otomatik yapar.
* Her şeyi OpenAPI ile dokümante eder; bu dokümantasyon şunlar tarafından kullanılabilir:
* Etkileşimli dokümantasyon sistemleri.
* Birçok dil için otomatik client kodu üretim sistemleri.
* 2 etkileşimli dokümantasyon web arayüzünü doğrudan sunar.
---
Daha yolun başındayız, ama bunun nasıl çalıştığı hakkında fikri kaptınız.
Şu satırı değiştirmeyi deneyin:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...şundan:
```Python
... "item_name": item.name ...
```
...şuna:
```Python
... "item_price": item.price ...
```
...ve editörünüzün alanları otomatik tamamladığını ve type'larını bildiğini görün:

Daha fazla özellik içeren daha kapsamlı bir örnek için Öğretici - Kullanıcı Rehberi'ne bakın.
**Spoiler alert**: öğretici - kullanıcı rehberi şunları içerir:
* **parameter**'ların farklı yerlerden: **header**'lar, **cookie**'ler, **form alanları** ve **file**'lar olarak tanımlanması.
* `maximum_length` ya da `regex` gibi **doğrulama kısıtlamalarının** nasıl ayarlanacağı.
* Çok güçlü ve kullanımı kolay bir **Bağımlılık Enjeksiyonu** sistemi.
* **JWT tokens** ve **HTTP Basic** auth ile **OAuth2** desteği dahil güvenlik ve kimlik doğrulama.
* **Çok derin iç içe JSON modelleri** tanımlamak için daha ileri (ama aynı derecede kolay) teknikler (Pydantic sayesinde).
* [Strawberry](https://strawberry.rocks) ve diğer kütüphaneler ile **GraphQL** entegrasyonu.
* Starlette sayesinde gelen birçok ek özellik:
* **WebSockets**
* HTTPX ve `pytest` tabanlı aşırı kolay testler
* **CORS**
* **Cookie Sessions**
* ...ve daha fazlası.
### Uygulamanızı deploy edin (opsiyonel) { #deploy-your-app-optional }
İsterseniz FastAPI uygulamanızı [FastAPI Cloud](https://fastapicloud.com)'a deploy edebilirsiniz; eğer henüz yapmadıysanız gidip bekleme listesine katılın. 🚀
Zaten bir **FastAPI Cloud** hesabınız varsa (bekleme listesinden sizi davet ettiysek 😉), uygulamanızı tek bir komutla deploy edebilirsiniz.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Hepsi bu! Artık uygulamanıza bu URL'den erişebilirsiniz. ✨
#### FastAPI Cloud hakkında { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**, **FastAPI**'ın arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir.
**Bir API'ı build etmek**, **deploy etmek** ve **erişmek** süreçlerini minimum eforla kolaylaştırır.
FastAPI ile uygulama geliştirmenin sağladığı aynı **developer experience**'ı, onları cloud'a **deploy etmeye** de taşır. 🎉
FastAPI Cloud, *FastAPI and friends* open source projelerinin ana sponsoru ve finansman sağlayıcısıdır. ✨
#### Diğer cloud sağlayıcılarına deploy { #deploy-to-other-cloud-providers }
FastAPI open source'tur ve standartlara dayanır. FastAPI uygulamalarını seçtiğiniz herhangi bir cloud sağlayıcısına deploy edebilirsiniz.
FastAPI uygulamalarını onlarla deploy etmek için cloud sağlayıcınızın rehberlerini takip edin. 🤓
## Performans { #performance }
Bağımsız TechEmpower kıyaslamaları, Uvicorn altında çalışan **FastAPI** uygulamalarının [mevcut en hızlı Python framework'lerinden biri](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7) olduğunu gösteriyor; sadece Starlette ve Uvicorn'un kendisinin gerisinde (FastAPI tarafından dahili olarak kullanılır). (*)
Daha iyi anlamak için [Kıyaslamalar](https://fastapi.tiangolo.com/tr/benchmarks/) bölümüne bakın.
## Bağımlılıklar { #dependencies }
FastAPI, Pydantic ve Starlette'a bağımlıdır.
### `standard` Bağımlılıkları { #standard-dependencies }
FastAPI'ı `pip install "fastapi[standard]"` ile yüklediğinizde, opsiyonel bağımlılıkların `standard` grubuyla birlikte gelir:
Pydantic tarafından kullanılanlar:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - email doğrulaması için.
Starlette tarafından kullanılanlar:
* [`httpx`](https://www.python-httpx.org) - `TestClient` kullanmak istiyorsanız gereklidir.
* [`jinja2`](https://jinja.palletsprojects.com) - varsayılan template yapılandırmasını kullanmak istiyorsanız gereklidir.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - `request.form()` ile, form "ayrıştırma" desteği istiyorsanız gereklidir.
FastAPI tarafından kullanılanlar:
* [`uvicorn`](https://www.uvicorn.dev) - uygulamanızı yükleyen ve servis eden server için. Buna, yüksek performanslı servis için gereken bazı bağımlılıkları (örn. `uvloop`) içeren `uvicorn[standard]` dahildir.
* `fastapi-cli[standard]` - `fastapi` komutunu sağlamak için.
* Buna, FastAPI uygulamanızı [FastAPI Cloud](https://fastapicloud.com)'a deploy etmenizi sağlayan `fastapi-cloud-cli` dahildir.
### `standard` Bağımlılıkları Olmadan { #without-standard-dependencies }
`standard` opsiyonel bağımlılıklarını dahil etmek istemiyorsanız, `pip install fastapi` ile kurabilirsiniz.
### `fastapi-cloud-cli` Olmadan { #without-fastapi-cloud-cli }
FastAPI'ı standard bağımlılıklarla ama `fastapi-cloud-cli` olmadan kurmak istiyorsanız, `pip install "fastapi[standard-no-fastapi-cloud-cli]"` ile yükleyebilirsiniz.
### Ek Opsiyonel Bağımlılıklar { #additional-optional-dependencies }
Yüklemek isteyebileceğiniz bazı ek bağımlılıklar da vardır.
Ek opsiyonel Pydantic bağımlılıkları:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - ayar yönetimi için.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - Pydantic ile kullanılacak ek type'lar için.
Ek opsiyonel FastAPI bağımlılıkları:
* [`orjson`](https://github.com/ijl/orjson) - `ORJSONResponse` kullanmak istiyorsanız gereklidir.
* [`ujson`](https://github.com/esnme/ultrajson) - `UJSONResponse` kullanmak istiyorsanız gereklidir.
## Lisans { #license }
Bu proje MIT lisansı şartları altında lisanslanmıştır.
================================================
FILE: docs/tr/docs/learn/index.md
================================================
# Öğren { #learn }
**FastAPI** öğrenmek için giriş bölümleri ve öğreticiler burada yer alıyor.
Burayı, bir **kitap**, bir **kurs**, FastAPI öğrenmenin **resmi** ve önerilen yolu olarak düşünebilirsiniz. 😎
================================================
FILE: docs/tr/docs/project-generation.md
================================================
# Full Stack FastAPI Şablonu { #full-stack-fastapi-template }
Şablonlar genellikle belirli bir kurulumla gelir, ancak esnek ve özelleştirilebilir olacak şekilde tasarlanırlar. Bu sayede şablonu projenizin gereksinimlerine göre değiştirip uyarlayabilir, çok iyi bir başlangıç noktası olarak kullanabilirsiniz. 🏁
Bu şablonu başlangıç için kullanabilirsiniz; çünkü ilk kurulumun, güvenliğin, veritabanının ve bazı API endpoint'lerinin önemli bir kısmı sizin için zaten hazırlanmıştır.
GitHub Repository: [Full Stack FastAPI Şablonu](https://github.com/tiangolo/full-stack-fastapi-template)
## Full Stack FastAPI Şablonu - Teknoloji Yığını ve Özellikler { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ Python backend API için [**FastAPI**](https://fastapi.tiangolo.com/tr).
- 🧰 Python SQL veritabanı etkileşimleri (ORM) için [SQLModel](https://sqlmodel.tiangolo.com).
- 🔍 FastAPI'nin kullandığı; veri doğrulama ve ayarlar yönetimi için [Pydantic](https://docs.pydantic.dev).
- 💾 SQL veritabanı olarak [PostgreSQL](https://www.postgresql.org).
- 🚀 frontend için [React](https://react.dev).
- 💃 TypeScript, hooks, Vite ve modern bir frontend stack'inin diğer parçalarını kullanır.
- 🎨 frontend component'leri için [Tailwind CSS](https://tailwindcss.com) ve [shadcn/ui](https://ui.shadcn.com).
- 🤖 Otomatik üretilen bir frontend client.
- 🧪 End-to-End testleri için [Playwright](https://playwright.dev).
- 🦇 Dark mode desteği.
- 🐋 Geliştirme ve production için [Docker Compose](https://www.docker.com).
- 🔒 Varsayılan olarak güvenli password hashing.
- 🔑 JWT (JSON Web Token) authentication.
- 📫 E-posta tabanlı şifre kurtarma.
- ✅ [Pytest](https://pytest.org) ile testler.
- 📞 Reverse proxy / load balancer olarak [Traefik](https://traefik.io).
- 🚢 Docker Compose kullanarak deployment talimatları; otomatik HTTPS sertifikalarını yönetmek için bir frontend Traefik proxy'sini nasıl kuracağınız dahil.
- 🏭 GitHub Actions tabanlı CI (continuous integration) ve CD (continuous deployment).
================================================
FILE: docs/tr/docs/python-types.md
================================================
# Python Tiplerine Giriş { #python-types-intro }
Python, isteğe bağlı "type hints" (diğer adıyla "type annotations") desteğine sahiptir.
Bu **"type hints"** veya annotations, bir değişkenin tip'ini bildirmeye yarayan özel bir sözdizimidir.
Değişkenleriniz için tip bildirerek, editörler ve araçlar size daha iyi destek sağlayabilir.
Bu, Python type hints hakkında sadece **hızlı bir eğitim / bilgi tazeleme** dokümanıdır. **FastAPI** ile kullanmak için gereken minimum bilgiyi kapsar... ki aslında bu çok azdır.
**FastAPI** tamamen bu type hints üzerine kuruludur; bunlar ona birçok avantaj ve fayda sağlar.
Ancak hiç **FastAPI** kullanmasanız bile, bunlar hakkında biraz öğrenmeniz size fayda sağlayacaktır.
/// note | Not
Eğer bir Python uzmanıysanız ve type hints hakkında her şeyi zaten biliyorsanız, sonraki bölüme geçin.
///
## Motivasyon { #motivation }
Basit bir örnekle başlayalım:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Bu programı çalıştırınca şu çıktıyı alırsınız:
```
John Doe
```
Fonksiyon şunları yapar:
* `first_name` ve `last_name` değerlerini alır.
* `title()` ile her birinin ilk harfini büyük harfe çevirir.
* Ortada bir boşluk olacak şekilde Birleştirir.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Düzenleyelim { #edit-it }
Bu çok basit bir program.
Ama şimdi bunu sıfırdan yazdığınızı hayal edin.
Bir noktada fonksiyon tanımını yazmaya başlamış olacaktınız, parametreler hazır...
Ama sonra "ilk harfi büyük harfe çeviren method"u çağırmanız gerekiyor.
`upper` mıydı? `uppercase` miydi? `first_uppercase`? `capitalize`?
Sonra eski programcı dostuyla denersiniz: editör autocomplete.
Fonksiyonun ilk parametresi olan `first_name`'i yazarsınız, sonra bir nokta (`.`) ve ardından autocomplete'i tetiklemek için `Ctrl+Space`'e basarsınız.
Ama ne yazık ki, işe yarar bir şey göremezsiniz:
### Tipleri ekleyelim { #add-types }
Önceki sürümden tek bir satırı değiştirelim.
Fonksiyonun parametreleri olan şu parçayı:
```Python
first_name, last_name
```
şuna çevireceğiz:
```Python
first_name: str, last_name: str
```
Bu kadar.
Bunlar "type hints":
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Bu, aşağıdaki gibi default değerler bildirmekle aynı şey değildir:
```Python
first_name="john", last_name="doe"
```
Bu farklı bir şey.
Eşittir (`=`) değil, iki nokta (`:`) kullanıyoruz.
Ve type hints eklemek, normalde onlarsız ne oluyorsa onu değiştirmez.
Ama şimdi, type hints ile o fonksiyonu oluşturmanın ortasında olduğunuzu tekrar hayal edin.
Aynı noktada, `Ctrl+Space` ile autocomplete'i tetiklemeye çalışırsınız ve şunu görürsünüz:
Bununla birlikte, seçenekleri görerek kaydırabilirsiniz; ta ki "tanıdık gelen" seçeneği bulana kadar:
## Daha fazla motivasyon { #more-motivation }
Şu fonksiyona bakın, zaten type hints içeriyor:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Editör değişkenlerin tiplerini bildiği için, sadece completion değil, aynı zamanda hata kontrolleri de alırsınız:
Artık bunu düzeltmeniz gerektiğini, `age`'i `str(age)` ile string'e çevirmeniz gerektiğini biliyorsunuz:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Tipleri Bildirmek { #declaring-types }
Type hints bildirmek için ana yeri az önce gördünüz: fonksiyon parametreleri.
Bu, **FastAPI** ile kullanırken de onları en çok kullanacağınız yerdir.
### Basit tipler { #simple-types }
Sadece `str` değil, tüm standart Python tiplerini bildirebilirsiniz.
Örneğin şunları kullanabilirsiniz:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### `typing` modülü { #typing-module }
Bazı ek kullanım durumları için standart kütüphanedeki `typing` modülünden bazı şeyleri import etmeniz gerekebilir. Örneğin bir şeyin "herhangi bir tip" olabileceğini bildirmek istediğinizde, `typing` içindeki `Any`'yi kullanabilirsiniz:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic tipler { #generic-types }
Bazı tipler, köşeli parantez içinde "type parameters" alarak iç tiplerini tanımlayabilir; örneğin "string listesi" `list[str]` olarak bildirilir.
Bu şekilde type parameter alabilen tiplere **Generic types** veya **Generics** denir.
Aynı builtin tipleri generics olarak kullanabilirsiniz (köşeli parantez ve içinde tiplerle):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
Örneğin, `str`'lerden oluşan bir `list` olan bir değişken tanımlayalım.
Değişkeni, aynı iki nokta (`:`) sözdizimiyle bildirin.
Tip olarak `list` yazın.
`list`, bazı iç tipleri barındıran bir tip olduğundan, bunları köşeli parantez içine yazarsınız:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Bilgi
Köşeli parantez içindeki bu iç tiplere "type parameters" denir.
Bu durumda `str`, `list`'e verilen type parameter'dır.
///
Bu şu demektir: "`items` değişkeni bir `list` ve bu listedeki her bir öğe `str`".
Bunu yaparak, editörünüz listeden öğeleri işlerken bile destek sağlayabilir:
Tipler olmadan, bunu başarmak neredeyse imkansızdır.
`item` değişkeninin, `items` listesindeki elemanlardan biri olduğuna dikkat edin.
Ve yine de editör bunun bir `str` olduğunu bilir ve buna göre destek sağlar.
#### Tuple ve Set { #tuple-and-set }
`tuple`'ları ve `set`'leri bildirmek için de aynısını yaparsınız:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Bu şu anlama gelir:
* `items_t` değişkeni 3 öğeli bir `tuple`'dır: bir `int`, bir başka `int` ve bir `str`.
* `items_s` değişkeni bir `set`'tir ve her bir öğesi `bytes` tipindedir.
#### Dict { #dict }
Bir `dict` tanımlamak için, virgülle ayrılmış 2 type parameter verirsiniz.
İlk type parameter, `dict`'in key'leri içindir.
İkinci type parameter, `dict`'in value'ları içindir:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Bu şu anlama gelir:
* `prices` değişkeni bir `dict`'tir:
* Bu `dict`'in key'leri `str` tipindedir (örneğin her bir öğenin adı).
* Bu `dict`'in value'ları `float` tipindedir (örneğin her bir öğenin fiyatı).
#### Union { #union }
Bir değişkenin **birkaç tipten herhangi biri** olabileceğini bildirebilirsiniz; örneğin bir `int` veya bir `str`.
Bunu tanımlamak için, her iki tipi ayırmak üzere dikey çizgi (`|`) kullanırsınız.
Buna "union" denir, çünkü değişken bu iki tip kümesinin birleşimindeki herhangi bir şey olabilir.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Bu, `item`'ın `int` veya `str` olabileceği anlamına gelir.
#### Muhtemelen `None` { #possibly-none }
Bir değerin `str` gibi bir tipi olabileceğini ama aynı zamanda `None` da olabileceğini bildirebilirsiniz.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Sadece `str` yerine `str | None` kullanmak, aslında değer `None` olabilecekken her zaman `str` olduğunu varsaydığınız hataları editörün yakalamanıza yardımcı olur.
### Tip olarak sınıflar { #classes-as-types }
Bir sınıfı da bir değişkenin tipi olarak bildirebilirsiniz.
Örneğin, adı olan bir `Person` sınıfınız olsun:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Sonra bir değişkeni `Person` tipinde olacak şekilde bildirebilirsiniz:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
Ve sonra, yine tüm editör desteğini alırsınız:
Bunun "`one_person`, `Person` sınıfının bir **instance**'ıdır" anlamına geldiğine dikkat edin.
"`one_person`, `Person` adlı **class**'tır" anlamına gelmez.
## Pydantic modelleri { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/), data validation yapmak için bir Python kütüphanesidir.
Verinin "shape"'ini attribute'lara sahip sınıflar olarak tanımlarsınız.
Ve her attribute'un bir tipi vardır.
Ardından o sınıfın bir instance'ını bazı değerlerle oluşturursunuz; bu değerleri doğrular, uygun tipe dönüştürür (gerekliyse) ve size tüm veriyi içeren bir nesne verir.
Ve bu ortaya çıkan nesne ile tüm editör desteğini alırsınız.
Resmî Pydantic dokümanlarından bir örnek:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Bilgi
Daha fazlasını öğrenmek için [Pydantic'in dokümanlarına bakın](https://docs.pydantic.dev/).
///
**FastAPI** tamamen Pydantic üzerine kuruludur.
Bunların pratikte nasıl çalıştığını [Eğitim - Kullanım Kılavuzu](tutorial/index.md) içinde çok daha fazla göreceksiniz.
## Metadata Annotations ile Type Hints { #type-hints-with-metadata-annotations }
Python'da ayrıca, `Annotated` kullanarak bu type hints içine **ek üstveri** koymayı sağlayan bir özellik de vardır.
`Annotated`'ı `typing` içinden import edebilirsiniz.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Python'un kendisi bu `Annotated` ile bir şey yapmaz. Editörler ve diğer araçlar için tip hâlâ `str`'dir.
Ama **FastAPI**'ye uygulamanızın nasıl davranmasını istediğinize dair ek metadata sağlamak için `Annotated` içindeki bu alanı kullanabilirsiniz.
Hatırlanması gereken önemli nokta: `Annotated`'a verdiğiniz **ilk *type parameter***, **gerçek tip**tir. Geri kalanı ise diğer araçlar için metadatadır.
Şimdilik, sadece `Annotated`'ın var olduğunu ve bunun standart Python olduğunu bilmeniz yeterli. 😎
İleride bunun ne kadar **güçlü** olabildiğini göreceksiniz.
/// tip | İpucu
Bunun **standart Python** olması, editörünüzde mümkün olan **en iyi developer experience**'ı almaya devam edeceğiniz anlamına gelir; kodu analiz etmek ve refactor etmek için kullandığınız araçlarla da, vb. ✨
Ayrıca kodunuzun pek çok başka Python aracı ve kütüphanesiyle çok uyumlu olacağı anlamına gelir. 🚀
///
## **FastAPI**'de type hints { #type-hints-in-fastapi }
**FastAPI**, birkaç şey yapmak için bu type hints'ten faydalanır.
**FastAPI** ile type hints kullanarak parametreleri bildirirsiniz ve şunları elde edersiniz:
* **Editör desteği**.
* **Tip kontrolleri**.
...ve **FastAPI** aynı bildirimleri şunlar için de kullanır:
* **Gereksinimleri tanımlamak**: request path parameters, query parameters, headers, bodies, bağımlılıklar (dependencies), vb.
* **Veriyi dönüştürmek**: request'ten gerekli tipe.
* **Veriyi doğrulamak**: her request'ten gelen veriyi:
* Veri geçersiz olduğunda client'a dönen **otomatik hatalar** üretmek.
* OpenAPI kullanarak API'yi **dokümante etmek**:
* bu, daha sonra otomatik etkileşimli dokümantasyon kullanıcı arayüzleri tarafından kullanılır.
Bunların hepsi kulağa soyut gelebilir. Merak etmeyin. Tüm bunları [Eğitim - Kullanım Kılavuzu](tutorial/index.md) içinde çalışırken göreceksiniz.
Önemli olan, standart Python tiplerini tek bir yerde kullanarak (daha fazla sınıf, decorator vb. eklemek yerine), **FastAPI**'nin sizin için işin büyük kısmını yapmasıdır.
/// info | Bilgi
Tüm tutorial'ı zaten bitirdiyseniz ve tipler hakkında daha fazlasını görmek için geri döndüyseniz, iyi bir kaynak: [`mypy`'nin "cheat sheet"i](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/tr/docs/resources/index.md
================================================
# Kaynaklar { #resources }
Ek kaynaklar, dış bağlantılar ve daha fazlası. ✈️
================================================
FILE: docs/tr/docs/translation-banner.md
================================================
/// details | 🌐 Yapay Zekâ ve İnsanlar Tarafından Çeviri
Bu çeviri, insanlar tarafından yönlendirilen bir yapay zekâ ile oluşturuldu. 🤝
Orijinal anlamın yanlış anlaşılması ya da kulağa doğal gelmeme gibi hatalar içerebilir. 🤖
[Yapay zekâ LLM'ini daha iyi yönlendirmemize yardımcı olarak](https://fastapi.tiangolo.com/tr/contributing/#translations) bu çeviriyi iyileştirebilirsiniz.
[İngilizce sürüm](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/tr/docs/tutorial/background-tasks.md
================================================
# Arka Plan Görevleri { #background-tasks }
Response döndürüldükten *sonra* çalıştırılacak arka plan görevleri tanımlayabilirsiniz.
Bu, request’ten sonra yapılması gereken; ancak client’ın response’u almadan önce tamamlanmasını beklemesine gerek olmayan işlemler için kullanışlıdır.
Örneğin:
* Bir işlem gerçekleştirdikten sonra gönderilen email bildirimleri:
* Bir email server’a bağlanmak ve email göndermek genellikle "yavaş" olduğundan (birkaç saniye), response’u hemen döndürüp email bildirimini arka planda gönderebilirsiniz.
* Veri işleme:
* Örneğin, yavaş bir süreçten geçmesi gereken bir dosya aldığınızı düşünün; "Accepted" (HTTP 202) response’u döndürüp dosyayı arka planda işleyebilirsiniz.
## `BackgroundTasks` Kullanımı { #using-backgroundtasks }
Önce `BackgroundTasks`’i import edin ve *path operation function*’ınızda `BackgroundTasks` tip bildirimi olan bir parametre tanımlayın:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI**, sizin için `BackgroundTasks` tipinde bir obje oluşturur ve onu ilgili parametre olarak geçirir.
## Bir Görev Fonksiyonu Oluşturun { #create-a-task-function }
Arka plan görevi olarak çalıştırılacak bir fonksiyon oluşturun.
Bu, parametre alabilen standart bir fonksiyondur.
`async def` de olabilir, normal `def` de olabilir; **FastAPI** bunu doğru şekilde nasıl ele alacağını bilir.
Bu örnekte görev fonksiyonu bir dosyaya yazacaktır (email göndermeyi simüle ediyor).
Ve yazma işlemi `async` ve `await` kullanmadığı için fonksiyonu normal `def` ile tanımlarız:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Arka Plan Görevini Ekleyin { #add-the-background-task }
*Path operation function*’ınızın içinde, görev fonksiyonunuzu `.add_task()` metodu ile *background tasks* objesine ekleyin:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` şu argümanları alır:
* Arka planda çalıştırılacak bir görev fonksiyonu (`write_notification`).
* Görev fonksiyonuna sırayla geçirilecek argümanlar (`email`).
* Görev fonksiyonuna geçirilecek keyword argümanlar (`message="some notification"`).
## Dependency Injection { #dependency-injection }
`BackgroundTasks` kullanımı dependency injection sistemiyle de çalışır; `BackgroundTasks` tipinde bir parametreyi birden fazla seviyede tanımlayabilirsiniz: bir *path operation function* içinde, bir dependency’de (dependable), bir sub-dependency’de, vb.
**FastAPI** her durumda ne yapılacağını ve aynı objenin nasıl yeniden kullanılacağını bilir; böylece tüm arka plan görevleri birleştirilir ve sonrasında arka planda çalıştırılır:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
Bu örnekte, response gönderildikten *sonra* mesajlar `log.txt` dosyasına yazılacaktır.
Request’te bir query varsa, log’a bir arka plan göreviyle yazılır.
Ardından *path operation function* içinde oluşturulan başka bir arka plan görevi, `email` path parametresini kullanarak bir mesaj yazar.
## Teknik Detaylar { #technical-details }
`BackgroundTasks` sınıfı doğrudan [`starlette.background`](https://www.starlette.dev/background/)’dan gelir.
`fastapi` üzerinden import edebilmeniz ve yanlışlıkla `starlette.background` içindeki alternatif `BackgroundTask`’i (sonunda `s` olmadan) import etmemeniz için FastAPI’nin içine doğrudan import/eklenmiştir.
Sadece `BackgroundTasks` (ve `BackgroundTask` değil) kullanarak, bunu bir *path operation function* parametresi olarak kullanmak ve gerisini **FastAPI**’nin sizin için halletmesini sağlamak mümkündür; tıpkı `Request` objesini doğrudan kullanırken olduğu gibi.
FastAPI’de `BackgroundTask`’i tek başına kullanmak hâlâ mümkündür; ancak bu durumda objeyi kendi kodunuzda oluşturmanız ve onu içeren bir Starlette `Response` döndürmeniz gerekir.
Daha fazla detayı [Starlette’in Background Tasks için resmi dokümantasyonunda](https://www.starlette.dev/background/) görebilirsiniz.
## Dikkat Edilmesi Gerekenler { #caveat }
Yoğun arka plan hesaplamaları yapmanız gerekiyorsa ve bunun aynı process tarafından çalıştırılmasına şart yoksa (örneğin memory, değişkenler vb. paylaşmanız gerekmiyorsa), [Celery](https://docs.celeryq.dev) gibi daha büyük araçları kullanmak size fayda sağlayabilir.
Bunlar genellikle daha karmaşık konfigurasyonlar ve RabbitMQ veya Redis gibi bir mesaj/iş kuyruğu yöneticisi gerektirir; ancak arka plan görevlerini birden fazla process’te ve özellikle birden fazla server’da çalıştırmanıza olanak tanırlar.
Ancak aynı **FastAPI** app’i içindeki değişkenlere ve objelere erişmeniz gerekiyorsa veya küçük arka plan görevleri (email bildirimi göndermek gibi) yapacaksanız, doğrudan `BackgroundTasks` kullanabilirsiniz.
## Özet { #recap }
Arka plan görevleri eklemek için *path operation function*’larda ve dependency’lerde parametre olarak `BackgroundTasks`’i import edip kullanın.
================================================
FILE: docs/tr/docs/tutorial/bigger-applications.md
================================================
# Daha Büyük Uygulamalar - Birden Fazla Dosya { #bigger-applications-multiple-files }
Bir uygulama veya web API geliştirirken, her şeyi tek bir dosyaya sığdırabilmek nadirdir.
**FastAPI**, tüm esnekliği korurken uygulamanızı yapılandırmanıza yardımcı olan pratik bir araç sunar.
/// info | Bilgi
Flask'ten geliyorsanız, bu yapı Flask'in Blueprints'ine denk gelir.
///
## Örnek Bir Dosya Yapısı { #an-example-file-structure }
Diyelim ki şöyle bir dosya yapınız var:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | İpucu
Birden fazla `__init__.py` dosyası var: her dizinde veya alt dizinde bir tane.
Bu sayede bir dosyadaki kodu diğerine import edebilirsiniz.
Örneğin `app/main.py` içinde şöyle bir satırınız olabilir:
```
from app.routers import items
```
///
* `app` dizini her şeyi içerir. Ayrıca boş bir `app/__init__.py` dosyası olduğu için bir "Python package" (bir "Python module" koleksiyonu) olur: `app`.
* İçinde bir `app/main.py` dosyası vardır. Bir Python package'in (içinde `__init__.py` dosyası olan bir dizinin) içinde olduğundan, o package'in bir "module"’üdür: `app.main`.
* Benzer şekilde `app/dependencies.py` dosyası da bir "module"’dür: `app.dependencies`.
* `app/routers/` adında bir alt dizin vardır ve içinde başka bir `__init__.py` dosyası bulunur; dolayısıyla bu bir "Python subpackage"’dir: `app.routers`.
* `app/routers/items.py` dosyası `app/routers/` package’i içinde olduğundan bir submodule’dür: `app.routers.items`.
* `app/routers/users.py` için de aynı şekilde, başka bir submodule’dür: `app.routers.users`.
* `app/internal/` adında bir alt dizin daha vardır ve içinde başka bir `__init__.py` dosyası bulunur; dolayısıyla bu da bir "Python subpackage"’dir: `app.internal`.
* Ve `app/internal/admin.py` dosyası başka bir submodule’dür: `app.internal.admin`.
Aynı dosya yapısı, yorumlarla birlikte:
```bash
.
├── app # "app" bir Python package'idir
│ ├── __init__.py # bu dosya, "app"i bir "Python package" yapar
│ ├── main.py # "main" module'ü, örn. import app.main
│ ├── dependencies.py # "dependencies" module'ü, örn. import app.dependencies
│ └── routers # "routers" bir "Python subpackage"idir
│ │ ├── __init__.py # "routers"ı bir "Python subpackage" yapar
│ │ ├── items.py # "items" submodule'ü, örn. import app.routers.items
│ │ └── users.py # "users" submodule'ü, örn. import app.routers.users
│ └── internal # "internal" bir "Python subpackage"idir
│ ├── __init__.py # "internal"ı bir "Python subpackage" yapar
│ └── admin.py # "admin" submodule'ü, örn. import app.internal.admin
```
## `APIRouter` { #apirouter }
Diyelim ki sadece kullanıcıları yönetmeye ayrılmış dosyanız `/app/routers/users.py` içindeki submodule olsun.
Kullanıcılarla ilgili *path operation*’ları, kodun geri kalanından ayrı tutmak istiyorsunuz; böylece düzenli kalır.
Ancak bu hâlâ aynı **FastAPI** uygulaması/web API’sinin bir parçasıdır (aynı "Python Package" içinde).
Bu module için *path operation*’ları `APIRouter` kullanarak oluşturabilirsiniz.
### `APIRouter` Import Edin { #import-apirouter }
`FastAPI` class’ında yaptığınız gibi import edip bir "instance" oluşturursunuz:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### `APIRouter` ile *Path Operations* { #path-operations-with-apirouter }
Sonra bunu kullanarak *path operation*’larınızı tanımlarsınız.
`FastAPI` class’ını nasıl kullanıyorsanız aynı şekilde kullanın:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
`APIRouter`’ı "mini bir `FastAPI`" class’ı gibi düşünebilirsiniz.
Aynı seçeneklerin hepsi desteklenir.
Aynı `parameters`, `responses`, `dependencies`, `tags`, vb.
/// tip | İpucu
Bu örnekte değişkenin adı `router`. Ancak istediğiniz gibi adlandırabilirsiniz.
///
Bu `APIRouter`’ı ana `FastAPI` uygulamasına ekleyeceğiz; ama önce dependency’lere ve bir diğer `APIRouter`’a bakalım.
## Dependencies { #dependencies }
Uygulamanın birden fazla yerinde kullanılacak bazı dependency’lere ihtiyacımız olacağını görüyoruz.
Bu yüzden onları ayrı bir `dependencies` module’üne koyuyoruz (`app/dependencies.py`).
Şimdi, özel bir `X-Token` header'ını okumak için basit bir dependency kullanalım:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | İpucu
Örneği basit tutmak için uydurma bir header kullanıyoruz.
Ancak gerçek senaryolarda, entegre [Security yardımcı araçlarını](security/index.md) kullanarak daha iyi sonuç alırsınız.
///
## `APIRouter` ile Başka Bir Module { #another-module-with-apirouter }
Diyelim ki uygulamanızdaki "items" ile ilgili endpoint'ler de `app/routers/items.py` module’ünde olsun.
Şunlar için *path operation*’larınız var:
* `/items/`
* `/items/{item_id}`
Bu, `app/routers/users.py` ile aynı yapıdadır.
Ancak biraz daha akıllı davranıp kodu sadeleştirmek istiyoruz.
Bu module’deki tüm *path operation*’ların şu ortak özelliklere sahip olduğunu biliyoruz:
* Path `prefix`: `/items`.
* `tags`: (tek bir tag: `items`).
* Ek `responses`.
* `dependencies`: hepsinin, oluşturduğumuz `X-Token` dependency’sine ihtiyacı var.
Dolayısıyla bunları her *path operation*’a tek tek eklemek yerine `APIRouter`’a ekleyebiliriz.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Her *path operation*’ın path’i aşağıdaki gibi `/` ile başlamak zorunda olduğundan:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...prefix’in sonunda `/` olmamalıdır.
Yani bu örnekte prefix `/items` olur.
Ayrıca, bu router içindeki tüm *path operation*’lara uygulanacak bir `tags` listesi ve ek `responses` da ekleyebiliriz.
Ve router’daki tüm *path operation*’lara eklenecek, her request için çalıştırılıp çözülecek bir `dependencies` listesi de ekleyebiliriz.
/// tip | İpucu
[ *path operation decorator*’larındaki dependency’lerde](dependencies/dependencies-in-path-operation-decorators.md) olduğu gibi, *path operation function*’ınıza herhangi bir değer aktarılmayacağını unutmayın.
///
Sonuç olarak item path’leri artık:
* `/items/`
* `/items/{item_id}`
...tam da istediğimiz gibi olur.
* Hepsi, içinde tek bir string `"items"` bulunan bir tag listesiyle işaretlenir.
* Bu "tags", özellikle otomatik interaktif dokümantasyon sistemleri (OpenAPI) için çok faydalıdır.
* Hepsi önceden tanımlı `responses`’ları içerir.
* Bu *path operation*’ların hepsinde, öncesinde `dependencies` listesi değerlendirilip çalıştırılır.
* Ayrıca belirli bir *path operation* içinde dependency tanımlarsanız, **onlar da çalıştırılır**.
* Önce router dependency’leri, sonra decorator’daki [`dependencies`](dependencies/dependencies-in-path-operation-decorators.md), sonra da normal parametre dependency’leri çalışır.
* Ayrıca [`scopes` ile `Security` dependency’leri](../advanced/security/oauth2-scopes.md) de ekleyebilirsiniz.
/// tip | İpucu
`APIRouter` içinde `dependencies` kullanmak, örneğin bir grup *path operation* için kimlik doğrulamayı zorunlu kılmakta kullanılabilir. Dependency’leri tek tek her birine eklemeseniz bile.
///
/// check | Ek bilgi
`prefix`, `tags`, `responses` ve `dependencies` parametreleri (çoğu başka örnekte olduğu gibi) kod tekrarını önlemenize yardımcı olan, **FastAPI**’nin bir özelliğidir.
///
### Dependency'leri Import Edin { #import-the-dependencies }
Bu kod `app.routers.items` module’ünde, yani `app/routers/items.py` dosyasında duruyor.
Dependency function’ını ise `app.dependencies` module’ünden, yani `app/dependencies.py` dosyasından almamız gerekiyor.
Bu yüzden dependency’ler için `..` ile relative import kullanıyoruz:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Relative Import Nasıl Çalışır { #how-relative-imports-work }
/// tip | İpucu
Import’ların nasıl çalıştığını çok iyi biliyorsanız, bir sonraki bölüme geçin.
///
Tek bir nokta `.`, örneğin:
```Python
from .dependencies import get_token_header
```
şu anlama gelir:
* Bu module’ün (yani `app/routers/items.py` dosyasının) bulunduğu package içinden başla ( `app/routers/` dizini)...
* `dependencies` module’ünü bul (`app/routers/dependencies.py` gibi hayali bir dosya)...
* ve oradan `get_token_header` function’ını import et.
Ama o dosya yok; bizim dependency’lerimiz `app/dependencies.py` dosyasında.
Uygulama/dosya yapımızın nasıl göründüğünü hatırlayın:
---
İki nokta `..`, örneğin:
```Python
from ..dependencies import get_token_header
```
şu anlama gelir:
* Bu module’ün bulunduğu package içinden başla (`app/routers/` dizini)...
* üst (parent) package’e çık (`app/` dizini)...
* burada `dependencies` module’ünü bul (`app/dependencies.py` dosyası)...
* ve oradan `get_token_header` function’ını import et.
Bu doğru şekilde çalışır! 🎉
---
Aynı şekilde, üç nokta `...` kullansaydık:
```Python
from ...dependencies import get_token_header
```
şu anlama gelirdi:
* Bu module’ün bulunduğu package içinden başla (`app/routers/` dizini)...
* üst package’e çık (`app/` dizini)...
* sonra bir üstüne daha çık (orada bir üst package yok; `app` en üst seviye 😱)...
* ve orada `dependencies` module’ünü bul (`app/dependencies.py` dosyası)...
* ve oradan `get_token_header` function’ını import et.
Bu, `app/` dizininin üstünde, kendi `__init__.py` dosyası olan başka bir package’e işaret ederdi. Ama bizde böyle bir şey yok. Dolayısıyla bu örnekte hata verirdi. 🚨
Artık nasıl çalıştığını bildiğinize göre, uygulamalarınız ne kadar karmaşık olursa olsun relative import’ları kullanabilirsiniz. 🤓
### Özel `tags`, `responses` ve `dependencies` Ekleyin { #add-some-custom-tags-responses-and-dependencies }
`/items` prefix’ini ya da `tags=["items"]` değerini her *path operation*’a tek tek eklemiyoruz; çünkü bunları `APIRouter`’a ekledik.
Ama yine de belirli bir *path operation*’a uygulanacak _ek_ `tags` tanımlayabilir, ayrıca o *path operation*’a özel `responses` ekleyebiliriz:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | İpucu
Bu son *path operation*’da tag kombinasyonu şöyle olur: `["items", "custom"]`.
Ayrıca dokümantasyonda iki response da görünür: biri `404`, diğeri `403`.
///
## Ana `FastAPI` { #the-main-fastapi }
Şimdi `app/main.py` module’üne bakalım.
Burada `FastAPI` class’ını import edip kullanırsınız.
Bu dosya, uygulamanızda her şeyi bir araya getiren ana dosya olacak.
Mantığın büyük kısmı artık kendi module’lerinde yaşayacağı için ana dosya oldukça basit kalır.
### `FastAPI` Import Edin { #import-fastapi }
Normal şekilde bir `FastAPI` class’ı oluşturursunuz.
Hatta her `APIRouter` için olan dependency’lerle birleştirilecek [global dependencies](dependencies/global-dependencies.md) bile tanımlayabilirsiniz:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### `APIRouter` Import Edin { #import-the-apirouter }
Şimdi `APIRouter` içeren diğer submodule’leri import ediyoruz:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
`app/routers/users.py` ve `app/routers/items.py` dosyaları aynı Python package’i olan `app`’in parçası olan submodule’ler olduğu için, onları "relative import" ile tek bir nokta `.` kullanarak import edebiliriz.
### Import Nasıl Çalışır { #how-the-importing-works }
Şu bölüm:
```Python
from .routers import items, users
```
şu anlama gelir:
* Bu module’ün (yani `app/main.py` dosyasının) bulunduğu package içinden başla (`app/` dizini)...
* `routers` subpackage’ini bul (`app/routers/` dizini)...
* ve buradan `items` submodule’ünü (`app/routers/items.py`) ve `users` submodule’ünü (`app/routers/users.py`) import et...
`items` module’ünün içinde `router` adında bir değişken vardır (`items.router`). Bu, `app/routers/items.py` dosyasında oluşturduğumuz aynı değişkendir; bir `APIRouter` nesnesidir.
Sonra aynı işlemi `users` module’ü için de yaparız.
Ayrıca şöyle de import edebilirdik:
```Python
from app.routers import items, users
```
/// info | Bilgi
İlk sürüm "relative import"tur:
```Python
from .routers import items, users
```
İkinci sürüm "absolute import"tur:
```Python
from app.routers import items, users
```
Python Packages ve Modules hakkında daha fazlası için, [Python'ın Modules ile ilgili resmi dokümantasyonunu](https://docs.python.org/3/tutorial/modules.html) okuyun.
///
### İsim Çakışmalarını Önleyin { #avoid-name-collisions }
`items` submodule’ünü doğrudan import ediyoruz; sadece içindeki `router` değişkenini import etmiyoruz.
Çünkü `users` submodule’ünde de `router` adlı başka bir değişken var.
Eğer şöyle sırayla import etseydik:
```Python
from .routers.items import router
from .routers.users import router
```
`users` içindeki `router`, `items` içindeki `router`’ın üstüne yazardı ve ikisini aynı anda kullanamazdık.
Bu yüzden ikisini de aynı dosyada kullanabilmek için submodule’leri doğrudan import ediyoruz:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### `users` ve `items` için `APIRouter`’ları Dahil Edin { #include-the-apirouters-for-users-and-items }
Şimdi `users` ve `items` submodule’lerindeki `router`’ları dahil edelim:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Bilgi
`users.router`, `app/routers/users.py` dosyasının içindeki `APIRouter`’ı içerir.
`items.router` ise `app/routers/items.py` dosyasının içindeki `APIRouter`’ı içerir.
///
`app.include_router()` ile her bir `APIRouter`’ı ana `FastAPI` uygulamasına ekleyebiliriz.
Böylece o router içindeki tüm route’lar uygulamanın bir parçası olarak dahil edilir.
/// note | Teknik Detaylar
Aslında içeride, `APIRouter` içinde tanımlanan her *path operation* için bir *path operation* oluşturur.
Yani perde arkasında, her şey tek bir uygulamaymış gibi çalışır.
///
/// check | Ek bilgi
Router’ları dahil ederken performans konusunda endişelenmeniz gerekmez.
Bu işlem mikrosaniyeler sürer ve sadece startup sırasında olur.
Dolayısıyla performansı etkilemez. ⚡
///
### Özel `prefix`, `tags`, `responses` ve `dependencies` ile Bir `APIRouter` Dahil Edin { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Şimdi, kurumunuzun size `app/internal/admin.py` dosyasını verdiğini düşünelim.
Bu dosyada, kurumunuzun birden fazla proje arasında paylaştığı bazı admin *path operation*’larını içeren bir `APIRouter` var.
Bu örnekte çok basit olacak. Ancak kurum içinde başka projelerle paylaşıldığı için, bunu değiştirip `prefix`, `dependencies`, `tags` vs. doğrudan `APIRouter`’a ekleyemediğimizi varsayalım:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Yine de bu `APIRouter`’ı dahil ederken özel bir `prefix` ayarlamak istiyoruz ki tüm *path operation*’ları `/admin` ile başlasın; ayrıca bu projede hâlihazırda kullandığımız `dependencies` ile güvene almak, `tags` ve `responses` eklemek istiyoruz.
Orijinal `APIRouter`’ı değiştirmeden, bu parametreleri `app.include_router()`’a vererek hepsini tanımlayabiliriz:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Böylece orijinal `APIRouter` değişmeden kalır; yani aynı `app/internal/admin.py` dosyasını kurum içindeki diğer projelerle de paylaşmaya devam edebiliriz.
Sonuç olarak, uygulamamızda `admin` module’ündeki her bir *path operation* şunlara sahip olur:
* `/admin` prefix’i.
* `admin` tag’i.
* `get_token_header` dependency’si.
* `418` response’u. 🍵
Ancak bu sadece bizim uygulamamızdaki o `APIRouter` için geçerlidir; onu kullanan diğer kodlar için değil.
Dolayısıyla örneğin diğer projeler aynı `APIRouter`’ı farklı bir authentication yöntemiyle kullanabilir.
### Bir *Path Operation* Dahil Edin { #include-a-path-operation }
*Path operation*’ları doğrudan `FastAPI` uygulamasına da ekleyebiliriz.
Burada bunu yapıyoruz... sadece yapabildiğimizi göstermek için 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
ve `app.include_router()` ile eklenen diğer tüm *path operation*’larla birlikte doğru şekilde çalışır.
/// info | Çok Teknik Detaylar
**Not**: Bu oldukça teknik bir detay; büyük ihtimalle **direkt geçebilirsiniz**.
---
`APIRouter`’lar "mount" edilmez; uygulamanın geri kalanından izole değildir.
Çünkü *path operation*’larını OpenAPI şemasına ve kullanıcı arayüzlerine dahil etmek istiyoruz.
Onları tamamen izole edip bağımsız şekilde "mount" edemediğimiz için, *path operation*’lar doğrudan eklenmek yerine "klonlanır" (yeniden oluşturulur).
///
## `pyproject.toml` İçinde `entrypoint` Yapılandırın { #configure-the-entrypoint-in-pyproject-toml }
FastAPI `app` nesneniz `app/main.py` içinde yaşadığına göre, `pyproject.toml` dosyanızda `entrypoint`’i şöyle yapılandırabilirsiniz:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
bu da şu import’a denktir:
```python
from app.main import app
```
Böylece `fastapi` komutu uygulamanızı nerede bulacağını bilir.
/// Note | Not
Komuta dosya yolunu da verebilirsiniz, örneğin:
```console
$ fastapi dev app/main.py
```
Ama o zaman her `fastapi` komutunu çalıştırdığınızda doğru yolu hatırlayıp geçirmeniz gerekir.
Ayrıca, diğer araçlar uygulamayı bulamayabilir; örneğin [VS Code Eklentisi](../editor-support.md) veya [FastAPI Cloud](https://fastapicloud.com). Bu yüzden `pyproject.toml` içinde `entrypoint` kullanmanız önerilir.
///
## Otomatik API Dokümanını Kontrol Edin { #check-the-automatic-api-docs }
Şimdi uygulamanızı çalıştırın:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Ve dokümanları [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresinde açın.
Tüm submodule’lerdeki path’leri, doğru path’ler (ve prefix’ler) ve doğru tag’lerle birlikte içeren otomatik API dokümanını göreceksiniz:
## Aynı Router'ı Farklı `prefix` ile Birden Fazla Kez Dahil Edin { #include-the-same-router-multiple-times-with-different-prefix }
`.include_router()` ile aynı router’ı farklı prefix’ler kullanarak birden fazla kez de dahil edebilirsiniz.
Örneğin aynı API’yi `/api/v1` ve `/api/latest` gibi farklı prefix’ler altında sunmak için faydalı olabilir.
Bu, muhtemelen ihtiyacınız olmayan ileri seviye bir kullanımdır; ancak gerekirse diye mevcut.
## Bir `APIRouter`’ı Başka Birine Dahil Edin { #include-an-apirouter-in-another }
Bir `APIRouter`’ı `FastAPI` uygulamasına dahil ettiğiniz gibi, bir `APIRouter`’ı başka bir `APIRouter`’a da şu şekilde dahil edebilirsiniz:
```Python
router.include_router(other_router)
```
`router`’ı `FastAPI` uygulamasına dahil etmeden önce bunu yaptığınızdan emin olun; böylece `other_router` içindeki *path operation*’lar da dahil edilmiş olur.
================================================
FILE: docs/tr/docs/tutorial/body-fields.md
================================================
# Body - Alanlar { #body-fields }
`Query`, `Path` ve `Body` ile *path operation function* parametrelerinde ek doğrulama ve metadata tanımlayabildiğiniz gibi, Pydantic modellerinin içinde de Pydantic'in `Field`'ını kullanarak doğrulama ve metadata tanımlayabilirsiniz.
## `Field`'ı import edin { #import-field }
Önce import etmeniz gerekir:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Uyarı
`Field`'ın, diğerlerinin (`Query`, `Path`, `Body` vb.) aksine `fastapi`'den değil doğrudan `pydantic`'den import edildiğine dikkat edin.
///
## Model attribute'larını tanımlayın { #declare-model-attributes }
Ardından `Field`'ı model attribute'larıyla birlikte kullanabilirsiniz:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field`, `Query`, `Path` ve `Body` ile aynı şekilde çalışır; aynı parametrelerin tamamına sahiptir, vb.
/// note | Teknik Detaylar
Aslında, `Query`, `Path` ve birazdan göreceğiniz diğerleri, ortak bir `Param` sınıfının alt sınıflarından nesneler oluşturur; `Param` sınıfı da Pydantic'in `FieldInfo` sınıfının bir alt sınıfıdır.
Pydantic'in `Field`'ı da `FieldInfo`'nun bir instance'ını döndürür.
`Body` ayrıca doğrudan `FieldInfo`'nun bir alt sınıfından nesneler döndürür. Daha sonra göreceğiniz başka bazıları da `Body` sınıfının alt sınıflarıdır.
`fastapi`'den `Query`, `Path` ve diğerlerini import ettiğinizde, bunların aslında özel sınıflar döndüren fonksiyonlar olduğunu unutmayın.
///
/// tip | İpucu
Type, varsayılan değer ve `Field` ile tanımlanan her model attribute'unun yapısının, *path operation function* parametresiyle aynı olduğuna dikkat edin; sadece `Path`, `Query` ve `Body` yerine `Field` kullanılmıştır.
///
## Ek bilgi ekleyin { #add-extra-information }
`Field`, `Query`, `Body` vb. içinde ek bilgi tanımlayabilirsiniz. Bu bilgiler oluşturulan JSON Schema'ya dahil edilir.
Örnek (examples) tanımlamayı öğrenirken, dokümanların ilerleyen kısımlarında ek bilgi ekleme konusunu daha ayrıntılı göreceksiniz.
/// warning | Uyarı
`Field`'a geçirilen ekstra key'ler, uygulamanız için üretilen OpenAPI schema'sında da yer alır.
Bu key'ler OpenAPI spesifikasyonunun bir parçası olmak zorunda olmadığından, örneğin [OpenAPI validator](https://validator.swagger.io/) gibi bazı OpenAPI araçları üretilen schema'nızla çalışmayabilir.
///
## Özet { #recap }
Model attribute'ları için ek doğrulamalar ve metadata tanımlamak üzere Pydantic'in `Field`'ını kullanabilirsiniz.
Ayrıca, ek keyword argument'ları kullanarak JSON Schema'ya ekstra metadata da iletebilirsiniz.
================================================
FILE: docs/tr/docs/tutorial/body-multiple-params.md
================================================
# Body - Birden Fazla Parametre { #body-multiple-parameters }
Artık `Path` ve `Query` kullanmayı gördüğümüze göre, request body bildirimlerinin daha ileri kullanım senaryolarına bakalım.
## `Path`, `Query` ve body parametrelerini karıştırma { #mix-path-query-and-body-parameters }
Öncelikle, elbette `Path`, `Query` ve request body parametre bildirimlerini serbestçe karıştırabilirsiniz ve **FastAPI** ne yapacağını bilir.
Ayrıca, varsayılan değeri `None` yaparak body parametrelerini opsiyonel olarak da tanımlayabilirsiniz:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Not
Bu durumda body'den alınacak `item` opsiyoneldir. Çünkü varsayılan değeri `None` olarak ayarlanmıştır.
///
## Birden fazla body parametresi { #multiple-body-parameters }
Önceki örnekte, *path operation*'lar `Item`'ın özelliklerini içeren bir JSON body beklerdi, örneğin:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Ancak birden fazla body parametresi de tanımlayabilirsiniz; örneğin `item` ve `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
Bu durumda **FastAPI**, fonksiyonda birden fazla body parametresi olduğunu fark eder (iki parametre de Pydantic modelidir).
Bunun üzerine, body içinde anahtar (field name) olarak parametre adlarını kullanır ve şu şekilde bir body bekler:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Not
`item` daha öncekiyle aynı şekilde tanımlanmış olsa bile, artık body içinde `item` anahtarı altında gelmesi beklenir.
///
**FastAPI**, request'ten otomatik dönüşümü yapar; böylece `item` parametresi kendi içeriğini alır, `user` için de aynı şekilde olur.
Birleşik verinin validasyonunu yapar ve OpenAPI şeması ile otomatik dokümantasyonda da bunu bu şekilde dokümante eder.
## Body içinde tekil değerler { #singular-values-in-body }
Query ve path parametreleri için ek veri tanımlamak üzere `Query` ve `Path` olduğu gibi, **FastAPI** bunların karşılığı olarak `Body` de sağlar.
Örneğin, önceki modeli genişleterek, aynı body içinde `item` ve `user` dışında bir de `importance` anahtarı olmasını isteyebilirsiniz.
Bunu olduğu gibi tanımlarsanız, tekil bir değer olduğu için **FastAPI** bunun bir query parametresi olduğunu varsayar.
Ama `Body` kullanarak, **FastAPI**'ye bunu body içinde başka bir anahtar olarak ele almasını söyleyebilirsiniz:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
Bu durumda **FastAPI** şu şekilde bir body bekler:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Yine veri tiplerini dönüştürür, validate eder, dokümante eder, vb.
## Birden fazla body parametresi ve query { #multiple-body-params-and-query }
Elbette ihtiyaç duyduğunuzda, body parametrelerine ek olarak query parametreleri de tanımlayabilirsiniz.
Varsayılan olarak tekil değerler query parametresi olarak yorumlandığı için, ayrıca `Query` eklemeniz gerekmez; şöyle yazmanız yeterlidir:
```Python
q: str | None = None
```
Örneğin:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Bilgi
`Body`, `Query`, `Path` ve daha sonra göreceğiniz diğerleriyle aynı ek validasyon ve metadata parametrelerine de sahiptir.
///
## Tek bir body parametresini gömme { #embed-a-single-body-parameter }
Diyelim ki Pydantic'teki `Item` modelinden gelen yalnızca tek bir `item` body parametreniz var.
Varsayılan olarak **FastAPI**, body'nin doğrudan bu modelin içeriği olmasını bekler.
Ancak, ek body parametreleri tanımladığınızda olduğu gibi, `item` anahtarı olan bir JSON ve onun içinde modelin içeriğini beklemesini istiyorsanız, `Body`'nin özel parametresi olan `embed`'i kullanabilirsiniz:
```Python
item: Item = Body(embed=True)
```
yani şöyle:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
Bu durumda **FastAPI** şu şekilde bir body bekler:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
şunun yerine:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Özet { #recap }
Bir request yalnızca tek bir body içerebilse de, *path operation function*'ınıza birden fazla body parametresi ekleyebilirsiniz.
Ancak **FastAPI** bunu yönetir; fonksiyonunuza doğru veriyi verir ve *path operation* içinde doğru şemayı validate edip dokümante eder.
Ayrıca tekil değerlerin body'nin bir parçası olarak alınmasını da tanımlayabilirsiniz.
Ve yalnızca tek bir parametre tanımlanmış olsa bile, **FastAPI**'ye body'yi bir anahtarın içine gömmesini söyleyebilirsiniz.
================================================
FILE: docs/tr/docs/tutorial/body-nested-models.md
================================================
# Body - İç İçe Modeller { #body-nested-models }
**FastAPI** ile (Pydantic sayesinde) istediğiniz kadar derin iç içe geçmiş modelleri tanımlayabilir, doğrulayabilir, dokümante edebilir ve kullanabilirsiniz.
## List alanları { #list-fields }
Bir attribute’u bir alt tipe sahip olacak şekilde tanımlayabilirsiniz. Örneğin, bir Python `list`:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Bu, `tags`’in bir list olmasını sağlar; ancak list’in elemanlarının tipini belirtmez.
## Tip parametresi olan list alanları { #list-fields-with-type-parameter }
Ancak Python’da, iç tipleri olan list’leri (ya da "type parameter" içeren tipleri) tanımlamanın belirli bir yolu vardır:
### Tip parametresiyle bir `list` tanımlayın { #declare-a-list-with-a-type-parameter }
`list`, `dict`, `tuple` gibi type parameter (iç tip) alan tipleri tanımlamak için, iç tipi(leri) köşeli parantezlerle "type parameter" olarak verin: `[` ve `]`
```Python
my_list: list[str]
```
Bu, tip tanımları için standart Python sözdizimidir.
İç tipleri olan model attribute’ları için de aynı standart sözdizimini kullanın.
Dolayısıyla örneğimizde, `tags`’i özel olarak bir "string list’i" yapabiliriz:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Set tipleri { #set-types }
Sonra bunu düşününce, tag’lerin tekrar etmemesi gerektiğini fark ederiz; büyük ihtimalle benzersiz string’ler olmalıdır.
Python’da benzersiz öğelerden oluşan kümeler için özel bir veri tipi vardır: `set`.
O zaman `tags`’i string’lerden oluşan bir set olarak tanımlayabiliriz:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Böylece duplicate veri içeren bir request alsanız bile, bu veri benzersiz öğelerden oluşan bir set’e dönüştürülür.
Ve bu veriyi ne zaman output etseniz, kaynakta duplicate olsa bile, benzersiz öğelerden oluşan bir set olarak output edilir.
Ayrıca buna göre annotate / dokümante edilir.
## İç İçe Modeller { #nested-models }
Bir Pydantic modelinin her attribute’unun bir tipi vardır.
Ancak bu tip, kendi başına başka bir Pydantic modeli de olabilir.
Yani belirli attribute adları, tipleri ve validation kurallarıyla derin iç içe JSON "object"leri tanımlayabilirsiniz.
Hem de istediğiniz kadar iç içe.
### Bir alt model tanımlayın { #define-a-submodel }
Örneğin bir `Image` modeli tanımlayabiliriz:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Alt modeli tip olarak kullanın { #use-the-submodel-as-a-type }
Ardından bunu bir attribute’un tipi olarak kullanabiliriz:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Bu da **FastAPI**’nin aşağıdakine benzer bir body bekleyeceği anlamına gelir:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Yine, sadece bu tanımı yaparak **FastAPI** ile şunları elde edersiniz:
* Editör desteği (tamamlama vb.), iç içe modeller için bile
* Veri dönüştürme
* Veri doğrulama (validation)
* Otomatik dokümantasyon
## Özel tipler ve doğrulama { #special-types-and-validation }
`str`, `int`, `float` vb. normal tekil tiplerin yanında, `str`’den türeyen daha karmaşık tekil tipleri de kullanabilirsiniz.
Tüm seçenekleri görmek için [Pydantic Türlerine Genel Bakış](https://docs.pydantic.dev/latest/concepts/types/) sayfasına göz atın. Sonraki bölümde bazı örnekleri göreceksiniz.
Örneğin `Image` modelinde bir `url` alanımız olduğuna göre, bunu `str` yerine Pydantic’in `HttpUrl` tipinden bir instance olacak şekilde tanımlayabiliriz:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
String’in geçerli bir URL olup olmadığı kontrol edilir ve JSON Schema / OpenAPI’de de buna göre dokümante edilir.
## Alt modellerden oluşan list’lere sahip attribute’lar { #attributes-with-lists-of-submodels }
Pydantic modellerini `list`, `set` vb. tiplerin alt tipi olarak da kullanabilirsiniz:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Bu, aşağıdaki gibi bir JSON body bekler (dönüştürür, doğrular, dokümante eder vb.):
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Bilgi
`images` key’inin artık image object’lerinden oluşan bir list içerdiğine dikkat edin.
///
## Çok derin iç içe modeller { #deeply-nested-models }
İstediğiniz kadar derin iç içe modeller tanımlayabilirsiniz:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Bilgi
`Offer`’ın bir `Item` list’i olduğuna, `Item`’ların da opsiyonel bir `Image` list’ine sahip olduğuna dikkat edin.
///
## Sadece list olan body’ler { #bodies-of-pure-lists }
Beklediğiniz JSON body’nin en üst seviye değeri bir JSON `array` (Python’da `list`) ise, tipi Pydantic modellerinde olduğu gibi fonksiyonun parametresinde tanımlayabilirsiniz:
```Python
images: list[Image]
```
şu örnekte olduğu gibi:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Her yerde editör desteği { #editor-support-everywhere }
Ve her yerde editör desteği alırsınız.
List içindeki öğeler için bile:
Pydantic modelleri yerine doğrudan `dict` ile çalışsaydınız bu tür bir editör desteğini alamazdınız.
Ancak bunlarla uğraşmanız da gerekmez; gelen dict’ler otomatik olarak dönüştürülür ve output’unuz da otomatik olarak JSON’a çevrilir.
## Rastgele `dict` body’leri { #bodies-of-arbitrary-dicts }
Body’yi, key’leri bir tipte ve value’ları başka bir tipte olan bir `dict` olarak da tanımlayabilirsiniz.
Bu şekilde (Pydantic modellerinde olduğu gibi) geçerli field/attribute adlarının önceden ne olduğunu bilmeniz gerekmez.
Bu, önceden bilmediğiniz key’leri almak istediğiniz durumlarda faydalıdır.
---
Bir diğer faydalı durum da key’lerin başka bir tipte olmasını istediğiniz zamandır (ör. `int`).
Burada göreceğimiz şey de bu.
Bu durumda, `int` key’lere ve `float` value’lara sahip olduğu sürece herhangi bir `dict` kabul edersiniz:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | İpucu
JSON key olarak yalnızca `str` destekler, bunu unutmayın.
Ancak Pydantic otomatik veri dönüştürme yapar.
Yani API client’larınız key’leri sadece string olarak gönderebilse bile, bu string’ler saf tamsayı içeriyorsa Pydantic bunları dönüştürür ve doğrular.
Ve `weights` olarak aldığınız `dict`, gerçekte `int` key’lere ve `float` value’lara sahip olur.
///
## Özet { #recap }
**FastAPI** ile Pydantic modellerinin sağladığı en yüksek esnekliği elde ederken, kodunuzu da basit, kısa ve şık tutarsınız.
Üstelik tüm avantajlarla birlikte:
* Editör desteği (her yerde tamamlama!)
* Veri dönüştürme (diğer adıyla parsing / serialization)
* Veri doğrulama (validation)
* Schema dokümantasyonu
* Otomatik dokümanlar
================================================
FILE: docs/tr/docs/tutorial/body-updates.md
================================================
# Body - Güncellemeler { #body-updates }
## `PUT` ile değiştirerek güncelleme { #update-replacing-with-put }
Bir öğeyi güncellemek için [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operasyonunu kullanabilirsiniz.
Girdi verisini JSON olarak saklanabilecek bir formata (ör. bir NoSQL veritabanı ile) dönüştürmek için `jsonable_encoder` kullanabilirsiniz. Örneğin, `datetime` değerlerini `str`'ye çevirmek gibi.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT`, mevcut verinin yerine geçmesi gereken veriyi almak için kullanılır.
### Değiştirerek güncelleme uyarısı { #warning-about-replacing }
Bu, `bar` öğesini `PUT` ile, body içinde şu verilerle güncellemek isterseniz:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
zaten kayıtlı olan `"tax": 20.2` alanını içermediği için, input model `"tax": 10.5` varsayılan değerini kullanacaktır.
Ve veri, bu "yeni" `tax` değeri olan `10.5` ile kaydedilecektir.
## `PATCH` ile kısmi güncellemeler { #partial-updates-with-patch }
Veriyi *kısmen* güncellemek için [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operasyonunu da kullanabilirsiniz.
Bu, yalnızca güncellemek istediğiniz veriyi gönderip, geri kalanını olduğu gibi bırakabileceğiniz anlamına gelir.
/// note | Not
`PATCH`, `PUT`'a göre daha az yaygın kullanılır ve daha az bilinir.
Hatta birçok ekip, kısmi güncellemeler için bile yalnızca `PUT` kullanır.
Bunları nasıl isterseniz öyle kullanmakta **özgürsünüz**; **FastAPI** herhangi bir kısıtlama dayatmaz.
Ancak bu kılavuz, aşağı yukarı, bunların nasıl kullanılması amaçlandığını gösterir.
///
### Pydantic'in `exclude_unset` parametresini kullanma { #using-pydantics-exclude-unset-parameter }
Kısmi güncellemeler almak istiyorsanız, Pydantic modelinin `.model_dump()` metodundaki `exclude_unset` parametresini kullanmak çok faydalıdır.
Örneğin: `item.model_dump(exclude_unset=True)`.
Bu, `item` modeli oluşturulurken set edilmiş verileri içeren; varsayılan değerleri hariç tutan bir `dict` üretir.
Sonrasında bunu, yalnızca set edilmiş (request'te gönderilmiş) veriyi içeren; varsayılan değerleri atlayan bir `dict` üretmek için kullanabilirsiniz:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Pydantic'in `update` parametresini kullanma { #using-pydantics-update-parameter }
Artık `.model_copy()` ile mevcut modelin bir kopyasını oluşturup, güncellenecek verileri içeren bir `dict` ile `update` parametresini geçebilirsiniz.
Örneğin: `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Kısmi güncellemeler özeti { #partial-updates-recap }
Özetle, kısmi güncelleme uygulamak için şunları yaparsınız:
* (İsteğe bağlı olarak) `PUT` yerine `PATCH` kullanın.
* Kayıtlı veriyi alın.
* Bu veriyi bir Pydantic modeline koyun.
* Input modelinden, varsayılan değerler olmadan bir `dict` üretin (`exclude_unset` kullanarak).
* Bu şekilde, modelinizdeki varsayılan değerlerle daha önce saklanmış değerlerin üzerine yazmak yerine, yalnızca kullanıcının gerçekten set ettiği değerleri güncellersiniz.
* Kayıtlı modelin bir kopyasını oluşturun ve alınan kısmi güncellemeleri kullanarak attribute'larını güncelleyin (`update` parametresini kullanarak).
* Kopyalanan modeli DB'nizde saklanabilecek bir şeye dönüştürün (ör. `jsonable_encoder` kullanarak).
* Bu, modelin `.model_dump()` metodunu yeniden kullanmaya benzer; ancak değerlerin JSON'a dönüştürülebilecek veri tiplerine çevrilmesini garanti eder (ör. `datetime` -> `str`).
* Veriyi DB'nize kaydedin.
* Güncellenmiş modeli döndürün.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | İpucu
Aynı tekniği HTTP `PUT` operasyonu ile de kullanabilirsiniz.
Ancak buradaki örnek `PATCH` kullanıyor, çünkü bu kullanım senaryoları için tasarlanmıştır.
///
/// note | Not
Input modelin yine de doğrulandığına dikkat edin.
Dolayısıyla, tüm attribute'ların atlanabildiği kısmi güncellemeler almak istiyorsanız, tüm attribute'ları optional olarak işaretlenmiş (varsayılan değerlerle veya `None` ile) bir modele ihtiyacınız vardır.
**Güncelleme** için tüm değerleri optional olan modeller ile **oluşturma** için zorunlu değerlere sahip modelleri ayırmak için, [Extra Models](extra-models.md) bölümünde anlatılan fikirleri kullanabilirsiniz.
///
================================================
FILE: docs/tr/docs/tutorial/body.md
================================================
# Request Body { #request-body }
Bir client'ten (örneğin bir tarayıcıdan) API'nize veri göndermeniz gerektiğinde, bunu **request body** olarak gönderirsiniz.
Bir **request** body, client'in API'nize gönderdiği veridir. Bir **response** body ise API'nizin client'e gönderdiği veridir.
API'niz neredeyse her zaman bir **response** body göndermek zorundadır. Ancak client'lerin her zaman **request body** göndermesi gerekmez; bazen sadece bir path isterler, belki birkaç query parametresiyle birlikte, ama body göndermezler.
Bir **request** body tanımlamak için, tüm gücü ve avantajlarıyla [Pydantic](https://docs.pydantic.dev/) modellerini kullanırsınız.
/// info | Bilgi
Veri göndermek için şunlardan birini kullanmalısınız: `POST` (en yaygını), `PUT`, `DELETE` veya `PATCH`.
`GET` request'i ile body göndermek, spesifikasyonlarda tanımsız bir davranıştır; yine de FastAPI bunu yalnızca çok karmaşık/uç kullanım senaryoları için destekler.
Önerilmediği için Swagger UI ile etkileşimli dokümanlar, `GET` kullanırken body için dokümantasyonu göstermez ve aradaki proxy'ler bunu desteklemeyebilir.
///
## Pydantic'in `BaseModel`'ini import edin { #import-pydantics-basemodel }
Önce, `pydantic` içinden `BaseModel`'i import etmeniz gerekir:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Veri modelinizi oluşturun { #create-your-data-model }
Sonra veri modelinizi, `BaseModel`'den kalıtım alan bir class olarak tanımlarsınız.
Tüm attribute'lar için standart Python type'larını kullanın:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Query parametrelerini tanımlarken olduğu gibi, bir model attribute'ü default bir değere sahipse zorunlu değildir. Aksi halde zorunludur. Sadece opsiyonel yapmak için `None` kullanın.
Örneğin, yukarıdaki model şu şekilde bir JSON "`object`" (veya Python `dict`) tanımlar:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...`description` ve `tax` opsiyonel olduğu için (default değerleri `None`), şu JSON "`object`" da geçerli olur:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Parametre olarak tanımlayın { #declare-it-as-a-parameter }
Bunu *path operation*'ınıza eklemek için, path ve query parametrelerini tanımladığınız şekilde tanımlayın:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...ve type'ını, oluşturduğunuz model olan `Item` olarak belirtin.
## Sonuçlar { #results }
Sadece bu Python type tanımıyla, **FastAPI** şunları yapar:
* Request'in body'sini JSON olarak okur.
* İlgili type'lara dönüştürür (gerekirse).
* Veriyi doğrular (validate eder).
* Veri geçersizse, tam olarak nerede ve hangi verinin hatalı olduğunu söyleyen, anlaşılır bir hata döndürür.
* Aldığı veriyi `item` parametresi içinde size verir.
* Fonksiyonda bunun type'ını `Item` olarak tanımladığınız için, tüm attribute'lar ve type'ları için editor desteğini (tamamlama vb.) de alırsınız.
* Modeliniz için [JSON Schema](https://json-schema.org) tanımları üretir; projeniz için anlamlıysa bunları başka yerlerde de kullanabilirsiniz.
* Bu şemalar üretilen OpenAPI şemasının bir parçası olur ve otomatik dokümantasyon UIs tarafından kullanılır.
## Otomatik dokümanlar { #automatic-docs }
Modellerinizin JSON Schema'ları, OpenAPI tarafından üretilen şemanın bir parçası olur ve etkileşimli API dokümanlarında gösterilir:
Ayrıca, ihtiyaç duyan her *path operation* içindeki API dokümanlarında da kullanılır:
## Editor desteği { #editor-support }
Editor'ünüzde, fonksiyonunuzun içinde her yerde type hint'leri ve tamamlama (completion) alırsınız (Pydantic modeli yerine `dict` alsaydınız bu olmazdı):
Yanlış type işlemleri için hata kontrolleri de alırsınız:
Bu bir tesadüf değil; tüm framework bu tasarımın etrafında inşa edildi.
Ayrıca, bunun tüm editor'lerle çalışacağından emin olmak için herhangi bir implementasyon yapılmadan önce tasarım aşamasında kapsamlı şekilde test edildi.
Hatta bunu desteklemek için Pydantic'in kendisinde bile bazı değişiklikler yapıldı.
Önceki ekran görüntüleri [Visual Studio Code](https://code.visualstudio.com) ile alınmıştır.
Ancak [PyCharm](https://www.jetbrains.com/pycharm/) ve diğer Python editor'lerinin çoğunda da aynı editor desteğini alırsınız:
/// tip | İpucu
Editor olarak [PyCharm](https://www.jetbrains.com/pycharm/) kullanıyorsanız, [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/) kullanabilirsiniz.
Pydantic modelleri için editor desteğini şu açılardan iyileştirir:
* auto-completion
* type checks
* refactoring
* searching
* inspections
///
## Modeli kullanın { #use-the-model }
Fonksiyonun içinde model nesnesinin tüm attribute'larına doğrudan erişebilirsiniz:
{* ../../docs_src/body/tutorial002_py310.py *}
## Request body + path parametreleri { #request-body-path-parameters }
Path parametrelerini ve request body'yi aynı anda tanımlayabilirsiniz.
**FastAPI**, path parametreleriyle eşleşen fonksiyon parametrelerinin **path'ten alınması** gerektiğini ve Pydantic model olarak tanımlanan fonksiyon parametrelerinin **request body'den alınması** gerektiğini anlayacaktır.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Request body + path + query parametreleri { #request-body-path-query-parameters }
**body**, **path** ve **query** parametrelerini aynı anda da tanımlayabilirsiniz.
**FastAPI** bunların her birini tanır ve veriyi doğru yerden alır.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Fonksiyon parametreleri şu şekilde tanınır:
* Parametre, **path** içinde de tanımlıysa path parametresi olarak kullanılır.
* Parametre **tekil bir type**'taysa (`int`, `float`, `str`, `bool` vb.), **query** parametresi olarak yorumlanır.
* Parametre bir **Pydantic model** type'ı olarak tanımlandıysa, request **body** olarak yorumlanır.
/// note | Not
FastAPI, `q` değerinin zorunlu olmadığını `= None` default değerinden anlayacaktır.
`str | None`, FastAPI tarafından bu değerin zorunlu olmadığını belirlemek için kullanılmaz; FastAPI bunun zorunlu olmadığını `= None` default değeri olduğu için bilir.
Ancak type annotation'larını eklemek, editor'ünüzün size daha iyi destek vermesini ve hataları yakalamasını sağlar.
///
## Pydantic olmadan { #without-pydantic }
Pydantic modellerini kullanmak istemiyorsanız, **Body** parametrelerini de kullanabilirsiniz. [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body) dokümanına bakın.
================================================
FILE: docs/tr/docs/tutorial/cookie-param-models.md
================================================
# Cookie Parametre Modelleri { #cookie-parameter-models }
Birbirleriyle ilişkili bir **cookie** grubunuz varsa, bunları tanımlamak için bir **Pydantic model** oluşturabilirsiniz. 🍪
Bu sayede **model'i yeniden kullanabilir**, **birden fazla yerde** tekrar tekrar kullanabilir ve tüm parametreler için validation ve metadata'yı tek seferde tanımlayabilirsiniz. 😎
/// note | Not
This is supported since FastAPI version `0.115.0`. 🤓
///
/// tip | İpucu
Aynı teknik `Query`, `Cookie` ve `Header` için de geçerlidir. 😎
///
## Pydantic Model ile Cookies { #cookies-with-a-pydantic-model }
İhtiyacınız olan **cookie** parametrelerini bir **Pydantic model** içinde tanımlayın ve ardından parametreyi `Cookie` olarak bildirin:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI**, request ile gelen **cookies** içinden **her bir field** için veriyi **extract** eder ve size tanımladığınız Pydantic model'i verir.
## Dokümanları Kontrol Edin { #check-the-docs }
Tanımlanan cookie'leri `/docs` altındaki docs UI'da görebilirsiniz:
/// info | Bilgi
Tarayıcıların cookie'leri özel biçimlerde ve arka planda yönetmesi nedeniyle, **JavaScript**'in cookie'lere erişmesine kolayca izin vermediğini aklınızda bulundurun.
`/docs` altındaki **API docs UI**'a giderseniz, *path operation*'larınız için cookie'lerin **dokümantasyonunu** görebilirsiniz.
Ancak verileri **doldurup** "Execute" düğmesine tıklasanız bile, docs UI **JavaScript** ile çalıştığı için cookie'ler gönderilmez; dolayısıyla hiç değer girmemişsiniz gibi bir **error** mesajı görürsünüz.
///
## Fazladan Cookie'leri Yasaklayın { #forbid-extra-cookies }
Bazı özel kullanım senaryolarında (muhtemelen çok yaygın değildir) almak istediğiniz cookie'leri **kısıtlamak** isteyebilirsiniz.
API'niz artık kendi cookie onayı'nı kontrol etme gücüne sahip. 🤪🍪
Pydantic'in model configuration'ını kullanarak `extra` olan herhangi bir field'ı `forbid` edebilirsiniz:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Bir client **fazladan cookie** göndermeye çalışırsa, bir **error** response alır.
Onayınızı almak için bunca çaba harcayan zavallı cookie banner'ları... API'nin bunu reddetmesi için. 🍪
Örneğin client, değeri `good-list-please` olan bir `santa_tracker` cookie'si göndermeye çalışırsa, client `santa_tracker` cookie'ye izin verilmiyor diyen bir **error** response alır:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Özet { #summary }
**FastAPI**'de **cookie** tanımlamak için **Pydantic model**'lerini kullanabilirsiniz. 😎
================================================
FILE: docs/tr/docs/tutorial/cookie-params.md
================================================
# Cookie (Çerez) Parametreleri { #cookie-parameters }
`Query` ve `Path` parametrelerini tanımladığınız şekilde Cookie parametreleri tanımlayabilirsiniz.
## `Cookie`'yi Import Edin { #import-cookie }
Öncelikle, `Cookie`'yi import edin:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## `Cookie` Parametrelerini Tanımlayın { #declare-cookie-parameters }
Ardından, `Path` ve `Query` ile aynı yapıyı kullanarak Cookie parametrelerini tanımlayın.
Varsayılan değeri ve tüm ekstra doğrulama veya annotation parametrelerini tanımlayabilirsiniz:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Teknik Detaylar
`Cookie`, `Path` ve `Query`'nin "kardeş" sınıfıdır. O da aynı ortak `Param` sınıfından miras alır.
Ancak `fastapi`'dan `Query`, `Path`, `Cookie` ve diğerlerini import ettiğinizde, bunlar aslında özel sınıflar döndüren fonksiyonlardır, bunu unutmayın.
///
/// info | Bilgi
Cookie'leri tanımlamak için `Cookie` kullanmanız gerekir, aksi halde parametreler query parametreleri olarak yorumlanır.
///
/// info | Bilgi
**Tarayıcılar cookie'leri** özel şekillerde ve arka planda işlediği için, **JavaScript**'in onlara dokunmasına kolayca izin **vermezler**.
`/docs` adresindeki **API docs UI**'a giderseniz, *path operation*'larınız için cookie'lerin **dokümantasyonunu** görebilirsiniz.
Ancak **veriyi doldurup** "Execute" düğmesine tıklasanız bile, docs UI **JavaScript** ile çalıştığı için cookie'ler gönderilmez ve herhangi bir değer yazmamışsınız gibi bir **hata** mesajı görürsünüz.
///
## Özet { #recap }
`Query` ve `Path` ile aynı ortak deseni kullanarak, cookie'leri `Cookie` ile tanımlayın.
================================================
FILE: docs/tr/docs/tutorial/cors.md
================================================
# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing }
[CORS veya "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), tarayıcıda çalışan bir frontend’in JavaScript kodunun bir backend ile iletişim kurduğu ve backend’in frontend’den farklı bir "origin"de olduğu durumları ifade eder.
## Origin { #origin }
Origin; protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`) ve port’un (`80`, `443`, `8080`) birleşimidir.
Dolayısıyla şunların hepsi farklı origin’lerdir:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Hepsi `localhost` üzerinde olsa bile, farklı protocol veya port kullandıkları için farklı "origin" sayılırlar.
## Adımlar { #steps }
Diyelim ki tarayıcınızda `http://localhost:8080` adresinde çalışan bir frontend’iniz var ve JavaScript’i, `http://localhost` adresinde çalışan bir backend ile iletişim kurmaya çalışıyor (port belirtmediğimiz için tarayıcı varsayılan port olan `80`’i kullanacaktır).
Bu durumda tarayıcı, `:80`-backend’e bir HTTP `OPTIONS` request’i gönderir. Eğer backend, bu farklı origin’den (`http://localhost:8080`) gelen iletişimi yetkilendiren uygun header’ları gönderirse, `:8080`-tarayıcı frontend’deki JavaScript’in `:80`-backend’e request göndermesine izin verir.
Bunu sağlayabilmek için `:80`-backend’in bir "allowed origins" listesi olmalıdır.
Bu örnekte `:8080`-frontend’in doğru çalışması için listede `http://localhost:8080` bulunmalıdır.
## Wildcard'lar { #wildcards }
Listeyi `"*"` (bir "wildcard") olarak tanımlayıp, hepsine izin verildiğini söylemek de mümkündür.
Ancak bu, credentials içeren her şeyi hariç tutarak yalnızca belirli iletişim türlerine izin verir: Cookie’ler, Bearer Token’larla kullanılanlar gibi Authorization header’ları, vb.
Bu yüzden her şeyin düzgün çalışması için, izin verilen origin’leri açıkça belirtmek daha iyidir.
## `CORSMiddleware` Kullanımı { #use-corsmiddleware }
Bunu **FastAPI** uygulamanızda `CORSMiddleware` ile yapılandırabilirsiniz.
* `CORSMiddleware`’i import edin.
* İzin verilen origin’lerden (string) oluşan bir liste oluşturun.
* Bunu **FastAPI** uygulamanıza bir "middleware" olarak ekleyin.
Ayrıca backend’in şunlara izin verip vermediğini de belirtebilirsiniz:
* Credentials (Authorization header’ları, Cookie’ler, vb).
* Belirli HTTP method’ları (`POST`, `PUT`) veya wildcard `"*"` ile hepsini.
* Belirli HTTP header’ları veya wildcard `"*"` ile hepsini.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
`CORSMiddleware` implementasyonu tarafından kullanılan varsayılan parametreler kısıtlayıcıdır; bu nedenle tarayıcıların Cross-Domain bağlamında kullanmasına izin vermek için belirli origin’leri, method’ları veya header’ları açıkça etkinleştirmeniz gerekir.
Aşağıdaki argümanlar desteklenir:
* `allow_origins` - Cross-origin request yapmasına izin verilmesi gereken origin’lerin listesi. Örn. `['https://example.org', 'https://www.example.org']`. Herhangi bir origin’e izin vermek için `['*']` kullanabilirsiniz.
* `allow_origin_regex` - Cross-origin request yapmasına izin verilmesi gereken origin’lerle eşleşecek bir regex string’i. Örn. `'https://.*\.example\.org'`.
* `allow_methods` - Cross-origin request’lerde izin verilmesi gereken HTTP method’larının listesi. Varsayılanı `['GET']`. Tüm standart method’lara izin vermek için `['*']` kullanabilirsiniz.
* `allow_headers` - Cross-origin request’lerde desteklenmesi gereken HTTP request header’larının listesi. Varsayılanı `[]`. Tüm header’lara izin vermek için `['*']` kullanabilirsiniz. `Accept`, `Accept-Language`, `Content-Language` ve `Content-Type` header’larına [basit CORS request'leri](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) için her zaman izin verilir.
* `allow_credentials` - Cross-origin request’ler için cookie desteği olup olmayacağını belirtir. Varsayılanı `False`.
`allow_credentials` `True` olarak ayarlanmışsa, `allow_origins`, `allow_methods` ve `allow_headers` değerlerinin hiçbiri `['*']` olamaz. Hepsinin [açıkça belirtilmesi](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards) gerekir.
* `expose_headers` - Tarayıcının erişebilmesi gereken response header’larını belirtir. Varsayılanı `[]`.
* `max_age` - Tarayıcıların CORS response’larını cache’lemesi için saniye cinsinden azami süreyi ayarlar. Varsayılanı `600`.
Middleware iki özel HTTP request türüne yanıt verir...
### CORS preflight request'leri { #cors-preflight-requests }
Bunlar, `Origin` ve `Access-Control-Request-Method` header’larına sahip herhangi bir `OPTIONS` request’idir.
Bu durumda middleware gelen request’i intercept eder ve uygun CORS header’larıyla yanıt verir; bilgilendirme amaçlı olarak da `200` veya `400` response döndürür.
### Basit request'ler { #simple-requests }
`Origin` header’ı olan herhangi bir request. Bu durumda middleware request’i normal şekilde geçirir, ancak response’a uygun CORS header’larını ekler.
## Daha Fazla Bilgi { #more-info }
CORS hakkında daha fazla bilgi için [Mozilla CORS dokümantasyonu](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)na bakın.
/// note | Teknik Detaylar
`from starlette.middleware.cors import CORSMiddleware` şeklinde de kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olması için `fastapi.middleware` içinde bazı middleware’ler sağlar. Ancak mevcut middleware’lerin çoğu doğrudan Starlette’ten gelir.
///
================================================
FILE: docs/tr/docs/tutorial/debugging.md
================================================
# Debugging { #debugging }
Visual Studio Code veya PyCharm gibi editörünüzde debugger'ı bağlayabilirsiniz.
## `uvicorn`'ı Çağırma { #call-uvicorn }
FastAPI uygulamanızda `uvicorn`'ı import edip doğrudan çalıştırın:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### `__name__ == "__main__"` Hakkında { #about-name-main }
`__name__ == "__main__"` ifadesinin temel amacı, dosyanız şu şekilde çağrıldığında çalışacak:
```console
$ python myapp.py
```
ancak başka bir dosya onu import ettiğinde çalışmayacak bir kod bölümüne sahip olmaktır, örneğin:
```Python
from myapp import app
```
#### Daha fazla detay { #more-details }
Dosyanızın adının `myapp.py` olduğunu varsayalım.
Şu şekilde çalıştırırsanız:
```console
$ python myapp.py
```
Python tarafından otomatik oluşturulan, dosyanızın içindeki `__name__` adlı dahili değişkenin değeri `"__main__"` string'i olur.
Dolayısıyla şu bölüm:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
çalışır.
---
Ancak modülü (dosyayı) import ederseniz bu gerçekleşmez.
Yani örneğin `importer.py` adında başka bir dosyanız var ve içinde şunlar bulunuyorsa:
```Python
from myapp import app
# Some more code
```
bu durumda `myapp.py` içindeki otomatik oluşturulan `__name__` değişkeni `"__main__"` değerine sahip olmaz.
Bu yüzden şu satır:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
çalıştırılmaz.
/// info | Bilgi
Daha fazla bilgi için [resmi Python dokümantasyonuna](https://docs.python.org/3/library/__main__.html) bakın.
///
## Kodunuzu Debugger ile Çalıştırma { #run-your-code-with-your-debugger }
Uvicorn server'ını doğrudan kodunuzdan çalıştırdığınız için, Python programınızı (FastAPI uygulamanızı) debugger'dan doğrudan başlatabilirsiniz.
---
Örneğin Visual Studio Code'da şunları yapabilirsiniz:
* "Debug" paneline gidin.
* "Add configuration..." seçin.
* "Python" seçin
* "`Python: Current File (Integrated Terminal)`" seçeneğiyle debugger'ı çalıştırın.
Böylece server, **FastAPI** kodunuzla başlar; breakpoint'lerinizde durur vb.
Aşağıdaki gibi görünebilir:
---
PyCharm kullanıyorsanız şunları yapabilirsiniz:
* "Run" menüsünü açın.
* "Debug..." seçeneğini seçin.
* Bir context menü açılır.
* Debug edilecek dosyayı seçin (bu örnekte `main.py`).
Böylece server, **FastAPI** kodunuzla başlar; breakpoint'lerinizde durur vb.
Aşağıdaki gibi görünebilir:
================================================
FILE: docs/tr/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Dependency Olarak Class'lar { #classes-as-dependencies }
**Dependency Injection** sistemine daha derinlemesine geçmeden önce, bir önceki örneği geliştirelim.
## Önceki Örnekten Bir `dict` { #a-dict-from-the-previous-example }
Önceki örnekte, dependency'mizden ("dependable") bir `dict` döndürüyorduk:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Ama sonra *path operation function* içindeki `commons` parametresinde bir `dict` alıyoruz.
Ve biliyoruz ki editor'ler `dict`'ler için çok fazla destek (ör. completion) veremez; çünkü key'lerini ve value type'larını bilemezler.
Daha iyisini yapabiliriz...
## Bir Şeyi Dependency Yapan Nedir { #what-makes-a-dependency }
Şimdiye kadar dependency'leri function olarak tanımlanmış şekilde gördünüz.
Ancak dependency tanımlamanın tek yolu bu değil (muhtemelen en yaygını bu olsa da).
Buradaki kritik nokta, bir dependency'nin "callable" olması gerektiğidir.
Python'da "**callable**", Python'ın bir function gibi "çağırabildiği" her şeydir.
Yani elinizde `something` adlı bir nesne varsa (function _olmak zorunda değil_) ve onu şöyle "çağırabiliyorsanız" (çalıştırabiliyorsanız):
```Python
something()
```
veya
```Python
something(some_argument, some_keyword_argument="foo")
```
o zaman bu bir "callable" demektir.
## Dependency Olarak Class'lar { #classes-as-dependencies_1 }
Python'da bir class'tan instance oluştururken de aynı söz dizimini kullandığınızı fark etmiş olabilirsiniz.
Örneğin:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
Bu durumda `fluffy`, `Cat` class'ının bir instance'ıdır.
Ve `fluffy` oluşturmak için `Cat`'i "çağırmış" olursunuz.
Dolayısıyla bir Python class'ı da bir **callable**'dır.
O zaman **FastAPI** içinde bir Python class'ını dependency olarak kullanabilirsiniz.
FastAPI'nin aslında kontrol ettiği şey, bunun bir "callable" olması (function, class ya da başka bir şey) ve tanımlı parametreleridir.
Eğer **FastAPI**'de bir dependency olarak bir "callable" verirseniz, FastAPI o "callable" için parametreleri analiz eder ve bunları *path operation function* parametreleriyle aynı şekilde işler. Sub-dependency'ler dahil.
Bu, hiç parametresi olmayan callable'lar için de geçerlidir. Tıpkı hiç parametresi olmayan *path operation function*'larda olduğu gibi.
O zaman yukarıdaki `common_parameters` adlı "dependable" dependency'sini `CommonQueryParams` class'ına çevirebiliriz:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Class instance'ını oluşturmak için kullanılan `__init__` metoduna dikkat edin:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...bizim önceki `common_parameters` ile aynı parametrelere sahip:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Bu parametreler, dependency'yi "çözmek" için **FastAPI**'nin kullanacağı şeylerdir.
Her iki durumda da şunlar olacak:
* `str` olan opsiyonel bir `q` query parametresi.
* Default değeri `0` olan `int` tipinde bir `skip` query parametresi.
* Default değeri `100` olan `int` tipinde bir `limit` query parametresi.
Her iki durumda da veriler dönüştürülecek, doğrulanacak, OpenAPI şemasında dokümante edilecek, vb.
## Kullanalım { #use-it }
Artık bu class'ı kullanarak dependency'nizi tanımlayabilirsiniz.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI**, `CommonQueryParams` class'ını çağırır. Bu, o class'ın bir "instance"ını oluşturur ve bu instance, sizin function'ınıza `commons` parametresi olarak geçirilir.
## Type Annotation vs `Depends` { #type-annotation-vs-depends }
Yukarıdaki kodda `CommonQueryParams`'ı iki kez yazdığımıza dikkat edin:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Şuradaki son `CommonQueryParams`:
```Python
... Depends(CommonQueryParams)
```
...FastAPI'nin dependency'nin ne olduğunu anlamak için gerçekten kullandığı şeydir.
FastAPI tanımlanan parametreleri buradan çıkarır ve aslında çağıracağı şey de budur.
---
Bu durumda, şuradaki ilk `CommonQueryParams`:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons: CommonQueryParams ...
```
////
...**FastAPI** için özel bir anlam taşımaz. FastAPI bunu veri dönüştürme, doğrulama vb. için kullanmaz (çünkü bunlar için `Depends(CommonQueryParams)` kullanıyor).
Hatta şunu bile yazabilirsiniz:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons = Depends(CommonQueryParams)
```
////
...şu örnekte olduğu gibi:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Ancak type'ı belirtmeniz önerilir; böylece editor'ünüz `commons` parametresine ne geçirileceğini bilir ve size code completion, type check'leri vb. konularda yardımcı olur:
## Kısayol { #shortcut }
Ama burada bir miktar kod tekrarımız olduğunu görüyorsunuz; `CommonQueryParams`'ı iki kez yazıyoruz:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI**, bu durumlar için bir kısayol sağlar: dependency'nin *özellikle* FastAPI'nin bir instance oluşturmak için "çağıracağı" bir class olduğu durumlar.
Bu özel durumlarda şunu yapabilirsiniz:
Şunu yazmak yerine:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
...şunu yazarsınız:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ Annotated Olmadan
/// tip | İpucu
Mümkünse `Annotated` sürümünü kullanmayı tercih edin.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Dependency'yi parametrenin type'ı olarak tanımlarsınız ve `Depends(CommonQueryParams)` içinde class'ı *yeniden* yazmak yerine, parametre vermeden `Depends()` kullanırsınız.
Aynı örnek şu hale gelir:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...ve **FastAPI** ne yapması gerektiğini bilir.
/// tip | İpucu
Bu size faydalı olmaktan çok kafa karıştırıcı geliyorsa, kullanmayın; buna *ihtiyacınız* yok.
Bu sadece bir kısayoldur. Çünkü **FastAPI** kod tekrarını en aza indirmenize yardımcı olmayı önemser.
///
================================================
FILE: docs/tr/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Path Operation Decorator'lerinde Dependency'ler { #dependencies-in-path-operation-decorators }
Bazı durumlarda bir dependency'nin döndürdüğü değere *path operation function* içinde gerçekten ihtiyacınız olmaz.
Ya da dependency zaten bir değer döndürmüyordur.
Ancak yine de çalıştırılmasını/çözülmesini istersiniz.
Bu gibi durumlarda, `Depends` ile bir *path operation function* parametresi tanımlamak yerine, *path operation decorator*'üne `dependencies` adında bir `list` ekleyebilirsiniz.
## *Path Operation Decorator*'üne `dependencies` Ekleyin { #add-dependencies-to-the-path-operation-decorator }
*Path operation decorator*, opsiyonel bir `dependencies` argümanı alır.
Bu, `Depends()` öğelerinden oluşan bir `list` olmalıdır:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Bu dependency'ler normal dependency'lerle aynı şekilde çalıştırılır/çözülür. Ancak (eğer bir değer döndürüyorlarsa) bu değer *path operation function*'ınıza aktarılmaz.
/// tip | İpucu
Bazı editörler, kullanılmayan function parametrelerini kontrol eder ve bunları hata olarak gösterebilir.
Bu `dependencies` yaklaşımıyla, editör/araç hatalarına takılmadan dependency'lerin çalıştırılmasını sağlayabilirsiniz.
Ayrıca kodunuzda kullanılmayan bir parametreyi gören yeni geliştiricilerin bunun gereksiz olduğunu düşünmesi gibi bir kafa karışıklığını da azaltabilir.
///
/// info | Bilgi
Bu örnekte uydurma özel header'lar olan `X-Key` ve `X-Token` kullanıyoruz.
Ancak gerçek senaryolarda, security uygularken, entegre [Security yardımcı araçlarını (bir sonraki bölüm)](../security/index.md) kullanmak size daha fazla fayda sağlar.
///
## Dependency Hataları ve Return Değerleri { #dependencies-errors-and-return-values }
Normalde kullandığınız aynı dependency *function*'larını burada da kullanabilirsiniz.
### Dependency Gereksinimleri { #dependency-requirements }
Request gereksinimleri (header'lar gibi) veya başka alt dependency'ler tanımlayabilirler:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Exception Fırlatmak { #raise-exceptions }
Bu dependency'ler, normal dependency'lerde olduğu gibi `raise` ile exception fırlatabilir:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Return Değerleri { #return-values }
Ayrıca değer döndürebilirler ya da döndürmeyebilirler; dönen değer kullanılmayacaktır.
Yani başka bir yerde zaten kullandığınız, değer döndüren normal bir dependency'yi tekrar kullanabilirsiniz; değer kullanılmasa bile dependency çalıştırılacaktır:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Bir *Path Operation* Grubu İçin Dependency'ler { #dependencies-for-a-group-of-path-operations }
Daha sonra, muhtemelen birden fazla dosya kullanarak daha büyük uygulamaları nasıl yapılandıracağınızı okurken ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md)), bir *path operation* grubu için tek bir `dependencies` parametresini nasıl tanımlayacağınızı öğreneceksiniz.
## Global Dependency'ler { #global-dependencies }
Sırada, dependency'leri tüm `FastAPI` uygulamasına nasıl ekleyeceğimizi göreceğiz; böylece her *path operation* için geçerli olacaklar.
================================================
FILE: docs/tr/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# `yield` ile Dependency'ler { #dependencies-with-yield }
FastAPI, işini bitirdikten sonra ek adımlar çalıştıran dependency'leri destekler.
Bunu yapmak için `return` yerine `yield` kullanın ve ek adımları (kodu) `yield` satırından sonra yazın.
/// tip | İpucu
Her dependency için yalnızca bir kez `yield` kullandığınızdan emin olun.
///
/// note | Teknik Detaylar
Şunlarla kullanılabilen herhangi bir fonksiyon:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) veya
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
bir **FastAPI** dependency'si olarak kullanılabilir.
Hatta FastAPI bu iki decorator'ı içeride (internally) kullanır.
///
## `yield` ile Bir Veritabanı Dependency'si { #a-database-dependency-with-yield }
Örneğin bunu, bir veritabanı session'ı oluşturmak ve iş bittikten sonra kapatmak için kullanabilirsiniz.
Response oluşturulmadan önce yalnızca `yield` satırına kadar olan (ve `yield` dahil) kod çalıştırılır:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
`yield` edilen değer, *path operation*'lara ve diğer dependency'lere enjekte edilen (injected) değerdir:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Response'dan sonra `yield` satırını takip eden kod çalıştırılır:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | İpucu
`async` ya da normal fonksiyonlar kullanabilirsiniz.
**FastAPI**, normal dependency'lerde olduğu gibi her ikisinde de doğru şekilde davranır.
///
## `yield` ve `try` ile Bir Dependency { #a-dependency-with-yield-and-try }
`yield` kullanan bir dependency içinde bir `try` bloğu kullanırsanız, dependency kullanılırken fırlatılan (raised) herhangi bir exception'ı alırsınız.
Örneğin, başka bir dependency'de veya bir *path operation* içinde çalışan bir kod, bir veritabanı transaction'ını "rollback" yaptıysa ya da başka bir exception oluşturduysa, o exception dependency'nizde size gelir.
Dolayısıyla `except SomeException` ile dependency içinde o spesifik exception'ı yakalayabilirsiniz.
Aynı şekilde, exception olsun ya da olmasın çıkış (exit) adımlarının çalıştırılmasını garanti etmek için `finally` kullanabilirsiniz.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## `yield` ile Alt Dependency'ler { #sub-dependencies-with-yield }
Her boyutta ve şekilde alt dependency'ler ve alt dependency "ağaçları" (trees) oluşturabilirsiniz; bunların herhangi biri veya hepsi `yield` kullanabilir.
**FastAPI**, `yield` kullanan her dependency'deki "exit code"'un doğru sırayla çalıştırılmasını sağlar.
Örneğin, `dependency_c`, `dependency_b`'ye; `dependency_b` de `dependency_a`'ya bağlı olabilir:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
Ve hepsi `yield` kullanabilir.
Bu durumda `dependency_c`, exit code'unu çalıştırabilmek için `dependency_b`'den gelen değerin (burada `dep_b`) hâlâ erişilebilir olmasına ihtiyaç duyar.
Aynı şekilde `dependency_b` de exit code'u için `dependency_a`'dan gelen değerin (burada `dep_a`) erişilebilir olmasına ihtiyaç duyar.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Benzer şekilde, bazı dependency'ler `yield`, bazıları `return` kullanabilir ve bunların bazıları diğerlerine bağlı olabilir.
Ayrıca birden fazla `yield` kullanan dependency gerektiren tek bir dependency'niz de olabilir, vb.
İstediğiniz herhangi bir dependency kombinasyonunu kullanabilirsiniz.
**FastAPI** her şeyin doğru sırada çalışmasını sağlar.
/// note | Teknik Detaylar
Bu, Python'un [Context Managers](https://docs.python.org/3/library/contextlib.html) yapısı sayesinde çalışır.
**FastAPI** bunu sağlamak için içeride onları kullanır.
///
## `yield` ve `HTTPException` ile Dependency'ler { #dependencies-with-yield-and-httpexception }
`yield` kullanan dependency'lerde `try` bloklarıyla bazı kodları çalıştırıp ardından `finally` sonrasında exit code çalıştırabileceğinizi gördünüz.
Ayrıca `except` ile fırlatılan exception'ı yakalayıp onunla bir şey yapabilirsiniz.
Örneğin `HTTPException` gibi farklı bir exception fırlatabilirsiniz.
/// tip | İpucu
Bu biraz ileri seviye bir tekniktir ve çoğu durumda gerçekten ihtiyaç duymazsınız; çünkü exception'ları (`HTTPException` dahil) uygulamanızın geri kalan kodundan, örneğin *path operation function* içinden fırlatabilirsiniz.
Ama ihtiyaç duyarsanız diye burada. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Exception yakalayıp buna göre özel bir response oluşturmak istiyorsanız bir [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers) oluşturun.
## `yield` ve `except` ile Dependency'ler { #dependencies-with-yield-and-except }
`yield` kullanan bir dependency içinde `except` ile bir exception yakalar ve bunu tekrar fırlatmazsanız (ya da yeni bir exception fırlatmazsanız), FastAPI normal Python'da olduğu gibi bir exception olduğunu fark edemez:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
Bu durumda client, biz `HTTPException` veya benzeri bir şey fırlatmadığımız için olması gerektiği gibi *HTTP 500 Internal Server Error* response'u görür; ancak server **hiç log üretmez** ve hatanın ne olduğuna dair başka bir işaret de olmaz. 😱
### `yield` ve `except` Kullanan Dependency'lerde Her Zaman `raise` Edin { #always-raise-in-dependencies-with-yield-and-except }
`yield` kullanan bir dependency içinde bir exception yakalarsanız, başka bir `HTTPException` veya benzeri bir şey fırlatmıyorsanız, **orijinal exception'ı tekrar raise etmelisiniz**.
Aynı exception'ı `raise` ile tekrar fırlatabilirsiniz:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Artık client yine aynı *HTTP 500 Internal Server Error* response'unu alır, ama server log'larda bizim özel `InternalError`'ımızı görür. 😎
## `yield` Kullanan Dependency'lerin Çalıştırılması { #execution-of-dependencies-with-yield }
Çalıştırma sırası kabaca aşağıdaki diyagramdaki gibidir. Zaman yukarıdan aşağı akar. Her sütun, etkileşime giren veya kod çalıştıran parçalardan birini temsil eder.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info | Bilgi
Client'a yalnızca **tek bir response** gönderilir. Bu, error response'lardan biri olabilir ya da *path operation*'dan dönen response olabilir.
Bu response'lardan biri gönderildikten sonra başka bir response gönderilemez.
///
/// tip | İpucu
*Path operation function* içindeki koddan herhangi bir exception raise ederseniz, `HTTPException` dahil olmak üzere bu exception `yield` kullanan dependency'lere aktarılır. Çoğu durumda, doğru şekilde ele alındığından emin olmak için `yield` kullanan dependency'den aynı exception'ı (veya yeni bir tanesini) yeniden raise etmek istersiniz.
///
## Erken Çıkış ve `scope` { #early-exit-and-scope }
Normalde `yield` kullanan dependency'lerin exit code'u, client'a response gönderildikten **sonra** çalıştırılır.
Ama *path operation function*'dan döndükten sonra dependency'yi kullanmayacağınızı biliyorsanız, `Depends(scope="function")` kullanarak FastAPI'ye dependency'yi *path operation function* döndükten sonra kapatmasını, ancak **response gönderilmeden önce** kapatmasını söyleyebilirsiniz.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` şu değerleri alabilen bir `scope` parametresi alır:
* `"function"`: dependency'yi request'i işleyen *path operation function* çalışmadan önce başlat, *path operation function* bittikten sonra bitir, ancak response client'a geri gönderilmeden **önce** sonlandır. Yani dependency fonksiyonu, *path operation **function***'ın **etrafında** çalıştırılır.
* `"request"`: dependency'yi request'i işleyen *path operation function* çalışmadan önce başlat (`"function"` kullanımına benzer), ancak response client'a geri gönderildikten **sonra** sonlandır. Yani dependency fonksiyonu, **request** ve response döngüsünün **etrafında** çalıştırılır.
Belirtilmezse ve dependency `yield` kullanıyorsa, varsayılan olarak `scope` `"request"` olur.
### Alt dependency'ler için `scope` { #scope-for-sub-dependencies }
`scope="request"` (varsayılan) ile bir dependency tanımladığınızda, herhangi bir alt dependency'nin `scope` değeri de `"request"` olmalıdır.
Ancak `scope` değeri `"function"` olan bir dependency, hem `"function"` hem de `"request"` scope'una sahip dependency'lere bağlı olabilir.
Bunun nedeni, bir dependency'nin exit code'unu alt dependency'lerden önce çalıştırabilmesi gerekmesidir; çünkü exit code sırasında hâlâ onları kullanması gerekebilir.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## `yield`, `HTTPException`, `except` ve Background Tasks ile Dependency'ler { #dependencies-with-yield-httpexception-except-and-background-tasks }
`yield` kullanan dependency'ler, zaman içinde farklı kullanım senaryolarını kapsamak ve bazı sorunları düzeltmek için gelişti.
FastAPI'nin farklı sürümlerinde nelerin değiştiğini görmek isterseniz, advanced guide'da şu bölümü okuyabilirsiniz: [Advanced Dependencies - Dependencies with `yield`, `HTTPException`, `except` and Background Tasks](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Context Managers { #context-managers }
### "Context Managers" Nedir? { #what-are-context-managers }
"Context Managers", `with` ifadesiyle kullanabildiğiniz Python nesneleridir.
Örneğin, [bir dosyayı okumak için `with` kullanabilirsiniz](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Temelde `open("./somefile.txt")`, "Context Manager" olarak adlandırılan bir nesne oluşturur.
`with` bloğu bittiğinde, exception olsa bile dosyanın kapatılmasını garanti eder.
`yield` ile bir dependency oluşturduğunuzda, **FastAPI** içeride bunun için bir context manager oluşturur ve bazı ilgili başka araçlarla birleştirir.
### `yield` kullanan dependency'lerde context manager kullanma { #using-context-managers-in-dependencies-with-yield }
/// warning | Uyarı
Bu, az çok "ileri seviye" bir fikirdir.
**FastAPI**'ye yeni başlıyorsanız şimdilik bunu atlamak isteyebilirsiniz.
///
Python'da Context Manager'ları, [iki method'a sahip bir class oluşturarak: `__enter__()` ve `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers) yaratabilirsiniz.
Ayrıca dependency fonksiyonunun içinde `with` veya `async with` ifadeleri kullanarak **FastAPI**'de `yield` kullanan dependency'lerin içinde de kullanabilirsiniz:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | İpucu
Bir context manager oluşturmanın başka bir yolu da şunlardır:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) veya
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
Bunları, tek bir `yield` içeren bir fonksiyonu decorate etmek için kullanabilirsiniz.
FastAPI, `yield` kullanan dependency'ler için içeride bunu yapar.
Ancak FastAPI dependency'leri için bu decorator'ları kullanmak zorunda değilsiniz (hatta kullanmamalısınız).
FastAPI bunu sizin yerinize içeride yapar.
///
================================================
FILE: docs/tr/docs/tutorial/dependencies/global-dependencies.md
================================================
# Global Dependencies { #global-dependencies }
Bazı uygulama türlerinde, tüm uygulama için dependency eklemek isteyebilirsiniz.
[`dependencies`'i *path operation decorator*'larına ekleyebildiğiniz](dependencies-in-path-operation-decorators.md) gibi, `FastAPI` uygulamasına da ekleyebilirsiniz.
Bu durumda, uygulamadaki tüm *path operation*'lara uygulanırlar:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Ve [*path operation decorator*'larına `dependencies` ekleme](dependencies-in-path-operation-decorators.md) bölümündeki tüm fikirler hâlâ geçerlidir; ancak bu sefer, uygulamadaki tüm *path operation*'lar için geçerli olur.
## *Path operations* grupları için Dependencies { #dependencies-for-groups-of-path-operations }
İleride, daha büyük uygulamaları nasıl yapılandıracağınızı ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md)) okurken, muhtemelen birden fazla dosyayla birlikte, bir *path operations* grubu için tek bir `dependencies` parametresini nasıl tanımlayacağınızı öğreneceksiniz.
================================================
FILE: docs/tr/docs/tutorial/dependencies/index.md
================================================
# Bağımlılıklar { #dependencies }
**FastAPI**, çok güçlü ama aynı zamanda sezgisel bir **Bağımlılık Enjeksiyonu** sistemine sahiptir.
Kullanımı çok basit olacak şekilde tasarlanmıştır ve herhangi bir geliştiricinin diğer bileşenleri **FastAPI** ile entegre etmesini kolaylaştırır.
## "Dependency Injection" Nedir? { #what-is-dependency-injection }
Programlamada **"Dependency Injection"**, kodunuzun (bu örnekte *path operation function*'larınızın) çalışmak ve kullanmak için ihtiyaç duyduğu şeyleri: "dependencies" (bağımlılıklar) olarak beyan edebilmesi anlamına gelir.
Ardından bu sistem (bu örnekte **FastAPI**), kodunuza gerekli bağımlılıkları sağlamak ("inject" etmek) için gereken her şeyi sizin yerinize halleder.
Bu yaklaşım, şunlara ihtiyaç duyduğunuzda özellikle faydalıdır:
* Paylaşılan bir mantığa sahip olmak (aynı kod mantığını tekrar tekrar kullanmak).
* Veritabanı bağlantılarını paylaşmak.
* Güvenlik, authentication, rol gereksinimleri vb. kuralları zorunlu kılmak.
* Ve daha birçok şey...
Tüm bunları, kod tekrarını minimumda tutarak yaparsınız.
## İlk Adımlar { #first-steps }
Çok basit bir örneğe bakalım. Şimdilik o kadar basit olacak ki pek işe yaramayacak.
Ama bu sayede **Dependency Injection** sisteminin nasıl çalıştığına odaklanabiliriz.
### Bir dependency (bağımlılık) veya "dependable" Oluşturun { #create-a-dependency-or-dependable }
Önce dependency'e odaklanalım.
Bu, bir *path operation function*'ın alabileceği parametrelerin aynısını alabilen basit bir fonksiyondur:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Bu kadar.
**2 satır**.
Ve tüm *path operation function*'larınızla aynı şekle ve yapıya sahiptir.
Bunu, "decorator" olmadan (yani `@app.get("/some-path")` olmadan) yazılmış bir *path operation function* gibi düşünebilirsiniz.
Ayrıca istediğiniz herhangi bir şeyi döndürebilir.
Bu örnekte, bu dependency şunları bekler:
* `str` olan, opsiyonel bir query parametresi `q`.
* `int` olan, opsiyonel bir query parametresi `skip` ve varsayılanı `0`.
* `int` olan, opsiyonel bir query parametresi `limit` ve varsayılanı `100`.
Sonra da bu değerleri içeren bir `dict` döndürür.
/// info | Bilgi
FastAPI, `Annotated` desteğini 0.95.0 sürümünde ekledi (ve önermeye başladı).
Daha eski bir sürüm kullanıyorsanız `Annotated` kullanmaya çalıştığınızda hata alırsınız.
`Annotated` kullanmadan önce **FastAPI** sürümünü en az 0.95.1'e yükseltmek için [FastAPI sürümünü yükseltin](../../deployment/versions.md#upgrading-the-fastapi-versions).
///
### `Depends`'i Import Edin { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### "Dependant" İçinde Dependency'yi Tanımlayın { #declare-the-dependency-in-the-dependant }
*Path operation function* parametrelerinizde `Body`, `Query` vb. kullandığınız gibi, yeni bir parametreyle `Depends` kullanın:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Fonksiyon parametrelerinde `Depends`'i `Body`, `Query` vb. ile aynı şekilde kullansanız da `Depends` biraz farklı çalışır.
`Depends`'e yalnızca tek bir parametre verirsiniz.
Bu parametre, bir fonksiyon gibi bir şey olmalıdır.
Onu doğrudan **çağırmazsınız** (sonuna parantez eklemezsiniz), sadece `Depends()`'e parametre olarak verirsiniz.
Ve bu fonksiyon da, *path operation function*'lar gibi parametre alır.
/// tip | İpucu
Fonksiyonların dışında başka hangi "şeylerin" dependency olarak kullanılabildiğini bir sonraki bölümde göreceksiniz.
///
Yeni bir request geldiğinde, **FastAPI** şunları sizin yerinize yapar:
* Dependency ("dependable") fonksiyonunuzu doğru parametrelerle çağırır.
* Fonksiyonunuzun sonucunu alır.
* Bu sonucu *path operation function*'ınızdaki parametreye atar.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Bu şekilde paylaşılan kodu bir kez yazarsınız ve onu *path operation*'larda çağırma işini **FastAPI** halleder.
/// check | Ek bilgi
Dikkat edin: Bunu "register" etmek ya da benzeri bir şey yapmak için özel bir class oluşturup **FastAPI**'ye bir yere geçirmeniz gerekmez.
Sadece `Depends`'e verirsiniz ve gerisini **FastAPI** nasıl yapacağını bilir.
///
## `Annotated` Dependency'lerini Paylaşın { #share-annotated-dependencies }
Yukarıdaki örneklerde, ufak bir **kod tekrarı** olduğunu görüyorsunuz.
`common_parameters()` dependency'sini kullanmanız gerektiğinde, type annotation ve `Depends()` içeren parametrenin tamamını yazmanız gerekir:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Ancak `Annotated` kullandığımız için bu `Annotated` değerini bir değişkende saklayıp birden fazla yerde kullanabiliriz:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | İpucu
Bu aslında standart Python'dır; buna "type alias" denir ve **FastAPI**'ye özel bir şey değildir.
Ama **FastAPI**, `Annotated` dahil Python standartları üzerine kurulu olduğu için bu tekniği kodunuzda kullanabilirsiniz. 😎
///
Dependency'ler beklediğiniz gibi çalışmaya devam eder ve **en güzel kısmı** da şudur: **type bilgisi korunur**. Bu da editörünüzün size **autocompletion**, **inline errors** vb. sağlamaya devam edeceği anlamına gelir. `mypy` gibi diğer araçlar için de aynısı geçerlidir.
Bu özellikle, **büyük bir kod tabanında**, aynı dependency'leri **birçok *path operation*** içinde tekrar tekrar kullandığınızda çok faydalı olacaktır.
## `async` Olsa da Olmasa da { #to-async-or-not-to-async }
Dependency'ler de **FastAPI** tarafından çağrılacağı için (tıpkı *path operation function*'larınız gibi), fonksiyonları tanımlarken aynı kurallar geçerlidir.
`async def` ya da normal `def` kullanabilirsiniz.
Ayrıca normal `def` *path operation function*'ları içinde `async def` dependency tanımlayabilir veya `async def` *path operation function*'ları içinde `def` dependency kullanabilirsiniz vb.
Fark etmez. **FastAPI** ne yapacağını bilir.
/// note | Not
Eğer bilmiyorsanız, dokümanlarda `async` ve `await` için [Async: *"In a hurry?"*](../../async.md#in-a-hurry) bölümüne bakın.
///
## OpenAPI ile Entegre { #integrated-with-openapi }
Dependency'lerinizin (ve alt dependency'lerin) tüm request tanımları, doğrulamaları ve gereksinimleri aynı OpenAPI şemasına entegre edilir.
Bu nedenle interaktif dokümanlar, bu dependency'lerden gelen tüm bilgileri de içerir:
## Basit Kullanım { #simple-usage }
Şöyle düşünürseniz: *Path operation function*'lar, bir *path* ve *operation* eşleştiğinde kullanılacak şekilde tanımlanır; ardından **FastAPI** fonksiyonu doğru parametrelerle çağırır ve request'ten veriyi çıkarır.
Aslında tüm (veya çoğu) web framework'ü de aynı şekilde çalışır.
Bu fonksiyonları hiçbir zaman doğrudan çağırmazsınız. Onları framework'ünüz (bu örnekte **FastAPI**) çağırır.
Dependency Injection sistemiyle, *path operation function*'ınızın, ondan önce çalıştırılması gereken başka bir şeye de "bağlı" olduğunu **FastAPI**'ye söyleyebilirsiniz; **FastAPI** bunu çalıştırır ve sonuçları "inject" eder.
Aynı "dependency injection" fikri için kullanılan diğer yaygın terimler:
* resources
* providers
* services
* injectables
* components
## **FastAPI** Plug-in'leri { #fastapi-plug-ins }
Entegrasyonlar ve "plug-in"ler **Dependency Injection** sistemi kullanılarak inşa edilebilir. Ancak aslında **"plug-in" oluşturmanıza gerek yoktur**; çünkü dependency'leri kullanarak *path operation function*'larınıza sunulabilecek sınırsız sayıda entegrasyon ve etkileşim tanımlayabilirsiniz.
Dependency'ler, çok basit ve sezgisel bir şekilde oluşturulabilir. Böylece ihtiyacınız olan Python package'larını import edip, API fonksiyonlarınızla birkaç satır kodla *kelimenin tam anlamıyla* entegre edebilirsiniz.
İlerleyen bölümlerde ilişkisel ve NoSQL veritabanları, güvenlik vb. konularda bunun örneklerini göreceksiniz.
## **FastAPI** Uyumluluğu { #fastapi-compatibility }
Dependency injection sisteminin sadeliği, **FastAPI**'yi şunlarla uyumlu hale getirir:
* tüm ilişkisel veritabanları
* NoSQL veritabanları
* harici paketler
* harici API'ler
* authentication ve authorization sistemleri
* API kullanım izleme (monitoring) sistemleri
* response verisi injection sistemleri
* vb.
## Basit ve Güçlü { #simple-and-powerful }
Hiyerarşik dependency injection sistemi tanımlamak ve kullanmak çok basit olsa da, hâlâ oldukça güçlüdür.
Kendileri de dependency tanımlayabilen dependency'ler tanımlayabilirsiniz.
Sonuçta hiyerarşik bir dependency ağacı oluşur ve **Dependency Injection** sistemi tüm bu dependency'leri (ve alt dependency'lerini) sizin için çözer ve her adımda sonuçları sağlar ("inject" eder).
Örneğin, 4 API endpoint'iniz (*path operation*) olduğunu varsayalım:
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
O zaman her biri için farklı izin gereksinimlerini yalnızca dependency'ler ve alt dependency'lerle ekleyebilirsiniz:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## **OpenAPI** ile Entegre { #integrated-with-openapi_1 }
Bu dependency'lerin tümü, gereksinimlerini beyan ederken aynı zamanda *path operation*'larınıza parametreler, doğrulamalar vb. da ekler.
**FastAPI**, bunların hepsini OpenAPI şemasına eklemekle ilgilenir; böylece interaktif dokümantasyon sistemlerinde gösterilir.
================================================
FILE: docs/tr/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Alt Bağımlılıklar { #sub-dependencies }
**Alt bağımlılıkları** olan bağımlılıklar oluşturabilirsiniz.
İhtiyacınız olduğu kadar **derine** gidebilirler.
Bunları çözme işini **FastAPI** üstlenir.
## İlk bağımlılık "dependable" { #first-dependency-dependable }
Şöyle bir ilk bağımlılık ("dependable") oluşturabilirsiniz:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Burada `q` adında opsiyonel bir query parametresi `str` olarak tanımlanır ve sonra doğrudan geri döndürülür.
Bu oldukça basit (pek faydalı değil), ama alt bağımlılıkların nasıl çalıştığına odaklanmamıza yardımcı olacak.
## İkinci bağımlılık: "dependable" ve "dependant" { #second-dependency-dependable-and-dependant }
Ardından, aynı zamanda kendi içinde bir bağımlılık da tanımlayan başka bir bağımlılık fonksiyonu (bir "dependable") oluşturabilirsiniz (yani o da bir "dependant" olur):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Tanımlanan parametrelere odaklanalım:
* Bu fonksiyonun kendisi bir bağımlılık ("dependable") olmasına rağmen, ayrıca başka bir bağımlılık daha tanımlar (başka bir şeye "depends" olur).
* `query_extractor`'a bağlıdır ve ondan dönen değeri `q` parametresine atar.
* Ayrıca `last_query` adında opsiyonel bir cookie'yi `str` olarak tanımlar.
* Kullanıcı herhangi bir query `q` sağlamadıysa, daha önce cookie'ye kaydettiğimiz en son kullanılan query'yi kullanırız.
## Bağımlılığı Kullanma { #use-the-dependency }
Sonra bu bağımlılığı şöyle kullanabiliriz:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Bilgi
Dikkat edin, *path operation function* içinde yalnızca tek bir bağımlılık tanımlıyoruz: `query_or_cookie_extractor`.
Ancak **FastAPI**, `query_or_cookie_extractor`'ı çağırmadan önce `query_extractor`'ı önce çözmesi gerektiğini bilir ve onun sonucunu `query_or_cookie_extractor`'a aktarır.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Aynı Bağımlılığı Birden Fazla Kez Kullanma { #using-the-same-dependency-multiple-times }
Bağımlılıklarınızdan biri aynı *path operation* için birden fazla kez tanımlanırsa (örneğin birden fazla bağımlılığın ortak bir alt bağımlılığı varsa), **FastAPI** o alt bağımlılığı request başına yalnızca bir kez çağıracağını bilir.
Dönen değeri bir "önbellek" içinde saklar ve aynı request içinde buna ihtiyaç duyan tüm "dependant"lara aktarır; böylece aynı request için bağımlılığı tekrar tekrar çağırmaz.
Daha ileri bir senaryoda, "cached" değeri kullanmak yerine aynı request içinde her adımda (muhtemelen birden fazla kez) bağımlılığın çağrılması gerektiğini biliyorsanız, `Depends` kullanırken `use_cache=False` parametresini ayarlayabilirsiniz:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ Annotated olmayan
/// tip | İpucu
Mümkünse `Annotated` sürümünü tercih edin.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Özet { #recap }
Burada kullanılan havalı terimlerin ötesinde, **Dependency Injection** sistemi aslında oldukça basittir.
*Path operation function*'lara benzeyen fonksiyonlardan ibarettir.
Yine de çok güçlüdür ve keyfi derecede derin iç içe geçmiş bağımlılık "graph"ları (ağaçları) tanımlamanıza izin verir.
/// tip | İpucu
Bu basit örneklerle çok faydalı görünmeyebilir.
Ancak **security** ile ilgili bölümlerde bunun ne kadar işe yaradığını göreceksiniz.
Ayrıca size ne kadar kod kazandırdığını da göreceksiniz.
///
================================================
FILE: docs/tr/docs/tutorial/encoder.md
================================================
# JSON Uyumlu Encoder { #json-compatible-encoder }
Bazı durumlarda, bir veri tipini (örneğin bir Pydantic model) JSON ile uyumlu bir şeye (örneğin `dict`, `list` vb.) dönüştürmeniz gerekebilir.
Örneğin, bunu bir veritabanında saklamanız gerekiyorsa.
Bunun için **FastAPI**, `jsonable_encoder()` fonksiyonunu sağlar.
## `jsonable_encoder` Kullanımı { #using-the-jsonable-encoder }
Yalnızca JSON ile uyumlu veri kabul eden bir veritabanınız olduğunu düşünelim: `fake_db`.
Örneğin bu veritabanı, JSON ile uyumlu olmadıkları için `datetime` objelerini kabul etmez.
Dolayısıyla bir `datetime` objesinin, [ISO formatında](https://en.wikipedia.org/wiki/ISO_8601) veriyi içeren bir `str`'e dönüştürülmesi gerekir.
Aynı şekilde bu veritabanı bir Pydantic model'i (attribute'lara sahip bir obje) de kabul etmez; yalnızca bir `dict` kabul eder.
Bunun için `jsonable_encoder` kullanabilirsiniz.
Bir Pydantic model gibi bir obje alır ve JSON ile uyumlu bir versiyonunu döndürür:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
Bu örnekte, Pydantic model'i bir `dict`'e, `datetime`'ı da bir `str`'e dönüştürür.
Bu fonksiyonun çağrılmasıyla elde edilen sonuç, Python standardındaki [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps) ile encode edilebilecek bir şeydir.
JSON formatında (string olarak) veriyi içeren büyük bir `str` döndürmez. Bunun yerine, tüm değerleri ve alt değerleri JSON ile uyumlu olacak şekilde, Python’un standart bir veri yapısını (örneğin bir `dict`) döndürür.
/// note | Not
`jsonable_encoder`, aslında **FastAPI** tarafından veriyi dönüştürmek için internal olarak kullanılır. Ancak birçok farklı senaryoda da oldukça faydalıdır.
///
================================================
FILE: docs/tr/docs/tutorial/extra-data-types.md
================================================
# Ek Veri Tipleri { #extra-data-types }
Şimdiye kadar şunlar gibi yaygın veri tiplerini kullanıyordunuz:
* `int`
* `float`
* `str`
* `bool`
Ancak daha karmaşık veri tiplerini de kullanabilirsiniz.
Ve yine, şimdiye kadar gördüğünüz özelliklerin aynısına sahip olursunuz:
* Harika editör desteği.
* Gelen request'lerden veri dönüştürme.
* response verileri için veri dönüştürme.
* Veri doğrulama.
* Otomatik annotation ve dokümantasyon.
## Diğer veri tipleri { #other-data-types }
Kullanabileceğiniz ek veri tiplerinden bazıları şunlardır:
* `UUID`:
* Birçok veritabanı ve sistemde ID olarak yaygın kullanılan, standart bir "Universally Unique Identifier".
* request'lerde ve response'larda `str` olarak temsil edilir.
* `datetime.datetime`:
* Python `datetime.datetime`.
* request'lerde ve response'larda ISO 8601 formatında bir `str` olarak temsil edilir, örn: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Python `datetime.date`.
* request'lerde ve response'larda ISO 8601 formatında bir `str` olarak temsil edilir, örn: `2008-09-15`.
* `datetime.time`:
* Python `datetime.time`.
* request'lerde ve response'larda ISO 8601 formatında bir `str` olarak temsil edilir, örn: `14:23:55.003`.
* `datetime.timedelta`:
* Python `datetime.timedelta`.
* request'lerde ve response'larda toplam saniye sayısını ifade eden bir `float` olarak temsil edilir.
* Pydantic, bunu ayrıca bir "ISO 8601 time diff encoding" olarak temsil etmeye de izin verir, [daha fazla bilgi için dokümanlara bakın](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* request'lerde ve response'larda, `set` ile aynı şekilde ele alınır:
* request'lerde bir list okunur, tekrarlar kaldırılır ve `set`'e dönüştürülür.
* response'larda `set`, `list`'e dönüştürülür.
* Üretilen schema, `set` değerlerinin benzersiz olduğunu belirtir (JSON Schema'nın `uniqueItems` özelliğini kullanarak).
* `bytes`:
* Standart Python `bytes`.
* request'lerde ve response'larda `str` gibi ele alınır.
* Üretilen schema, bunun `binary` "format"ına sahip bir `str` olduğunu belirtir.
* `Decimal`:
* Standart Python `Decimal`.
* request'lerde ve response'larda `float` ile aynı şekilde işlenir.
* Geçerli tüm Pydantic veri tiplerini burada görebilirsiniz: [Pydantic veri tipleri](https://docs.pydantic.dev/latest/usage/types/types/).
## Örnek { #example }
Yukarıdaki tiplerden bazılarını kullanan parametrelere sahip bir örnek *path operation* şöyle:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Fonksiyonun içindeki parametrelerin doğal veri tiplerinde olduğunu unutmayın; örneğin normal tarih işlemleri yapabilirsiniz:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/tr/docs/tutorial/extra-models.md
================================================
# Ek Modeller { #extra-models }
Önceki örnekten devam edersek, birbiriyle ilişkili birden fazla modelin olması oldukça yaygındır.
Bu durum özellikle kullanıcı modellerinde sık görülür, çünkü:
* **input modeli** bir `password` içerebilmelidir.
* **output modeli** `password` içermemelidir.
* **database modeli** büyük ihtimalle hash'lenmiş bir `password` tutmalıdır.
/// danger | Tehlike
Kullanıcının düz metin (plaintext) `password`'ünü asla saklamayın. Her zaman sonradan doğrulayabileceğiniz "güvenli bir hash" saklayın.
Eğer bilmiyorsanız, "password hash" nedir konusunu [güvenlik bölümlerinde](security/simple-oauth2.md#password-hashing) öğreneceksiniz.
///
## Birden Çok Model { #multiple-models }
`password` alanlarıyla birlikte modellerin genel olarak nasıl görünebileceğine ve nerelerde kullanılacaklarına dair bir fikir:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### `**user_in.model_dump()` Hakkında { #about-user-in-model-dump }
#### Pydantic'in `.model_dump()` Metodu { #pydantics-model-dump }
`user_in`, `UserIn` sınıfına ait bir Pydantic modelidir.
Pydantic modellerinde, model verilerini içeren bir `dict` döndüren `.model_dump()` metodu bulunur.
Yani, şöyle bir Pydantic nesnesi `user_in` oluşturursak:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
ve sonra şunu çağırırsak:
```Python
user_dict = user_in.model_dump()
```
artık `user_dict` değişkeninde modelin verilerini içeren bir `dict` vardır (Pydantic model nesnesi yerine bir `dict` elde etmiş oluruz).
Ve eğer şunu çağırırsak:
```Python
print(user_dict)
```
şöyle bir Python `dict` elde ederiz:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Bir `dict`'i Unpack Etmek { #unpacking-a-dict }
`user_dict` gibi bir `dict` alıp bunu bir fonksiyona (ya da sınıfa) `**user_dict` ile gönderirsek, Python bunu "unpack" eder. Yani `user_dict` içindeki key ve value'ları doğrudan key-value argümanları olarak geçirir.
Dolayısıyla, yukarıdaki `user_dict` ile devam edersek, şunu yazmak:
```Python
UserInDB(**user_dict)
```
şuna eşdeğer bir sonuç üretir:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
Ya da daha net şekilde, `user_dict`'i doğrudan kullanarak, gelecekte içeriği ne olursa olsun:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Bir Pydantic Modelinden Diğerinin İçeriğiyle Pydantic Model Oluşturmak { #a-pydantic-model-from-the-contents-of-another }
Yukarıdaki örnekte `user_dict`'i `user_in.model_dump()` ile elde ettiğimiz için, şu kod:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
şuna eşdeğerdir:
```Python
UserInDB(**user_in.model_dump())
```
...çünkü `user_in.model_dump()` bir `dict` döndürür ve biz de bunu `UserInDB`'ye `**` önekiyle vererek Python'ın "unpack" etmesini sağlarız.
Böylece, bir Pydantic modelindeki verilerden başka bir Pydantic model üretmiş oluruz.
#### Bir `dict`'i Unpack Etmek ve Ek Keyword'ler { #unpacking-a-dict-and-extra-keywords }
Sonrasında, aşağıdaki gibi ek keyword argümanı `hashed_password=hashed_password` eklemek:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...şuna benzer bir sonuca dönüşür:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Uyarı
Ek destek fonksiyonları olan `fake_password_hasher` ve `fake_save_user` sadece verinin olası bir akışını göstermek içindir; elbette gerçek bir güvenlik sağlamazlar.
///
## Tekrarı Azaltma { #reduce-duplication }
Kod tekrarını azaltmak, **FastAPI**'nin temel fikirlerinden biridir.
Kod tekrarı; bug, güvenlik problemi, kodun senkron dışına çıkması (bir yeri güncelleyip diğerlerini güncellememek) gibi sorunların olasılığını artırır.
Bu modellerin hepsi verinin büyük bir kısmını paylaşıyor ve attribute adlarını ve type'larını tekrar ediyor.
Daha iyisini yapabiliriz.
Diğer modellerimiz için temel olacak bir `UserBase` modeli tanımlayabiliriz. Sonra da bu modelden türeyen (subclass) modeller oluşturup onun attribute'larını (type deklarasyonları, doğrulama vb.) miras aldırabiliriz.
Tüm veri dönüştürme, doğrulama, dokümantasyon vb. her zamanki gibi çalışmaya devam eder.
Bu sayede modeller arasındaki farkları (plaintext `password` olan, `hashed_password` olan ve `password` olmayan) sadece o farklılıklar olarak tanımlayabiliriz:
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` veya `anyOf` { #union-or-anyof }
Bir response'u iki ya da daha fazla type'ın `Union`'ı olarak tanımlayabilirsiniz; bu, response'un bunlardan herhangi biri olabileceği anlamına gelir.
OpenAPI'de bu `anyOf` ile tanımlanır.
Bunu yapmak için standart Python type hint'i olan [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union)'ı kullanın:
/// note | Not
Bir [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions) tanımlarken en spesifik type'ı önce, daha az spesifik olanı sonra ekleyin. Aşağıdaki örnekte daha spesifik olan `PlaneItem`, `Union[PlaneItem, CarItem]` içinde `CarItem`'dan önce gelir.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### Python 3.10'da `Union` { #union-in-python-3-10 }
Bu örnekte `Union[PlaneItem, CarItem]` değerini `response_model` argümanına veriyoruz.
Bunu bir **type annotation** içine koymak yerine bir **argümana değer** olarak geçtiğimiz için, Python 3.10'da bile `Union` kullanmamız gerekiyor.
Eğer bu bir type annotation içinde olsaydı, dikey çizgiyi kullanabilirdik:
```Python
some_variable: PlaneItem | CarItem
```
Ancak bunu `response_model=PlaneItem | CarItem` atamasına koyarsak hata alırız; çünkü Python bunu bir type annotation olarak yorumlamak yerine `PlaneItem` ile `CarItem` arasında **geçersiz bir işlem** yapmaya çalışır.
## Model Listesi { #list-of-models }
Aynı şekilde, nesne listesi döndüren response'ları da tanımlayabilirsiniz.
Bunun için standart Python `list`'i kullanın:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Rastgele `dict` ile Response { #response-with-arbitrary-dict }
Bir Pydantic modeli kullanmadan, sadece key ve value type'larını belirterek düz, rastgele bir `dict` ile de response tanımlayabilirsiniz.
Bu, geçerli field/attribute adlarını (Pydantic modeli için gerekli olurdu) önceden bilmiyorsanız kullanışlıdır.
Bu durumda `dict` kullanabilirsiniz:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Özet { #recap }
Her duruma göre birden fazla Pydantic modeli kullanın ve gerekirse özgürce inheritance uygulayın.
Bir entity'nin farklı "state"lere sahip olması gerekiyorsa, o entity için tek bir veri modeli kullanmak zorunda değilsiniz. Örneğin `password` içeren, `password_hash` içeren ve `password` içermeyen state'lere sahip kullanıcı "entity"si gibi.
================================================
FILE: docs/tr/docs/tutorial/first-steps.md
================================================
# İlk Adımlar { #first-steps }
En sade FastAPI dosyası şu şekilde görünür:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Yukarıdakini `main.py` adlı bir dosyaya kopyalayın.
Canlı sunucuyu çalıştırın:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Çıktıda, şuna benzer bir satır göreceksiniz:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Bu satır, uygulamanızın yerel makinenizde hangi URL'de sunulduğunu gösterir.
### Kontrol Edelim { #check-it }
Tarayıcınızı açıp [http://127.0.0.1:8000](http://127.0.0.1:8000) adresine gidin.
Şu şekilde bir JSON response göreceksiniz:
```JSON
{"message": "Hello World"}
```
### Etkileşimli API Dokümantasyonu { #interactive-api-docs }
Şimdi [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresine gidin.
Otomatik etkileşimli API dokümantasyonunu ([Swagger UI](https://github.com/swagger-api/swagger-ui) tarafından sağlanan) göreceksiniz:

### Alternatif API Dokümantasyonu { #alternative-api-docs }
Ve şimdi [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) adresine gidin.
Alternatif otomatik dokümantasyonu ([ReDoc](https://github.com/Rebilly/ReDoc) tarafından sağlanan) göreceksiniz:

### OpenAPI { #openapi }
**FastAPI**, API'ları tanımlamak için **OpenAPI** standardını kullanarak tüm API'nızın tamamını içeren bir "şema" üretir.
#### "Şema" { #schema }
"Şema", bir şeyin tanımı veya açıklamasıdır. Onu uygulayan kod değil, sadece soyut bir açıklamadır.
#### API "şeması" { #api-schema }
Bu durumda, [OpenAPI](https://github.com/OAI/OpenAPI-Specification), API'nızın şemasını nasıl tanımlayacağınızı belirleyen bir şartnamedir.
Bu şema tanımı, API path'leriniz, alabilecekleri olası parametreler vb. şeyleri içerir.
#### Veri "şeması" { #data-schema }
"Şema" terimi, JSON içeriği gibi bazı verilerin şeklini de ifade edebilir.
Bu durumda, JSON attribute'ları ve sahip oldukları veri türleri vb. anlamına gelir.
#### OpenAPI ve JSON Schema { #openapi-and-json-schema }
OpenAPI, API'nız için bir API şeması tanımlar. Ve bu şema, JSON veri şemaları standardı olan **JSON Schema** kullanılarak API'nız tarafından gönderilen ve alınan verilerin tanımlarını (veya "şemalarını") içerir.
#### `openapi.json` Dosyasına Göz At { #check-the-openapi-json }
Ham OpenAPI şemasının nasıl göründüğünü merak ediyorsanız, FastAPI otomatik olarak tüm API'nızın açıklamalarını içeren bir JSON (şema) üretir.
Bunu doğrudan şuradan görebilirsiniz: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Şuna benzer bir şekilde başlayan bir JSON gösterecektir:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### OpenAPI Ne İşe Yarar? { #what-is-openapi-for }
OpenAPI şeması, dahil edilen iki etkileşimli dokümantasyon sistemine güç veren şeydir.
Ve OpenAPI tabanlı düzinelerce alternatif vardır. **FastAPI** ile oluşturulmuş uygulamanıza bu alternatiflerden herhangi birini kolayca ekleyebilirsiniz.
Ayrıca, API'nızla iletişim kuran istemciler için otomatik olarak kod üretmekte de kullanabilirsiniz. Örneğin frontend, mobil veya IoT uygulamaları.
### `pyproject.toml` içinde uygulama `entrypoint`'ını yapılandırın { #configure-the-app-entrypoint-in-pyproject-toml }
Uygulamanızın nerede bulunduğunu `pyproject.toml` dosyasında şöyle yapılandırabilirsiniz:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Bu `entrypoint`, `fastapi` komutuna uygulamayı şu şekilde import etmesi gerektiğini söyler:
```python
from main import app
```
Kodunuz şöyle yapılandırılmışsa:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
O zaman `entrypoint`'i şöyle ayarlardınız:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
Bu da şuna eşdeğer olur:
```python
from backend.main import app
```
### Path ile `fastapi dev` { #fastapi-dev-with-path }
Dosya path'ini `fastapi dev` komutuna da verebilirsiniz; hangi FastAPI app objesini kullanacağını tahmin eder:
```console
$ fastapi dev main.py
```
Ancak `fastapi` komutunu her çağırdığınızda doğru path'i geçmeyi hatırlamanız gerekir.
Ayrıca, [VS Code Eklentisi](../editor-support.md) veya [FastAPI Cloud](https://fastapicloud.com) gibi başka araçlar da onu bulamayabilir; bu yüzden `pyproject.toml` içindeki `entrypoint`'i kullanmanız önerilir.
### Uygulamanızı Yayınlayın (opsiyonel) { #deploy-your-app-optional }
İsterseniz FastAPI uygulamanızı [FastAPI Cloud](https://fastapicloud.com)'a deploy edebilirsiniz; henüz katılmadıysanız gidip bekleme listesine yazılın. 🚀
Zaten bir **FastAPI Cloud** hesabınız varsa (bekleme listesinden sizi davet ettiysek 😉), uygulamanızı tek komutla deploy edebilirsiniz.
Deploy etmeden önce giriş yaptığınızdan emin olun:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Ardından uygulamanızı deploy edin:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Bu kadar! Artık uygulamanıza o URL üzerinden erişebilirsiniz. ✨
## Adım Adım Özetleyelim { #recap-step-by-step }
### Adım 1: `FastAPI` import edin { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI`, API'nız için tüm işlevselliği sağlayan bir Python class'ıdır.
/// note | Teknik Detaylar
`FastAPI`, doğrudan `Starlette`'ten miras alan bir class'tır.
[Starlette](https://www.starlette.dev/)'in tüm işlevselliğini `FastAPI` ile de kullanabilirsiniz.
///
### Adım 2: bir `FastAPI` "instance"ı oluşturun { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Burada `app` değişkeni `FastAPI` class'ının bir "instance"ı olacaktır.
Bu, tüm API'nızı oluşturmak için ana etkileşim noktası olacaktır.
### Adım 3: bir *path operation* oluşturun { #step-3-create-a-path-operation }
#### Path { #path }
Buradaki "Path", URL'in ilk `/` işaretinden başlayarak son kısmını ifade eder.
Yani, şu şekilde bir URL'de:
```
https://example.com/items/foo
```
...path şöyle olur:
```
/items/foo
```
/// info | Bilgi
Bir "path" genellikle "endpoint" veya "route" olarak da adlandırılır.
///
Bir API oluştururken, "path", "concerns" ve "resources" ayrımını yapmanın ana yoludur.
#### Operation { #operation }
Burada "Operation", HTTP "method"larından birini ifade eder.
Şunlardan biri:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...ve daha egzotik olanlar:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
HTTP protokolünde, her bir path ile bu "method"lardan biri (veya birden fazlası) ile iletişim kurabilirsiniz.
---
API oluştururken, normalde belirli bir aksiyon için bu spesifik HTTP method'larını kullanırsınız.
Normalde şunları kullanırsınız:
* `POST`: veri oluşturmak için.
* `GET`: veri okumak için.
* `PUT`: veriyi güncellemek için.
* `DELETE`: veriyi silmek için.
Bu nedenle, OpenAPI'da HTTP method'larının her birine "operation" denir.
Biz de bunlara "**operation**" diyeceğiz.
#### Bir *path operation decorator* tanımlayın { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
`@app.get("/")`, **FastAPI**'a hemen altındaki fonksiyonun şuraya giden request'leri ele almakla sorumlu olduğunu söyler:
* path `/`
* get operation kullanarak
/// info | `@decorator` Bilgisi
Python'daki `@something` söz dizimi "decorator" olarak adlandırılır.
Onu bir fonksiyonun üstüne koyarsınız. Güzel, dekoratif bir şapka gibi (sanırım terim de buradan geliyor).
Bir "decorator", altındaki fonksiyonu alır ve onunla bir şey yapar.
Bizim durumumuzda bu decorator, **FastAPI**'a altındaki fonksiyonun **path** `/` ile **operation** `get`'e karşılık geldiğini söyler.
Bu, "**path operation decorator**"dır.
///
Diğer operation'ları da kullanabilirsiniz:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Ve daha egzotik olanları:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip | İpucu
Her bir operation'ı (HTTP method'unu) istediğiniz gibi kullanmakta özgürsünüz.
**FastAPI** herhangi bir özel anlamı zorunlu kılmaz.
Buradaki bilgiler bir gereklilik değil, bir kılavuz olarak sunulmaktadır.
Örneğin GraphQL kullanırken, normalde tüm aksiyonları yalnızca `POST` operation'ları kullanarak gerçekleştirirsiniz.
///
### Adım 4: **path operation function**'ı tanımlayın { #step-4-define-the-path-operation-function }
Bu bizim "**path operation function**"ımız:
* **path**: `/`.
* **operation**: `get`.
* **function**: "decorator"ün altındaki fonksiyondur (`@app.get("/")`'in altındaki).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Bu bir Python fonksiyonudur.
**FastAPI**, "`/`" URL'ine `GET` operation kullanarak bir request aldığında bu fonksiyonu çağıracaktır.
Bu durumda, bu bir `async` fonksiyondur.
---
Bunu `async def` yerine normal bir fonksiyon olarak da tanımlayabilirsiniz:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note | Not
Eğer farkı bilmiyorsanız, [Async: *"Aceleniz mi var?"*](../async.md#in-a-hurry) sayfasına bakın.
///
### Adım 5: içeriği döndürün { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Bir `dict`, `list`, `str`, `int` vb. tekil değerler döndürebilirsiniz.
Ayrıca Pydantic modelleri de döndürebilirsiniz (bununla ilgili daha fazlasını ileride göreceksiniz).
Otomatik olarak JSON'a dönüştürülecek (ORM'ler vb. dahil) başka birçok nesne ve model vardır. En sevdiğiniz nesne/model'leri kullanmayı deneyin; büyük ihtimalle zaten destekleniyordur.
### Adım 6: Deploy edin { #step-6-deploy-it }
Uygulamanızı tek komutla **[FastAPI Cloud](https://fastapicloud.com)**'a deploy edin: `fastapi deploy`. 🎉
#### FastAPI Cloud Hakkında { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)**, **FastAPI**'ın arkasındaki aynı yazar ve ekip tarafından geliştirilmiştir.
Minimum eforla bir API'ı **oluşturma**, **deploy etme** ve **erişme** sürecini sadeleştirir.
FastAPI ile uygulama geliştirirken yaşadığınız aynı **developer experience**'ı, onları buluta **deploy etme** aşamasına da taşır. 🎉
FastAPI Cloud, *FastAPI and friends* açık kaynak projelerinin birincil sponsoru ve finansman sağlayıcısıdır. ✨
#### Diğer cloud sağlayıcılarına deploy edin { #deploy-to-other-cloud-providers }
FastAPI açık kaynaklıdır ve standartlara dayanır. FastAPI uygulamalarını seçtiğiniz herhangi bir cloud sağlayıcısına deploy edebilirsiniz.
FastAPI uygulamalarını onlarla deploy etmek için cloud sağlayıcınızın kılavuzlarını takip edin. 🤓
## Özet { #recap }
* `FastAPI` import edin.
* Bir `app` instance'ı oluşturun.
* `@app.get("/")` gibi decorator'ları kullanarak bir **path operation decorator** yazın.
* Bir **path operation function** tanımlayın; örneğin `def root(): ...`.
* `fastapi dev` komutunu kullanarak geliştirme sunucusunu çalıştırın.
* İsterseniz `fastapi deploy` ile uygulamanızı deploy edin.
================================================
FILE: docs/tr/docs/tutorial/handling-errors.md
================================================
# Hataları Yönetme { #handling-errors }
API’nizi kullanan bir client’a hata bildirmek zorunda olduğunuz pek çok durum vardır.
Bu client; frontend’i olan bir tarayıcı, başka birinin yazdığı bir kod, bir IoT cihazı vb. olabilir.
Client’a şunları söylemeniz gerekebilir:
* Client’ın bu işlem için yeterli yetkisi yok.
* Client’ın bu kaynağa erişimi yok.
* Client’ın erişmeye çalıştığı öğe mevcut değil.
* vb.
Bu durumlarda genellikle **400** aralığında (**400** ile **499** arası) bir **HTTP status code** döndürürsünüz.
Bu, 200 HTTP status code’larına (200 ile 299 arası) benzer. Bu "200" status code’ları, request’in bir şekilde "başarılı" olduğunu ifade eder.
400 aralığındaki status code’lar ise hatanın client tarafından kaynaklandığını gösterir.
Şu meşhur **"404 Not Found"** hatalarını (ve şakalarını) hatırlıyor musunuz?
## `HTTPException` Kullanma { #use-httpexception }
Client’a hata içeren HTTP response’ları döndürmek için `HTTPException` kullanırsınız.
### `HTTPException`’ı Import Etme { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Kodunuzda Bir `HTTPException` Raise Etme { #raise-an-httpexception-in-your-code }
`HTTPException`, API’lerle ilgili ek veriler içeren normal bir Python exception’ıdır.
Python exception’ı olduğu için `return` etmezsiniz, `raise` edersiniz.
Bu aynı zamanda şunu da ifade eder: *path operation function*’ınızın içinde çağırdığınız bir yardımcı (utility) fonksiyonun içindeyken `HTTPException` raise ederseniz, *path operation function* içindeki kodun geri kalanı çalışmaz; request’i hemen sonlandırır ve `HTTPException` içindeki HTTP hatasını client’a gönderir.
Bir değer döndürmek yerine exception raise etmenin faydası, Dependencies ve Security bölümünde daha da netleşecektir.
Bu örnekte, client var olmayan bir ID ile bir item istediğinde, `404` status code’u ile bir exception raise edelim:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Ortaya Çıkan Response { #the-resulting-response }
Client `http://example.com/items/foo` (bir `item_id` `"foo"`) isterse, HTTP status code olarak 200 ve şu JSON response’u alır:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Ancak client `http://example.com/items/bar` (mevcut olmayan bir `item_id` `"bar"`) isterse, HTTP status code olarak 404 ("not found" hatası) ve şu JSON response’u alır:
```JSON
{
"detail": "Item not found"
}
```
/// tip | İpucu
Bir `HTTPException` raise ederken, `detail` parametresine sadece `str` değil, JSON’a dönüştürülebilen herhangi bir değer geçebilirsiniz.
Örneğin `dict`, `list` vb. geçebilirsiniz.
Bunlar **FastAPI** tarafından otomatik olarak işlenir ve JSON’a dönüştürülür.
///
## Özel Header’lar Eklemek { #add-custom-headers }
Bazı durumlarda HTTP hata response’una özel header’lar eklemek faydalıdır. Örneğin bazı güvenlik türlerinde.
Muhtemelen bunu doğrudan kendi kodunuzda kullanmanız gerekmeyecek.
Ama ileri seviye bir senaryo için ihtiyaç duyarsanız, özel header’lar ekleyebilirsiniz:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Özel Exception Handler’ları Kurmak { #install-custom-exception-handlers }
[Starlette’in aynı exception yardımcı araçlarıyla](https://www.starlette.dev/exceptions/) özel exception handler’lar ekleyebilirsiniz.
Diyelim ki sizin (ya da kullandığınız bir kütüphanenin) `raise` edebileceği `UnicornException` adında özel bir exception’ınız var.
Ve bu exception’ı FastAPI ile global olarak handle etmek istiyorsunuz.
`@app.exception_handler()` ile özel bir exception handler ekleyebilirsiniz:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Burada `/unicorns/yolo` için request atarsanız, *path operation* bir `UnicornException` `raise` eder.
Namun bu, `unicorn_exception_handler` tarafından handle edilir.
Böylece HTTP status code’u `418` olan, JSON içeriği şu şekilde temiz bir hata response’u alırsınız:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Teknik Detaylar
`from starlette.requests import Request` ve `from starlette.responses import JSONResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.responses` içeriğini `fastapi.responses` olarak da sunar. Ancak mevcut response’ların çoğu doğrudan Starlette’ten gelir. `Request` için de aynısı geçerlidir.
///
## Varsayılan Exception Handler’ları Override Etmek { #override-the-default-exception-handlers }
**FastAPI** bazı varsayılan exception handler’lara sahiptir.
Bu handler’lar, `HTTPException` `raise` ettiğinizde ve request geçersiz veri içerdiğinde varsayılan JSON response’ları döndürmekten sorumludur.
Bu exception handler’ları kendi handler’larınızla override edebilirsiniz.
### Request Validation Exception’larını Override Etmek { #override-request-validation-exceptions }
Bir request geçersiz veri içerdiğinde, **FastAPI** içeride `RequestValidationError` raise eder.
Ve bunun için varsayılan bir exception handler da içerir.
Override etmek için `RequestValidationError`’ı import edin ve exception handler’ı `@app.exception_handler(RequestValidationError)` ile decorate edin.
Exception handler, bir `Request` ve exception’ı alır.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Artık `/items/foo`’ya giderseniz, şu varsayılan JSON hatası yerine:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
şu şekilde bir metin (text) versiyonu alırsınız:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### `HTTPException` Hata Handler’ını Override Etmek { #override-the-httpexception-error-handler }
Benzer şekilde `HTTPException` handler’ını da override edebilirsiniz.
Örneğin bu hatalar için JSON yerine plain text response döndürmek isteyebilirsiniz:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Teknik Detaylar
`from starlette.responses import PlainTextResponse` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.responses` içeriğini `fastapi.responses` olarak da sunar. Ancak mevcut response’ların çoğu doğrudan Starlette’ten gelir.
///
/// warning | Uyarı
`RequestValidationError`, validation hatasının gerçekleştiği dosya adı ve satır bilgilerini içerir; isterseniz bunu log’larınıza ilgili bilgilerle birlikte yazdırabilirsiniz.
Ancak bu, eğer sadece string’e çevirip bu bilgiyi doğrudan response olarak döndürürseniz sisteminiz hakkında bir miktar bilgi sızdırabileceğiniz anlamına gelir. Bu yüzden burada kod, her bir hatayı ayrı ayrı çıkarıp gösterir.
///
### `RequestValidationError` Body’sini Kullanmak { #use-the-requestvalidationerror-body }
`RequestValidationError`, geçersiz veriyle aldığı `body`’yi içerir.
Uygulamanızı geliştirirken body’yi log’lamak, debug etmek, kullanıcıya döndürmek vb. için bunu kullanabilirsiniz.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Şimdi şu gibi geçersiz bir item göndermeyi deneyin:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Aldığınız body’yi de içeren, verinin geçersiz olduğunu söyleyen bir response alırsınız:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### FastAPI’nin `HTTPException`’ı vs Starlette’in `HTTPException`’ı { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI**’nin kendi `HTTPException`’ı vardır.
Ve **FastAPI**’nin `HTTPException` hata sınıfı, Starlette’in `HTTPException` hata sınıfından kalıtım alır (inherit).
Tek fark şudur: **FastAPI**’nin `HTTPException`’ı `detail` alanı için JSON’a çevrilebilir herhangi bir veri kabul ederken, Starlette’in `HTTPException`’ı burada sadece string kabul eder.
Bu yüzden kodunuzda her zamanki gibi **FastAPI**’nin `HTTPException`’ını raise etmeye devam edebilirsiniz.
Ancak bir exception handler register ederken, bunu Starlette’in `HTTPException`’ı için register etmelisiniz.
Böylece Starlette’in internal kodunun herhangi bir bölümü ya da bir Starlette extension/plug-in’i Starlette `HTTPException` raise ederse, handler’ınız bunu yakalayıp (catch) handle edebilir.
Bu örnekte, iki `HTTPException`’ı da aynı kodda kullanabilmek için Starlette’in exception’ı `StarletteHTTPException` olarak yeniden adlandırılıyor:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### **FastAPI**’nin Exception Handler’larını Yeniden Kullanmak { #reuse-fastapis-exception-handlers }
Exception’ı, **FastAPI**’nin aynı varsayılan exception handler’larıyla birlikte kullanmak isterseniz, varsayılan exception handler’ları `fastapi.exception_handlers` içinden import edip yeniden kullanabilirsiniz:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
Bu örnekte sadece oldukça açıklayıcı bir mesajla hatayı yazdırıyorsunuz; ama fikir anlaşılıyor. Exception’ı kullanıp ardından varsayılan exception handler’ları olduğu gibi yeniden kullanabilirsiniz.
================================================
FILE: docs/tr/docs/tutorial/header-param-models.md
================================================
# Header Parametre Modelleri { #header-parameter-models }
Birbiriyle ilişkili **header parametreleri**nden oluşan bir grubunuz varsa, bunları tanımlamak için bir **Pydantic model** oluşturabilirsiniz.
Bu sayede modeli **birden fazla yerde yeniden kullanabilir**, ayrıca tüm parametreler için doğrulamaları ve metadata bilgilerini tek seferde tanımlayabilirsiniz. 😎
/// note | Not
Bu özellik FastAPI `0.115.0` sürümünden beri desteklenmektedir. 🤓
///
## Pydantic Model ile Header Parametreleri { #header-parameters-with-a-pydantic-model }
İhtiyacınız olan **header parametreleri**ni bir **Pydantic model** içinde tanımlayın, ardından parametreyi `Header` olarak belirtin:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI**, request içindeki **headers** bölümünden **her alan** için veriyi **çıkarır** ve size tanımladığınız Pydantic model örneğini verir.
## Dokümanları Kontrol Edin { #check-the-docs }
Gerekli header'ları `/docs` altındaki doküman arayüzünde görebilirsiniz:
## Ek Header'ları Yasaklayın { #forbid-extra-headers }
Bazı özel kullanım senaryolarında (muhtemelen çok yaygın değil), kabul etmek istediğiniz header'ları **kısıtlamak** isteyebilirsiniz.
Pydantic'in model yapılandırmasını kullanarak `extra` alanları `forbid` edebilirsiniz:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Bir client bazı **ek header'lar** göndermeye çalışırsa, **hata** response'u alır.
Örneğin client, değeri `plumbus` olan bir `tool` header'ı göndermeye çalışırsa, `tool` header parametresine izin verilmediğini söyleyen bir **hata** response'u alır:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Alt Çizgileri Dönüştürmeyi Kapatın { #disable-convert-underscores }
Normal header parametrelerinde olduğu gibi, parametre adlarında alt çizgi karakterleri olduğunda bunlar **otomatik olarak tireye dönüştürülür**.
Örneğin kodda `save_data` adlı bir header parametreniz varsa, beklenen HTTP header `save-data` olur ve dokümanlarda da bu şekilde görünür.
Herhangi bir sebeple bu otomatik dönüşümü kapatmanız gerekiyorsa, header parametreleri için kullandığınız Pydantic model'lerde de bunu devre dışı bırakabilirsiniz.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Uyarı
`convert_underscores` değerini `False` yapmadan önce, bazı HTTP proxy'lerinin ve server'ların alt çizgi içeren header'ların kullanımına izin vermediğini unutmayın.
///
## Özet { #summary }
**FastAPI**'de **headers** tanımlamak için **Pydantic model** kullanabilirsiniz. 😎
================================================
FILE: docs/tr/docs/tutorial/header-params.md
================================================
# Header Parametreleri { #header-parameters }
`Query`, `Path` ve `Cookie` parametrelerini nasıl tanımlıyorsanız, Header parametrelerini de aynı şekilde tanımlayabilirsiniz.
## `Header`'ı Import Edin { #import-header }
Önce `Header`'ı import edin:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## `Header` Parametrelerini Tanımlayın { #declare-header-parameters }
Ardından header parametrelerini, `Path`, `Query` ve `Cookie` ile kullandığınız yapının aynısıyla tanımlayın.
Default değeri ve ek validation ya da annotation parametrelerinin tamamını belirleyebilirsiniz:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Teknik Detaylar
`Header`, `Path`, `Query` ve `Cookie`'nin "kardeş" sınıfıdır. Ayrıca aynı ortak `Param` sınıfından kalıtım alır.
Ancak şunu unutmayın: `fastapi`'den `Query`, `Path`, `Header` ve diğerlerini import ettiğinizde, bunlar aslında özel sınıfları döndüren fonksiyonlardır.
///
/// info | Bilgi
Header'ları tanımlamak için `Header` kullanmanız gerekir; aksi halde parametreler query parametreleri olarak yorumlanır.
///
## Otomatik Dönüştürme { #automatic-conversion }
`Header`, `Path`, `Query` ve `Cookie`'nin sağladıklarına ek olarak küçük bir ekstra işlevsellik sunar.
Standart header'ların çoğu, "hyphen" karakteri (diğer adıyla "minus symbol" (`-`)) ile ayrılır.
Ancak `user-agent` gibi bir değişken adı Python'da geçersizdir.
Bu yüzden, default olarak `Header`, header'ları almak ve dokümante etmek için parametre adlarındaki underscore (`_`) karakterlerini hyphen (`-`) ile dönüştürür.
Ayrıca HTTP header'ları büyük/küçük harfe duyarlı değildir; dolayısıyla onları standart Python stiliyle (diğer adıyla "snake_case") tanımlayabilirsiniz.
Yani `User_Agent` gibi bir şey yazıp ilk harfleri büyütmeniz gerekmeden, Python kodunda normalde kullandığınız gibi `user_agent` kullanabilirsiniz.
Herhangi bir nedenle underscore'ların hyphen'lara otomatik dönüştürülmesini kapatmanız gerekirse, `Header`'ın `convert_underscores` parametresini `False` yapın:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Uyarı
`convert_underscores`'u `False` yapmadan önce, bazı HTTP proxy'lerinin ve server'ların underscore içeren header'ların kullanımına izin vermediğini unutmayın.
///
## Yinelenen Header'lar { #duplicate-headers }
Yinelenen header'lar almak mümkündür. Yani aynı header'ın birden fazla değeri olabilir.
Bu tür durumları, type tanımında bir list kullanarak belirtebilirsiniz.
Yinelenen header'daki tüm değerleri Python `list` olarak alırsınız.
Örneğin, birden fazla kez gelebilen `X-Token` header'ını tanımlamak için şöyle yazabilirsiniz:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Eğer bu *path operation* ile iki HTTP header göndererek iletişim kurarsanız:
```
X-Token: foo
X-Token: bar
```
response şöyle olur:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Özet { #recap }
Header'ları `Header` ile tanımlayın; `Query`, `Path` ve `Cookie` ile kullanılan ortak kalıbı burada da kullanın.
Değişkenlerinizdeki underscore'lar konusunda endişelenmeyin, **FastAPI** bunları dönüştürmeyi halleder.
================================================
FILE: docs/tr/docs/tutorial/index.md
================================================
# Eğitim - Kullanıcı Rehberi { #tutorial-user-guide }
Bu eğitim, **FastAPI**'yi özelliklerinin çoğuyla birlikte adım adım nasıl kullanacağınızı gösterir.
Her bölüm bir öncekilerin üzerine kademeli olarak eklenir, ancak konular birbirinden ayrılacak şekilde yapılandırılmıştır; böylece API ihtiyaçlarınıza göre doğrudan belirli bir konuya gidip aradığınızı bulabilirsiniz.
Ayrıca, ileride tekrar dönüp tam olarak ihtiyaç duyduğunuz şeyi görebileceğiniz bir referans olarak da tasarlanmıştır.
## Kodu Çalıştırın { #run-the-code }
Tüm code block'lar kopyalanıp doğrudan kullanılabilir (zaten test edilmiş Python dosyalarıdır).
Örneklerden herhangi birini çalıştırmak için, kodu `main.py` adlı bir dosyaya kopyalayın ve `fastapi dev`'i başlatın:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
Kodu yazmanız ya da kopyalayıp düzenlemeniz ve yerelinizde çalıştırmanız **şiddetle önerilir**.
Editörünüzde kullanmak FastAPI'nin avantajlarını gerçekten gösterir: ne kadar az kod yazmanız gerektiğini, type check'leri, autocompletion'ı vb. görürsünüz.
---
## FastAPI'yi Kurun { #install-fastapi }
İlk adım FastAPI'yi kurmaktır.
Bir [sanal ortam](../virtual-environments.md) oluşturduğunuzdan emin olun, etkinleştirin ve ardından **FastAPI'yi kurun**:
/// note | Not
`pip install "fastapi[standard]"` ile kurduğunuzda, bazı varsayılan opsiyonel standard bağımlılıklarla birlikte gelir. Bunlara `fastapi-cloud-cli` da dahildir; bu sayede [FastAPI Cloud](https://fastapicloud.com)'a deploy edebilirsiniz.
Bu opsiyonel bağımlılıkları istemiyorsanız bunun yerine `pip install fastapi` kurabilirsiniz.
Standard bağımlılıkları kurmak istiyor ama `fastapi-cloud-cli` olmasın diyorsanız, `pip install "fastapi[standard-no-fastapi-cloud-cli]"` ile kurabilirsiniz.
///
/// tip | İpucu
FastAPI'nin [VS Code için resmi bir eklentisi](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (ve Cursor) vardır; path operation gezgini, path operation araması, testlerde CodeLens ile gezinme (testlerden tanıma atlama) ve FastAPI Cloud deploy ve logları gibi pek çok özelliği doğrudan editörünüzden sunar.
///
## İleri Düzey Kullanıcı Rehberi { #advanced-user-guide }
Bu **Eğitim - Kullanıcı Rehberi**'ni bitirdikten sonra daha sonra okuyabileceğiniz bir **İleri Düzey Kullanıcı Rehberi** de var.
**İleri Düzey Kullanıcı Rehberi** bunun üzerine inşa eder, aynı kavramları kullanır ve size bazı ek özellikler öğretir.
Ancak önce **Eğitim - Kullanıcı Rehberi**'ni (şu anda okuduğunuz bölümü) okumalısınız.
Yalnızca **Eğitim - Kullanıcı Rehberi** ile eksiksiz bir uygulama oluşturabilmeniz hedeflenmiştir; ardından ihtiyaçlarınıza göre, **İleri Düzey Kullanıcı Rehberi**'ndeki ek fikirlerden bazılarını kullanarak farklı şekillerde genişletebilirsiniz.
================================================
FILE: docs/tr/docs/tutorial/metadata.md
================================================
# Metadata ve Doküman URL'leri { #metadata-and-docs-urls }
**FastAPI** uygulamanızda çeşitli metadata yapılandırmalarını özelleştirebilirsiniz.
## API için Metadata { #metadata-for-api }
OpenAPI spesifikasyonunda ve otomatik API doküman arayüzlerinde kullanılan şu alanları ayarlayabilirsiniz:
| Parametre | Tip | Açıklama |
|------------|------|-------------|
| `title` | `str` | API'nin başlığı. |
| `summary` | `str` | API'nin kısa özeti. OpenAPI 3.1.0, FastAPI 0.99.0 sürümünden itibaren mevcut. |
| `description` | `str` | API'nin kısa açıklaması. Markdown kullanabilir. |
| `version` | `string` | API'nin sürümü. Bu, OpenAPI'nin değil, kendi uygulamanızın sürümüdür. Örneğin `2.5.0`. |
| `terms_of_service` | `str` | API'nin Kullanım Koşulları (Terms of Service) için bir URL. Verilirse, URL formatında olmalıdır. |
| `contact` | `dict` | Yayınlanan API için iletişim bilgileri. Birden fazla alan içerebilir. contact alanları
Parametre
Tip
Açıklama
name
str
İletişim kişisi/kuruluşunu tanımlayan ad.
url
str
İletişim bilgilerine işaret eden URL. URL formatında OLMALIDIR.
|
| `license_info` | `dict` | Yayınlanan API için lisans bilgileri. Birden fazla alan içerebilir. license_info alanları
Parametre
Tip
Açıklama
name
str
ZORUNLU (license_info ayarlanmışsa). API için kullanılan lisans adı.
identifier
str
API için bir [SPDX](https://spdx.org/licenses/) lisans ifadesi. identifier alanı, url alanıyla karşılıklı olarak dışlayıcıdır (ikisi aynı anda kullanılamaz). OpenAPI 3.1.0, FastAPI 0.99.0 sürümünden itibaren mevcut.
url
str
API için kullanılan lisansa ait URL. URL formatında OLMALIDIR.
|
Şu şekilde ayarlayabilirsiniz:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | İpucu
`description` alanına Markdown yazabilirsiniz; çıktı tarafında render edilir.
///
Bu yapılandırmayla otomatik API dokümanları şöyle görünür:
## Lisans Tanımlayıcısı { #license-identifier }
OpenAPI 3.1.0 ve FastAPI 0.99.0 sürümünden itibaren, `license_info` içinde `url` yerine bir `identifier` da ayarlayabilirsiniz.
Örneğin:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Tag'ler için Metadata { #metadata-for-tags }
`openapi_tags` parametresiyle, path operation'larınızı gruplamak için kullandığınız farklı tag'ler adına ek metadata da ekleyebilirsiniz.
Bu parametre, her tag için bir sözlük (dictionary) içeren bir liste alır.
Her sözlük şunları içerebilir:
* `name` (**zorunlu**): *path operation*'larda ve `APIRouter`'larda `tags` parametresinde kullandığınız tag adıyla aynı olan bir `str`.
* `description`: tag için kısa bir açıklama içeren `str`. Markdown içerebilir ve doküman arayüzünde gösterilir.
* `externalDocs`: harici dokümanları tanımlayan bir `dict`:
* `description`: harici dokümanlar için kısa açıklama içeren `str`.
* `url` (**zorunlu**): harici dokümantasyonun URL'sini içeren `str`.
### Tag'ler için metadata oluşturun { #create-metadata-for-tags }
`users` ve `items` tag'lerini içeren bir örnekle deneyelim.
Tag'leriniz için metadata oluşturup `openapi_tags` parametresine geçin:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Açıklamaların içinde Markdown kullanabileceğinizi unutmayın; örneğin "login" kalın (**login**) ve "fancy" italik (_fancy_) olarak gösterilecektir.
/// tip | İpucu
Kullandığınız tüm tag'ler için metadata eklemek zorunda değilsiniz.
///
### Tag'lerinizi kullanın { #use-your-tags }
*path operation*'larınızı (ve `APIRouter`'ları) farklı tag'lere atamak için `tags` parametresini kullanın:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Bilgi
Tag'ler hakkında daha fazlası için: [Path Operation Configuration](path-operation-configuration.md#tags).
///
### Dokümanları kontrol edin { #check-the-docs }
Artık dokümanlara baktığınızda, eklediğiniz tüm metadata gösterilir:
### Tag sırası { #order-of-tags }
Her tag metadata sözlüğünün listedeki sırası, doküman arayüzünde gösterilecek sırayı da belirler.
Örneğin alfabetik sıralamada `users`, `items`'tan sonra gelirdi; ancak listedeki ilk sözlük olarak `users` metadata'sını eklediğimiz için, dokümanlarda önce o görünür.
## OpenAPI URL'si { #openapi-url }
Varsayılan olarak OpenAPI şeması `/openapi.json` adresinden sunulur.
Ancak bunu `openapi_url` parametresiyle yapılandırabilirsiniz.
Örneğin `/api/v1/openapi.json` adresinden sunulacak şekilde ayarlamak için:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
OpenAPI şemasını tamamen kapatmak isterseniz `openapi_url=None` ayarlayabilirsiniz; bu, onu kullanan dokümantasyon arayüzlerini de devre dışı bırakır.
## Doküman URL'leri { #docs-urls }
Dahil gelen iki dokümantasyon arayüzünü yapılandırabilirsiniz:
* **Swagger UI**: `/docs` adresinden sunulur.
* URL'sini `docs_url` parametresiyle ayarlayabilirsiniz.
* `docs_url=None` ayarlayarak devre dışı bırakabilirsiniz.
* **ReDoc**: `/redoc` adresinden sunulur.
* URL'sini `redoc_url` parametresiyle ayarlayabilirsiniz.
* `redoc_url=None` ayarlayarak devre dışı bırakabilirsiniz.
Örneğin Swagger UI'yi `/documentation` adresinden sunup ReDoc'u kapatmak için:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/tr/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
**FastAPI** uygulamalarına middleware ekleyebilirsiniz.
"Middleware", herhangi bir özel *path operation* tarafından işlenmeden önce her **request** ile çalışan bir fonksiyondur. Ayrıca geri döndürmeden önce her **response** ile de çalışır.
* Uygulamanıza gelen her **request**'i alır.
* Ardından o **request** üzerinde bir işlem yapabilir veya gerekli herhangi bir kodu çalıştırabilir.
* Sonra **request**'i uygulamanın geri kalanı tarafından işlenmesi için iletir (bir *path operation* tarafından).
* Ardından uygulama tarafından üretilen **response**'u alır (bir *path operation* tarafından).
* Sonra o **response** üzerinde bir işlem yapabilir veya gerekli herhangi bir kodu çalıştırabilir.
* Son olarak **response**'u döndürür.
/// note | Teknik Detaylar
`yield` ile dependency'leriniz varsa, çıkış (exit) kodu middleware'den *sonra* çalışır.
Herhangi bir background task varsa ([Background Tasks](background-tasks.md) bölümünde ele alınıyor, ileride göreceksiniz), bunlar tüm middleware'ler *tamamlandıktan sonra* çalışır.
///
## Middleware Oluşturma { #create-a-middleware }
Bir middleware oluşturmak için bir fonksiyonun üzerine `@app.middleware("http")` decorator'ünü kullanırsınız.
Middleware fonksiyonu şunları alır:
* `request`.
* Parametre olarak `request` alacak bir `call_next` fonksiyonu.
* Bu fonksiyon `request`'i ilgili *path operation*'a iletir.
* Ardından ilgili *path operation* tarafından üretilen `response`'u döndürür.
* Sonrasında `response`'u döndürmeden önce ayrıca değiştirebilirsiniz.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip | İpucu
Özel (proprietary) header'lar [`X-` prefix'i kullanılarak](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) eklenebilir, bunu aklınızda tutun.
Ancak tarayıcıdaki bir client'ın görebilmesini istediğiniz özel header'larınız varsa, bunları CORS konfigürasyonlarınıza ([CORS (Cross-Origin Resource Sharing)](cors.md)) eklemeniz gerekir. Bunun için, [Starlette'ın CORS dokümanlarında](https://www.starlette.dev/middleware/#corsmiddleware) belgelenen `expose_headers` parametresini kullanın.
///
/// note | Teknik Detaylar
`from starlette.requests import Request` da kullanabilirdiniz.
**FastAPI** bunu geliştirici olarak size kolaylık olsun diye sunar. Ancak doğrudan Starlette'tan gelir.
///
### `response`'tan Önce ve Sonra { #before-and-after-the-response }
Herhangi bir *path operation* `request`'i almadan önce, `request` ile birlikte çalışacak kod ekleyebilirsiniz.
Ayrıca `response` üretildikten sonra, geri döndürmeden önce de kod çalıştırabilirsiniz.
Örneğin, request'i işleyip response üretmenin kaç saniye sürdüğünü içeren `X-Process-Time` adlı özel bir header ekleyebilirsiniz:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip | İpucu
Burada [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) yerine `time.time()` kullanıyoruz, çünkü bu kullanım senaryolarında daha hassas olabilir. 🤓
///
## Birden Fazla Middleware Çalıştırma Sırası { #multiple-middleware-execution-order }
`@app.middleware()` decorator'ü veya `app.add_middleware()` metodu ile birden fazla middleware eklediğinizde, eklenen her yeni middleware uygulamayı sarar ve bir stack oluşturur. En son eklenen middleware en *dıştaki* (outermost), ilk eklenen ise en *içteki* (innermost) olur.
Request tarafında önce en *dıştaki* middleware çalışır.
Response tarafında ise en son o çalışır.
Örneğin:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Bu, aşağıdaki çalıştırma sırasını oluşturur:
* **Request**: MiddlewareB → MiddlewareA → route
* **Response**: route → MiddlewareA → MiddlewareB
Bu stack davranışı, middleware'lerin öngörülebilir ve kontrol edilebilir bir sırayla çalıştırılmasını sağlar.
## Diğer Middleware'ler { #other-middlewares }
Diğer middleware'ler hakkında daha fazlasını daha sonra [Advanced User Guide: Advanced Middleware](../advanced/middleware.md) bölümünde okuyabilirsiniz.
Bir sonraki bölümde, middleware ile CORS'un nasıl ele alınacağını göreceksiniz.
================================================
FILE: docs/tr/docs/tutorial/path-operation-configuration.md
================================================
# Path Operation Yapılandırması { #path-operation-configuration }
Onu yapılandırmak için *path operation decorator*’ınıza geçebileceğiniz çeşitli parametreler vardır.
/// warning | Uyarı
Bu parametrelerin *path operation function*’ınıza değil, doğrudan *path operation decorator*’ına verildiğine dikkat edin.
///
## Response Status Code { #response-status-code }
*Path operation*’ınızın response’unda kullanılacak (HTTP) `status_code`’u tanımlayabilirsiniz.
`404` gibi `int` kodu doğrudan verebilirsiniz.
Ancak her sayısal kodun ne işe yaradığını hatırlamıyorsanız, `status` içindeki kısayol sabitlerini kullanabilirsiniz:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Bu status code response’da kullanılacak ve OpenAPI şemasına eklenecektir.
/// note | Teknik Detaylar
`from starlette import status` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak işinizi kolaylaştırmak için `starlette.status`’u `fastapi.status` olarak da sunar. Ancak kaynağı doğrudan Starlette’tir.
///
## Tags { #tags }
*Path operation*’ınıza tag ekleyebilirsiniz; `tags` parametresine `str` öğelerinden oluşan bir `list` verin (genellikle tek bir `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Bunlar OpenAPI şemasına eklenecek ve otomatik dokümantasyon arayüzleri tarafından kullanılacaktır:
### Enum ile Tags { #tags-with-enums }
Büyük bir uygulamanız varsa, zamanla **birden fazla tag** birikebilir ve ilişkili *path operation*’lar için her zaman **aynı tag**’i kullandığınızdan emin olmak isteyebilirsiniz.
Bu durumlarda tag’leri bir `Enum` içinde tutmak mantıklı olabilir.
**FastAPI** bunu düz string’lerde olduğu gibi aynı şekilde destekler:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Özet ve açıklama { #summary-and-description }
Bir `summary` ve `description` ekleyebilirsiniz:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Docstring’den Açıklama { #description-from-docstring }
Açıklamalar genelde uzun olur ve birden fazla satıra yayılır; bu yüzden *path operation* açıklamasını, fonksiyonun içinde docstring olarak tanımlayabilirsiniz; **FastAPI** de onu buradan okur.
Docstring içinde [Markdown](https://en.wikipedia.org/wiki/Markdown) yazabilirsiniz; doğru şekilde yorumlanır ve gösterilir (docstring girintisi dikkate alınarak).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Interactive docs’ta şöyle kullanılacaktır:
## Response description { #response-description }
`response_description` parametresi ile response açıklamasını belirtebilirsiniz:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Bilgi
`response_description` özellikle response’u ifade eder; `description` ise genel olarak *path operation*’ı ifade eder.
///
/// check | Ek bilgi
OpenAPI, her *path operation* için bir response description zorunlu kılar.
Bu yüzden siz sağlamazsanız, **FastAPI** otomatik olarak "Successful response" üretir.
///
## Bir *path operation*’ı Deprecate Etmek { #deprecate-a-path-operation }
Bir *path operation*’ı kaldırmadan, deprecated olarak işaretlemeniz gerekiyorsa `deprecated` parametresini verin:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
Interactive docs’ta deprecated olduğu net şekilde işaretlenecektir:
Deprecated olan ve olmayan *path operation*’ların nasıl göründüğüne bakın:
## Özet { #recap }
*Path operation*’larınızı, *path operation decorator*’larına parametre geçirerek kolayca yapılandırabilir ve metadata ekleyebilirsiniz.
================================================
FILE: docs/tr/docs/tutorial/path-params-numeric-validations.md
================================================
# Path Parametreleri ve Sayısal Doğrulamalar { #path-parameters-and-numeric-validations }
`Query` ile query parametreleri için daha fazla doğrulama ve metadata tanımlayabildiğiniz gibi, `Path` ile de path parametreleri için aynı tür doğrulama ve metadata tanımlayabilirsiniz.
## `Path`'i İçe Aktarın { #import-path }
Önce `fastapi` içinden `Path`'i ve `Annotated`'ı içe aktarın:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Bilgi
FastAPI, 0.95.0 sürümünde `Annotated` desteğini ekledi (ve bunu önermeye başladı).
Daha eski bir sürüm kullanıyorsanız, `Annotated` kullanmaya çalıştığınızda hata alırsınız.
`Annotated` kullanmadan önce mutlaka FastAPI sürümünü en az 0.95.1 olacak şekilde [FastAPI sürümünü yükseltin](../deployment/versions.md#upgrading-the-fastapi-versions).
///
## Metadata Tanımlayın { #declare-metadata }
`Query` için geçerli olan parametrelerin aynısını tanımlayabilirsiniz.
Örneğin, `item_id` path parametresi için bir `title` metadata değeri tanımlamak isterseniz şunu yazabilirsiniz:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Not
Bir path parametresi her zaman zorunludur, çünkü path'in bir parçası olmak zorundadır. `None` ile tanımlasanız veya bir varsayılan değer verseniz bile bu hiçbir şeyi değiştirmez; yine her zaman zorunlu olur.
///
## Parametreleri İhtiyacınıza Göre Sıralayın { #order-the-parameters-as-you-need }
/// tip | İpucu
`Annotated` kullanıyorsanız, bu muhtemelen o kadar önemli ya da gerekli değildir.
///
Diyelim ki query parametresi `q`'yu zorunlu bir `str` olarak tanımlamak istiyorsunuz.
Ayrıca bu parametre için başka bir şey tanımlamanız gerekmiyor; dolayısıyla `Query` kullanmanıza da aslında gerek yok.
Ancak `item_id` path parametresi için yine de `Path` kullanmanız gerekiyor. Ve bir sebepten `Annotated` kullanmak istemiyorsunuz.
Python, "default" değeri olan bir parametreyi, "default" değeri olmayan bir parametreden önce yazarsanız şikayet eder.
Ama bunların sırasını değiştirebilir ve default değeri olmayan parametreyi (query parametresi `q`) en başa koyabilirsiniz.
Bu **FastAPI** için önemli değildir. FastAPI parametreleri isimlerine, tiplerine ve default tanımlarına (`Query`, `Path`, vb.) göre tespit eder; sırayla ilgilenmez.
Dolayısıyla fonksiyonunuzu şöyle tanımlayabilirsiniz:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Namun şunu unutmayın: `Annotated` kullanırsanız bu problem olmaz; çünkü `Query()` veya `Path()` için fonksiyon parametresi default değerlerini kullanmıyorsunuz.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Parametreleri İhtiyacınıza Göre Sıralayın: Küçük Hileler { #order-the-parameters-as-you-need-tricks }
/// tip | İpucu
`Annotated` kullanıyorsanız, bu muhtemelen o kadar önemli ya da gerekli değildir.
///
İşte bazen işe yarayan **küçük bir hile**; ama çok sık ihtiyacınız olmayacak.
Şunları yapmak istiyorsanız:
* `q` query parametresini `Query` kullanmadan ve herhangi bir default değer vermeden tanımlamak
* `item_id` path parametresini `Path` kullanarak tanımlamak
* bunları farklı bir sırada yazmak
* `Annotated` kullanmamak
...Python bunun için küçük, özel bir sözdizimi sunar.
Fonksiyonun ilk parametresi olarak `*` geçin.
Python bu `*` ile bir şey yapmaz; ama bundan sonraki tüm parametrelerin keyword argument (anahtar-değer çiftleri) olarak çağrılması gerektiğini bilir; buna kwargs da denir. Default değerleri olmasa bile.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### `Annotated` ile Daha İyi { #better-with-annotated }
Şunu da unutmayın: `Annotated` kullanırsanız, fonksiyon parametresi default değerlerini kullanmadığınız için bu sorun ortaya çıkmaz ve muhtemelen `*` kullanmanız da gerekmez.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Sayı Doğrulamaları: Büyük Eşit { #number-validations-greater-than-or-equal }
`Query` ve `Path` (ve ileride göreceğiniz diğerleri) ile sayı kısıtları tanımlayabilirsiniz.
Burada `ge=1` ile, `item_id` değerinin `1`'den "`g`reater than or `e`qual" olacak şekilde bir tam sayı olması gerekir.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Sayı Doğrulamaları: Büyük ve Küçük Eşit { #number-validations-greater-than-and-less-than-or-equal }
Aynısı şunlar için de geçerlidir:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Sayı Doğrulamaları: `float` Değerler, Büyük ve Küçük { #number-validations-floats-greater-than-and-less-than }
Sayı doğrulamaları `float` değerler için de çalışır.
Burada gt tanımlayabilmek (sadece ge değil) önemli hale gelir. Çünkü örneğin bir değerin `0`'dan büyük olmasını isteyebilirsiniz; `1`'den küçük olsa bile.
Bu durumda `0.5` geçerli bir değer olur. Ancak `0.0` veya `0` geçerli olmaz.
Aynısı lt için de geçerlidir.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Özet { #recap }
`Query`, `Path` (ve henüz görmedikleriniz) ile metadata ve string doğrulamalarını, [Query Parametreleri ve String Doğrulamalar](query-params-str-validations.md) bölümündekiyle aynı şekilde tanımlayabilirsiniz.
Ayrıca sayısal doğrulamalar da tanımlayabilirsiniz:
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info | Bilgi
`Query`, `Path` ve ileride göreceğiniz diğer class'lar ortak bir `Param` class'ının alt class'larıdır.
Hepsi, gördüğünüz ek doğrulama ve metadata parametrelerini paylaşır.
///
/// note | Teknik Detaylar
`Query`, `Path` ve diğerlerini `fastapi` içinden import ettiğinizde, bunlar aslında birer fonksiyondur.
Çağrıldıklarında, aynı isme sahip class'ların instance'larını döndürürler.
Yani `Query`'yi import edersiniz; bu bir fonksiyondur. Onu çağırdığınızda, yine `Query` adlı bir class'ın instance'ını döndürür.
Bu fonksiyonlar (class'ları doğrudan kullanmak yerine) editörünüzün type'larıyla ilgili hata işaretlememesi için vardır.
Bu sayede, bu hataları yok saymak üzere özel ayarlar eklemeden normal editörünüzü ve coding araçlarınızı kullanabilirsiniz.
///
================================================
FILE: docs/tr/docs/tutorial/path-params.md
================================================
# Yol Parametreleri { #path-parameters }
Python string biçimlemede kullanılan sözdizimiyle path "parametreleri"ni veya "değişkenleri"ni tanımlayabilirsiniz:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
Path parametresi `item_id`'nin değeri, fonksiyonunuza `item_id` argümanı olarak aktarılacaktır.
Yani, bu örneği çalıştırıp [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) adresine giderseniz, şöyle bir response görürsünüz:
```JSON
{"item_id":"foo"}
```
## Tip İçeren Yol Parametreleri { #path-parameters-with-types }
Standart Python tip belirteçlerini kullanarak path parametresinin tipini fonksiyonun içinde tanımlayabilirsiniz:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
Bu durumda, `item_id` bir `int` olarak tanımlanır.
/// check | Ek bilgi
Bu sayede, fonksiyon içinde hata denetimi, kod tamamlama vb. konularda editör desteğine kavuşursunuz.
///
## Veri dönüştürme { #data-conversion }
Bu örneği çalıştırıp tarayıcınızda [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3) adresini açarsanız, şöyle bir response görürsünüz:
```JSON
{"item_id":3}
```
/// check | Ek bilgi
Dikkat edin: fonksiyonunuzun aldığı (ve döndürdüğü) değer olan `3`, string `"3"` değil, bir Python `int`'idir.
Yani, bu tip tanımıyla birlikte **FastAPI** size otomatik request "ayrıştırma" sağlar.
///
## Veri Doğrulama { #data-validation }
Ancak tarayıcınızda [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) adresine giderseniz, şuna benzer güzel bir HTTP hatası görürsünüz:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
çünkü path parametresi `item_id`, `int` olmayan `"foo"` değerine sahipti.
Aynı hata, şu örnekte olduğu gibi `int` yerine `float` verirseniz de ortaya çıkar: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Ek bilgi
Yani, aynı Python tip tanımıyla birlikte **FastAPI** size veri doğrulama sağlar.
Dikkat edin: hata ayrıca doğrulamanın geçmediği noktayı da açıkça belirtir.
Bu, API'ınızla etkileşime giren kodu geliştirirken ve debug ederken inanılmaz derecede faydalıdır.
///
## Dokümantasyon { #documentation }
Tarayıcınızı [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) adresinde açtığınızda, aşağıdaki gibi otomatik ve interaktif bir API dokümantasyonu görürsünüz:
/// check | Ek bilgi
Yine, sadece aynı Python tip tanımıyla **FastAPI** size otomatik ve interaktif dokümantasyon (Swagger UI entegrasyonuyla) sağlar.
Dikkat edin: path parametresi integer olarak tanımlanmıştır.
///
## Standartlara Dayalı Avantajlar, Alternatif Dokümantasyon { #standards-based-benefits-alternative-documentation }
Üretilen şema [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) standardından geldiği için birçok uyumlu araç vardır.
Bu nedenle **FastAPI**'ın kendisi, [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) adresinden erişebileceğiniz alternatif bir API dokümantasyonu (ReDoc kullanarak) sağlar:
Aynı şekilde, birçok uyumlu araç vardır. Birçok dil için kod üretme araçları da buna dahildir.
## Pydantic { #pydantic }
Tüm veri doğrulamaları, arka planda [Pydantic](https://docs.pydantic.dev/) tarafından gerçekleştirilir; böylece onun tüm avantajlarından faydalanırsınız. Ve emin ellerde olduğunuzu bilirsiniz.
Aynı tip tanımlarını `str`, `float`, `bool` ve daha birçok karmaşık veri tipiyle kullanabilirsiniz.
Bunların birkaçı, eğitimin sonraki bölümlerinde ele alınacaktır.
## Sıralama Önemlidir { #order-matters }
*Path operation*'lar oluştururken sabit bir path'e sahip olduğunuz durumlarla karşılaşabilirsiniz.
Örneğin `/users/me`'nin, geçerli kullanıcı hakkında veri almak için kullanıldığını varsayalım.
Sonra belirli bir kullanıcı hakkında, kullanıcı ID'si ile veri almak için `/users/{user_id}` şeklinde bir path'iniz de olabilir.
*Path operation*'lar sırayla değerlendirildiği için, `/users/me` için olan path'in `/users/{user_id}` olandan önce tanımlandığından emin olmanız gerekir:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Aksi halde, `/users/{user_id}` için olan path, `"me"` değerini `user_id` parametresi olarak aldığını "düşünerek" `/users/me` için de eşleşir.
Benzer şekilde, bir path operation'ı yeniden tanımlayamazsınız:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Path önce eşleştiği için her zaman ilk olan kullanılır.
## Ön Tanımlı Değerler { #predefined-values }
Bir *path operation*'ınız *path parameter* alıyorsa ama olası geçerli *path parameter* değerlerinin önceden tanımlı olmasını istiyorsanız, standart bir Python `Enum` kullanabilirsiniz.
### Bir `Enum` Sınıfı Oluşturalım { #create-an-enum-class }
`Enum`'u import edin ve `str` ile `Enum`'dan miras alan bir alt sınıf oluşturun.
`str`'den miras aldığınızda API dokümanları değerlerin `string` tipinde olması gerektiğini anlayabilir ve doğru şekilde render edebilir.
Sonra, kullanılabilir geçerli değerler olacak sabit değerli class attribute'ları oluşturun:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | İpucu
Merak ediyorsanız: "AlexNet", "ResNet" ve "LeNet", Makine Öğrenmesi modellerinin sadece isimleridir.
///
### Bir *Path Parameter* Tanımlayalım { #declare-a-path-parameter }
Ardından oluşturduğunuz enum sınıfını (`ModelName`) kullanarak tip belirteciyle bir *path parameter* oluşturun:
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Dokümana Göz Atalım { #check-the-docs }
*Path parameter* için kullanılabilir değerler ön tanımlı olduğu için, interaktif dokümanlar bunları güzelce gösterebilir:
### Python *Enumeration*'ları ile Çalışmak { #working-with-python-enumerations }
*Path parameter*'ın değeri bir *enumeration member* olacaktır.
#### *Enumeration Member*'ları Karşılaştıralım { #compare-enumeration-members }
Bunu, oluşturduğunuz enum `ModelName` içindeki *enumeration member* ile karşılaştırabilirsiniz:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### *Enumeration Value*'yu Alalım { #get-the-enumeration-value }
Gerçek değeri (bu durumda bir `str`) `model_name.value` ile veya genel olarak `your_enum_member.value` ile alabilirsiniz:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | İpucu
`"lenet"` değerine `ModelName.lenet.value` ile de erişebilirsiniz.
///
#### *Enumeration Member*'ları Döndürelim { #return-enumeration-members }
*Path operation*'ınızdan, bir JSON body'nin içine gömülü olsalar bile (ör. bir `dict`) *enum member*'ları döndürebilirsiniz.
İstemciye dönmeden önce, karşılık gelen değerlerine (bu durumda string) dönüştürülürler:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
İstemcinizde şöyle bir JSON response alırsınız:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Path İçeren Path Parametreleri { #path-parameters-containing-paths }
Diyelim ki `/files/{file_path}` path'ine sahip bir *path operation*'ınız var.
Ama `file_path`'in kendisinin `home/johndoe/myfile.txt` gibi bir *path* içermesi gerekiyor.
Böylece, o dosyanın URL'si şu şekilde olur: `/files/home/johndoe/myfile.txt`.
### OpenAPI Desteği { #openapi-support }
OpenAPI, içinde bir *path* barındıracak bir *path parameter* tanımlamak için bir yöntem desteklemez; çünkü bu, test etmesi ve tanımlaması zor senaryolara yol açabilir.
Yine de, Starlette'in dahili araçlarından birini kullanarak bunu **FastAPI**'da yapabilirsiniz.
Ve dokümanlar, parametrenin bir path içermesi gerektiğini söyleyen herhangi bir dokümantasyon eklemese bile çalışmaya devam eder.
### Path Dönüştürücü { #path-convertor }
Starlette'ten doğrudan gelen bir seçenekle, *path* içeren bir *path parameter*'ı şu URL ile tanımlayabilirsiniz:
```
/files/{file_path:path}
```
Bu durumda parametrenin adı `file_path`'tir ve son kısım olan `:path`, parametrenin herhangi bir *path* ile eşleşmesi gerektiğini söyler.
Yani şununla kullanabilirsiniz:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | İpucu
Parametrenin başında `/home/johndoe/myfile.txt` örneğinde olduğu gibi bir eğik çizgi (`/`) ile başlaması gerekebilir.
Bu durumda URL, `files` ile `home` arasında çift eğik çizgi (`//`) olacak şekilde `/files//home/johndoe/myfile.txt` olur.
///
## Özet { #recap }
**FastAPI** ile kısa, sezgisel ve standart Python tip tanımlarını kullanarak şunları elde edersiniz:
* Editör desteği: hata denetimleri, otomatik tamamlama vb.
* Veri "ayrıştırma"
* Veri doğrulama
* API annotation ve otomatik dokümantasyon
Ve bunları sadece bir kez tanımlamanız yeterlidir.
Bu, (ham performans dışında) **FastAPI**'ın alternatif framework'lere kıyasla muhtemelen en görünür ana avantajıdır.
================================================
FILE: docs/tr/docs/tutorial/query-param-models.md
================================================
# Query Parameter Modelleri { #query-parameter-models }
Birbirleriyle ilişkili bir **query parameter** grubunuz varsa, bunları tanımlamak için bir **Pydantic model** oluşturabilirsiniz.
Böylece **modeli yeniden kullanabilir**, **birden fazla yerde** tekrar tekrar kullanabilir ve tüm parametreler için validation (doğrulama) ile metadata’yı tek seferde tanımlayabilirsiniz. 😎
/// note | Not
Bu özellik FastAPI `0.115.0` sürümünden beri desteklenmektedir. 🤓
///
## Pydantic Model ile Query Parameters { #query-parameters-with-a-pydantic-model }
İhtiyacınız olan **query parameter**’ları bir **Pydantic model** içinde tanımlayın, ardından parametreyi `Query` olarak belirtin:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI**, request’teki **query parameter**’lardan **her field** için veriyi **extract** eder ve tanımladığınız Pydantic model’i size verir.
## Dokümanları Kontrol Edin { #check-the-docs }
Query parameter’ları `/docs` altındaki dokümantasyon arayüzünde görebilirsiniz:
## Ek Query Parameter'ları Yasaklayın { #forbid-extra-query-parameters }
Bazı özel kullanım senaryolarında (muhtemelen çok yaygın değil), almak istediğiniz query parameter’ları **kısıtlamak** isteyebilirsiniz.
Pydantic’in model konfigürasyonunu kullanarak `extra` field’ları `forbid` edebilirsiniz:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Bir client, **query parameter**’larda **ek (extra)** veri göndermeye çalışırsa, **error** response alır.
Örneğin client, değeri `plumbus` olan bir `tool` query parameter’ı göndermeye çalışırsa:
```http
https://example.com/items/?limit=10&tool=plumbus
```
`tool` query parameter’ına izin verilmediğini söyleyen bir **error** response alır:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Özet { #summary }
**FastAPI** içinde **query parameter**’ları tanımlamak için **Pydantic model**’leri kullanabilirsiniz. 😎
/// tip | İpucu
Spoiler: cookie ve header’ları tanımlamak için de Pydantic model’leri kullanabilirsiniz; ancak bunu tutorial’ın ilerleyen bölümlerinde göreceksiniz. 🤫
///
================================================
FILE: docs/tr/docs/tutorial/query-params-str-validations.md
================================================
# Query Parametreleri ve String Doğrulamaları { #query-parameters-and-string-validations }
**FastAPI**, parametreleriniz için ek bilgi ve doğrulamalar (validation) tanımlamanıza izin verir.
Örnek olarak şu uygulamayı ele alalım:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Query parametresi `q`, `str | None` tipindedir. Yani tipi `str`’dir ama `None` da olabilir. Nitekim varsayılan değer `None` olduğu için FastAPI bunun zorunlu olmadığını anlar.
/// note | Not
FastAPI, `q`’nun zorunlu olmadığını `= None` varsayılan değerinden anlar.
`str | None` kullanmak, editörünüzün daha iyi destek vermesini ve hataları yakalamasını sağlar.
///
## Ek Doğrulama { #additional-validation }
`q` opsiyonel olsa bile, verildiği durumda **uzunluğunun 50 karakteri geçmemesini** zorlayacağız.
### `Query` ve `Annotated` import edin { #import-query-and-annotated }
Bunu yapmak için önce şunları import edin:
* `fastapi` içinden `Query`
* `typing` içinden `Annotated`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Bilgi
FastAPI, 0.95.0 sürümünde `Annotated` desteğini ekledi (ve önermeye başladı).
Daha eski bir sürüm kullanıyorsanız `Annotated` kullanmaya çalışırken hata alırsınız.
`Annotated` kullanmadan önce FastAPI sürümünü en az 0.95.1’e yükseltmek için [FastAPI sürümünü yükseltin](../deployment/versions.md#upgrading-the-fastapi-versions).
///
## `q` parametresinin tipinde `Annotated` kullanın { #use-annotated-in-the-type-for-the-q-parameter }
[Python Tiplerine Giriş](../python-types.md#type-hints-with-metadata-annotations) içinde `Annotated` ile parametrelerinize metadata ekleyebileceğinizi söylemiştim, hatırlıyor musunuz?
Şimdi bunu FastAPI ile kullanmanın zamanı. 🚀
Şu tip anotasyonuna sahiptik:
```Python
q: str | None = None
```
Şimdi bunu `Annotated` ile saracağız; şöyle olacak:
```Python
q: Annotated[str | None] = None
```
Bu iki sürüm de aynı anlama gelir: `q`, `str` veya `None` olabilen bir parametredir ve varsayılan olarak `None`’dır.
Şimdi işin eğlenceli kısmına geçelim. 🎉
## `q` parametresindeki `Annotated` içine `Query` ekleyin { #add-query-to-annotated-in-the-q-parameter }
Artık ek bilgi (bu durumda ek doğrulama) koyabildiğimiz bir `Annotated`’ımız olduğuna göre, `Annotated` içine `Query` ekleyin ve `max_length` parametresini `50` olarak ayarlayın:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Varsayılan değerin hâlâ `None` olduğuna dikkat edin; yani parametre hâlâ opsiyonel.
Ama şimdi `Annotated` içinde `Query(max_length=50)` kullanarak FastAPI’ye bu değer için **ek doğrulama** istediğimizi söylüyoruz: en fazla 50 karakter. 😎
/// tip | İpucu
Burada `Query()` kullanıyoruz çünkü bu bir **query parametresi**. İleride `Path()`, `Body()`, `Header()` ve `Cookie()` gibi, `Query()` ile aynı argümanları kabul eden diğerlerini de göreceğiz.
///
FastAPI artık şunları yapacak:
* Verinin uzunluğunun en fazla 50 karakter olduğundan emin olacak şekilde **doğrulayacak**
* Veri geçerli değilse client için **net bir hata** gösterecek
* Parametreyi OpenAPI şemasındaki *path operation* içinde **dokümante edecek** (dolayısıyla **otomatik dokümantasyon arayüzünde** görünecek)
## Alternatif (eski): Varsayılan değer olarak `Query` { #alternative-old-query-as-the-default-value }
FastAPI’nin önceki sürümlerinde ( 0.95.0 öncesi) `Query`’yi `Annotated` içine koymak yerine, parametrenizin varsayılan değeri olarak kullanmanız gerekiyordu. Etrafta bu şekilde yazılmış kod görme ihtimaliniz yüksek; bu yüzden açıklayalım.
/// tip | İpucu
Yeni kodlarda ve mümkün olduğunda, yukarıda anlatıldığı gibi `Annotated` kullanın. Birden fazla avantajı vardır (aşağıda anlatılıyor) ve dezavantajı yoktur. 🍰
///
Fonksiyon parametresinin varsayılan değeri olarak `Query()` kullanıp `max_length` parametresini 50 yapmak şöyle olurdu:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Bu senaryoda (`Annotated` kullanmadığımız için) fonksiyondaki `None` varsayılan değerini `Query()` ile değiştirmemiz gerekiyor. Bu durumda varsayılan değeri `Query(default=None)` ile vermeliyiz; bu, (en azından FastAPI açısından) aynı varsayılan değeri tanımlama amacına hizmet eder.
Yani:
```Python
q: str | None = Query(default=None)
```
...parametreyi `None` varsayılan değeriyle opsiyonel yapar; şununla aynı:
```Python
q: str | None = None
```
Ancak `Query` sürümü bunun bir query parametresi olduğunu açıkça belirtir.
Sonrasında `Query`’ye daha fazla parametre geçebiliriz. Bu örnekte string’ler için geçerli olan `max_length`:
```Python
q: str | None = Query(default=None, max_length=50)
```
Bu, veriyi doğrular, veri geçerli değilse net bir hata gösterir ve parametreyi OpenAPI şemasındaki *path operation* içinde dokümante eder.
### Varsayılan değer olarak `Query` veya `Annotated` içinde `Query` { #query-as-the-default-value-or-in-annotated }
`Annotated` içinde `Query` kullanırken `Query` için `default` parametresini kullanamayacağınızı unutmayın.
Bunun yerine fonksiyon parametresinin gerçek varsayılan değerini kullanın. Aksi halde tutarsız olur.
Örneğin şu kullanım izinli değildir:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...çünkü varsayılan değerin `"rick"` mi `"morty"` mi olması gerektiği belli değildir.
Bu nedenle (tercihen) şöyle kullanırsınız:
```Python
q: Annotated[str, Query()] = "rick"
```
...veya eski kod tabanlarında şuna rastlarsınız:
```Python
q: str = Query(default="rick")
```
### `Annotated`’ın avantajları { #advantages-of-annotated }
Fonksiyon parametrelerindeki varsayılan değer stiline göre **`Annotated` kullanmanız önerilir**; birden fazla nedenle **daha iyidir**. 🤓
**Fonksiyon parametresinin** **varsayılan** değeri, **gerçek varsayılan** değerdir; bu genel olarak Python açısından daha sezgiseldir. 😌
Aynı fonksiyonu FastAPI olmadan **başka yerlerde** de **çağırabilirsiniz** ve **beklendiği gibi çalışır**. Eğer **zorunlu** bir parametre varsa (varsayılan değer yoksa) editörünüz hata ile bunu belirtir; ayrıca gerekli parametreyi vermeden çalıştırırsanız **Python** da şikayet eder.
`Annotated` kullanmayıp bunun yerine **(eski) varsayılan değer stilini** kullanırsanız, o fonksiyonu FastAPI olmadan **başka yerlerde** çağırdığınızda doğru çalışması için argümanları geçmeniz gerektiğini **hatırlamak** zorunda kalırsınız; yoksa değerler beklediğinizden farklı olur (ör. `QueryInfo` veya benzeri). Üstelik editörünüz de şikayet etmez ve Python da fonksiyonu çalıştırırken şikayet etmez; ancak içerideki operasyonlar hata verince ortaya çıkar.
`Annotated` birden fazla metadata anotasyonu alabildiği için, artık aynı fonksiyonu [Typer](https://typer.tiangolo.com/) gibi başka araçlarla da kullanabilirsiniz. 🚀
## Daha fazla doğrulama ekleyin { #add-more-validations }
`min_length` parametresini de ekleyebilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Regular expression ekleyin { #add-regular-expressions }
Parametrenin eşleşmesi gereken bir `pattern` düzenli ifade tanımlayabilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Bu özel regular expression pattern’i, gelen parametre değerinin şunları sağladığını kontrol eder:
* `^`: Aşağıdaki karakterlerle başlar; öncesinde karakter yoktur.
* `fixedquery`: Tam olarak `fixedquery` değerine sahiptir.
* `$`: Orada biter; `fixedquery` sonrasında başka karakter yoktur.
Bu **"regular expression"** konuları gözünüzü korkutuyorsa sorun değil. Birçok kişi için zor bir konudur. Regular expression’lara ihtiyaç duymadan da pek çok şey yapabilirsiniz.
Artık ihtiyaç duyduğunuzda **FastAPI** içinde kullanabileceğinizi biliyorsunuz.
## Varsayılan değerler { #default-values }
Elbette `None` dışında varsayılan değerler de kullanabilirsiniz.
Örneğin `q` query parametresi için `min_length` değerini `3` yapmak ve varsayılan değer olarak `"fixedquery"` vermek istediğinizi düşünelim:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Not
`None` dahil herhangi bir tipte varsayılan değere sahip olmak, parametreyi opsiyonel (zorunlu değil) yapar.
///
## Zorunlu parametreler { #required-parameters }
Daha fazla doğrulama veya metadata tanımlamamız gerekmiyorsa, `q` query parametresini yalnızca varsayılan değer tanımlamayarak zorunlu yapabiliriz:
```Python
q: str
```
şunun yerine:
```Python
q: str | None = None
```
Ancak biz artık `Query` ile tanımlıyoruz; örneğin şöyle:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Dolayısıyla `Query` kullanırken bir değeri zorunlu yapmak istediğinizde, varsayılan değer tanımlamamanız yeterlidir:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Zorunlu ama `None` olabilir { #required-can-be-none }
Bir parametrenin `None` kabul edebileceğini söyleyip yine de zorunlu olmasını sağlayabilirsiniz. Bu, client’ların değer göndermesini zorunlu kılar; değer `None` olsa bile.
Bunu yapmak için `None`’ı geçerli bir tip olarak tanımlayın ama varsayılan değer vermeyin:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Query parametresi listesi / birden fazla değer { #query-parameter-list-multiple-values }
Bir query parametresini `Query` ile açıkça tanımladığınızda, bir değer listesi alacak şekilde (başka bir deyişle, birden fazla değer alacak şekilde) de tanımlayabilirsiniz.
Örneğin URL’de `q` query parametresinin birden fazla kez görünebilmesini istiyorsanız şöyle yazabilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Sonra şu URL ile:
```
http://localhost:8000/items/?q=foo&q=bar
```
*path operation function* içinde, *function parameter* olan `q` parametresinde, birden fazla `q` *query parameters* değerini (`foo` ve `bar`) bir Python `list`’i olarak alırsınız.
Dolayısıyla bu URL’ye response şöyle olur:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | İpucu
Yukarıdaki örnekte olduğu gibi tipi `list` olan bir query parametresi tanımlamak için `Query`’yi açıkça kullanmanız gerekir; aksi halde request body olarak yorumlanır.
///
Etkileşimli API dokümanları da buna göre güncellenir ve birden fazla değere izin verir:
### Varsayılanlarla query parametresi listesi / birden fazla değer { #query-parameter-list-multiple-values-with-defaults }
Hiç değer verilmezse varsayılan bir `list` de tanımlayabilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Şu adrese giderseniz:
```
http://localhost:8000/items/
```
`q`’nun varsayılanı `["foo", "bar"]` olur ve response şöyle olur:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Sadece `list` kullanmak { #using-just-list }
`list[str]` yerine doğrudan `list` de kullanabilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Not
Bu durumda FastAPI, listenin içeriğini kontrol etmez.
Örneğin `list[int]`, listenin içeriğinin integer olduğunu kontrol eder (ve dokümante eder). Ancak tek başına `list` bunu yapmaz.
///
## Daha fazla metadata tanımlayın { #declare-more-metadata }
Parametre hakkında daha fazla bilgi ekleyebilirsiniz.
Bu bilgiler oluşturulan OpenAPI’a dahil edilir ve dokümantasyon arayüzleri ile harici araçlar tarafından kullanılır.
/// note | Not
Farklı araçların OpenAPI desteği farklı seviyelerde olabilir.
Bazıları tanımladığınız ek bilgilerin hepsini göstermeyebilir; ancak çoğu durumda eksik özellik geliştirme planındadır.
///
Bir `title` ekleyebilirsiniz:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
Ve bir `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Alias parametreleri { #alias-parameters }
Parametrenin adının `item-query` olmasını istediğinizi düşünün.
Örneğin:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Ancak `item-query` geçerli bir Python değişken adı değildir.
En yakın seçenek `item_query` olur.
Ama sizin hâlâ tam olarak `item-query` olmasına ihtiyacınız var...
O zaman bir `alias` tanımlayabilirsiniz; bu alias, parametre değerini bulmak için kullanılacaktır:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Parametreleri deprecated yapmak { #deprecating-parameters }
Diyelim ki artık bu parametreyi istemiyorsunuz.
Bazı client’lar hâlâ kullandığı için bir süre tutmanız gerekiyor, ama dokümanların bunu açıkça deprecated olarak göstermesini istiyorsunuz.
O zaman `Query`’ye `deprecated=True` parametresini geçin:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
Dokümanlarda şöyle görünür:
## Parametreleri OpenAPI’dan hariç tutun { #exclude-parameters-from-openapi }
Oluşturulan OpenAPI şemasından (dolayısıyla otomatik dokümantasyon sistemlerinden) bir query parametresini hariç tutmak için `Query`’nin `include_in_schema` parametresini `False` yapın:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Özel Doğrulama { #custom-validation }
Yukarıdaki parametrelerle yapılamayan bazı **özel doğrulama** ihtiyaçlarınız olabilir.
Bu durumlarda, normal doğrulamadan sonra (ör. değerin `str` olduğunun doğrulanmasından sonra) uygulanacak bir **custom validator function** kullanabilirsiniz.
Bunu, `Annotated` içinde [Pydantic’in `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator)’ını kullanarak yapabilirsiniz.
/// tip | İpucu
Pydantic’te [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) ve başka validator’lar da vardır. 🤓
///
Örneğin bu custom validator, bir item ID’sinin ISBN kitap numarası için `isbn-` ile veya IMDB film URL ID’si için `imdb-` ile başladığını kontrol eder:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Bilgi
Bu özellik Pydantic 2 ve üzeri sürümlerde mevcuttur. 😎
///
/// tip | İpucu
Veritabanı veya başka bir API gibi herhangi bir **harici bileşen** ile iletişim gerektiren bir doğrulama yapmanız gerekiyorsa, bunun yerine **FastAPI Dependencies** kullanmalısınız; onları ileride öğreneceksiniz.
Bu custom validator’lar, request’te sağlanan **yalnızca** **aynı veri** ile kontrol edilebilen şeyler içindir.
///
### O Kodu Anlamak { #understand-that-code }
Önemli nokta, **`Annotated` içinde bir fonksiyonla birlikte `AfterValidator` kullanmak**. İsterseniz bu kısmı atlayabilirsiniz. 🤸
---
Ama bu örnek kodun detaylarını merak ediyorsanız, birkaç ek bilgi:
#### `value.startswith()` ile String { #string-with-value-startswith }
Fark ettiniz mi? `value.startswith()` ile bir string, tuple alabilir ve tuple içindeki her değeri kontrol eder:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Rastgele Bir Item { #a-random-item }
`data.items()` ile, her dictionary öğesi için key ve value içeren tuple’lardan oluşan bir yinelemeli nesne elde ederiz.
Bu yinelemeli nesneyi `list(data.items())` ile düzgün bir `list`’e çeviririz.
Ardından `random.choice()` ile list’ten **rastgele bir değer** alırız; yani `(id, name)` içeren bir tuple elde ederiz. Şuna benzer: `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Sonra tuple içindeki bu **iki değeri** `id` ve `name` değişkenlerine **atarız**.
Böylece kullanıcı bir item ID’si vermemiş olsa bile yine de rastgele bir öneri alır.
...bütün bunları **tek bir basit satırda** yapıyoruz. 🤯 Python’u sevmemek elde mi? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Özet { #recap }
Parametreleriniz için ek doğrulamalar ve metadata tanımlayabilirsiniz.
Genel doğrulamalar ve metadata:
* `alias`
* `title`
* `description`
* `deprecated`
String’lere özel doğrulamalar:
* `min_length`
* `max_length`
* `pattern`
`AfterValidator` ile custom doğrulamalar.
Bu örneklerde `str` değerleri için doğrulamanın nasıl tanımlanacağını gördünüz.
Sayılar gibi diğer tipler için doğrulamaları nasıl tanımlayacağınızı öğrenmek için sonraki bölümlere geçin.
================================================
FILE: docs/tr/docs/tutorial/query-params.md
================================================
# Sorgu Parametreleri { #query-parameters }
Fonksiyonda path parametrelerinin parçası olmayan diğer parametreleri tanımladığınızda, bunlar otomatik olarak "query" parametreleri olarak yorumlanır.
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
Query, bir URL'de `?` işaretinden sonra gelen ve `&` karakterleriyle ayrılan anahtar-değer çiftlerinin kümesidir.
Örneğin, şu URL'de:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...query parametreleri şunlardır:
* `skip`: değeri `0`
* `limit`: değeri `10`
URL'nin bir parçası oldukları için "doğal olarak" string'tirler.
Ancak, bunları Python tipleriyle (yukarıdaki örnekte `int` olarak) tanımladığınızda, o tipe dönüştürülürler ve o tipe göre doğrulanırlar.
Path parametreleri için geçerli olan aynı süreç query parametreleri için de geçerlidir:
* Editör desteği (tabii ki)
* Veri "ayrıştırma"
* Veri doğrulama
* Otomatik dokümantasyon
## Varsayılanlar { #defaults }
Query parametreleri path'in sabit bir parçası olmadığından, opsiyonel olabilir ve varsayılan değerlere sahip olabilir.
Yukarıdaki örnekte varsayılan değerleri `skip=0` ve `limit=10`'dur.
Yani şu URL'ye gitmek:
```
http://127.0.0.1:8000/items/
```
şuraya gitmekle aynı olur:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Ancak örneğin şuraya giderseniz:
```
http://127.0.0.1:8000/items/?skip=20
```
Fonksiyonunuzdaki parametre değerleri şöyle olacaktır:
* `skip=20`: çünkü URL'de siz ayarladınız
* `limit=10`: çünkü varsayılan değer oydu
## İsteğe bağlı parametreler { #optional-parameters }
Aynı şekilde, varsayılan değerlerini `None` yaparak isteğe bağlı query parametreleri tanımlayabilirsiniz:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
Bu durumda, fonksiyon parametresi `q` isteğe bağlı olur ve varsayılan olarak `None` olur.
/// check | Ek bilgi
Ayrıca, **FastAPI** path parametresi olan `item_id`'nin bir path parametresi olduğunu ve `q`'nun path olmadığını fark edecek kadar akıllıdır; dolayısıyla bu bir query parametresidir.
///
## Sorgu parametresi tip dönüşümü { #query-parameter-type-conversion }
`bool` tipleri de tanımlayabilirsiniz, ve bunlar dönüştürülür:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
Bu durumda, şuraya giderseniz:
```
http://127.0.0.1:8000/items/foo?short=1
```
veya
```
http://127.0.0.1:8000/items/foo?short=True
```
veya
```
http://127.0.0.1:8000/items/foo?short=true
```
veya
```
http://127.0.0.1:8000/items/foo?short=on
```
veya
```
http://127.0.0.1:8000/items/foo?short=yes
```
veya başka herhangi bir büyük/küçük harf varyasyonunda (tamamı büyük, ilk harf büyük, vb.), fonksiyonunuz `short` parametresini `bool` değeri `True` olarak görecektir. Aksi halde `False` olarak görür.
## Çoklu path ve query parametreleri { #multiple-path-and-query-parameters }
Aynı anda birden fazla path parametresi ve query parametresi tanımlayabilirsiniz; **FastAPI** hangisinin hangisi olduğunu bilir.
Ayrıca bunları belirli bir sırayla tanımlamanız gerekmez.
İsme göre tespit edilirler:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Zorunlu query parametreleri { #required-query-parameters }
Path olmayan parametreler (şimdilik sadece query parametrelerini gördük) için varsayılan değer tanımladığınızda, bu parametre zorunlu olmaz.
Belirli bir değer eklemek istemiyor ama sadece opsiyonel olmasını istiyorsanız, varsayılanı `None` olarak ayarlayın.
Ancak bir query parametresini zorunlu yapmak istediğinizde, herhangi bir varsayılan değer tanımlamamanız yeterlidir:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Burada query parametresi `needy`, `str` tipinde zorunlu bir query parametresidir.
Tarayıcınızda şöyle bir URL açarsanız:
```
http://127.0.0.1:8000/items/foo-item
```
...zorunlu `needy` parametresini eklemeden, şuna benzer bir hata görürsünüz:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
`needy` zorunlu bir parametre olduğundan, URL'de ayarlamanız gerekir:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...bu çalışır:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
Ve elbette, bazı parametreleri zorunlu, bazılarını varsayılan değerli, bazılarını da tamamen isteğe bağlı olarak tanımlayabilirsiniz:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
Bu durumda, 3 tane query parametresi vardır:
* `needy`, zorunlu bir `str`.
* `skip`, varsayılan değeri `0` olan bir `int`.
* `limit`, isteğe bağlı bir `int`.
/// tip | İpucu
[Path Parametreleri](path-params.md#predefined-values) ile aynı şekilde `Enum`'ları da kullanabilirsiniz.
///
================================================
FILE: docs/tr/docs/tutorial/request-files.md
================================================
# Request Dosyaları { #request-files }
İstemcinin upload edeceği dosyaları `File` kullanarak tanımlayabilirsiniz.
/// info | Bilgi
Upload edilen dosyaları alabilmek için önce [`python-multipart`](https://github.com/Kludex/python-multipart) yükleyin.
Bir [virtual environment](../virtual-environments.md) oluşturduğunuzdan, aktive ettiğinizden ve ardından paketi yüklediğinizden emin olun. Örneğin:
```console
$ pip install python-multipart
```
Bunun nedeni, upload edilen dosyaların "form data" olarak gönderilmesidir.
///
## `File` Import Edin { #import-file }
`fastapi` içinden `File` ve `UploadFile` import edin:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## `File` Parametrelerini Tanımlayın { #define-file-parameters }
`Body` veya `Form` için yaptığınız gibi dosya parametreleri oluşturun:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Bilgi
`File`, doğrudan `Form`’dan türeyen bir sınıftır.
Ancak unutmayın: `fastapi` içinden `Query`, `Path`, `File` ve diğerlerini import ettiğinizde, bunlar aslında özel sınıflar döndüren fonksiyonlardır.
///
/// tip | İpucu
File body’leri tanımlamak için `File` kullanmanız gerekir; aksi halde parametreler query parametreleri veya body (JSON) parametreleri olarak yorumlanır.
///
Dosyalar "form data" olarak upload edilir.
*path operation function* parametrenizin tipini `bytes` olarak tanımlarsanız, **FastAPI** dosyayı sizin için okur ve içeriği `bytes` olarak alırsınız.
Bunun, dosyanın tüm içeriğinin bellekte tutulacağı anlamına geldiğini unutmayın. Küçük dosyalar için iyi çalışır.
Ancak bazı durumlarda `UploadFile` kullanmak size fayda sağlayabilir.
## `UploadFile` ile Dosya Parametreleri { #file-parameters-with-uploadfile }
Tipi `UploadFile` olan bir dosya parametresi tanımlayın:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
`UploadFile` kullanmanın `bytes`’a göre birkaç avantajı vardır:
* Parametrenin varsayılan değerinde `File()` kullanmak zorunda değilsiniz.
* "Spooled" bir dosya kullanır:
* Belirli bir maksimum boyuta kadar bellekte tutulan, bu limiti aşınca diske yazılan bir dosya.
* Bu sayede görüntüler, videolar, büyük binary’ler vb. gibi büyük dosyalarda tüm belleği tüketmeden iyi çalışır.
* Upload edilen dosyadan metadata alabilirsiniz.
* [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) bir `async` arayüze sahiptir.
* [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) nesnesini dışa açar; bunu, file-like nesne bekleyen diğer library’lere doğrudan geçebilirsiniz.
### `UploadFile` { #uploadfile }
`UploadFile` şu attribute’lara sahiptir:
* `filename`: Upload edilen orijinal dosya adını içeren bir `str` (örn. `myimage.jpg`).
* `content_type`: Content type’ı (MIME type / media type) içeren bir `str` (örn. `image/jpeg`).
* `file`: Bir [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (bir [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) nesne). Bu, "file-like" nesne bekleyen diğer fonksiyonlara veya library’lere doğrudan verebileceğiniz gerçek Python file nesnesidir.
`UploadFile` şu `async` method’lara sahiptir. Bunların hepsi altta ilgili dosya method’larını çağırır (dahili `SpooledTemporaryFile` kullanarak).
* `write(data)`: Dosyaya `data` (`str` veya `bytes`) yazar.
* `read(size)`: Dosyadan `size` (`int`) kadar byte/karakter okur.
* `seek(offset)`: Dosyada `offset` (`int`) byte pozisyonuna gider.
* Örn. `await myfile.seek(0)` dosyanın başına gider.
* Bu, özellikle bir kez `await myfile.read()` çalıştırdıysanız ve sonra içeriği yeniden okumaya ihtiyaç duyuyorsanız faydalıdır.
* `close()`: Dosyayı kapatır.
Bu method’ların hepsi `async` olduğundan, bunları "await" etmeniz gerekir.
Örneğin, bir `async` *path operation function* içinde içeriği şöyle alabilirsiniz:
```Python
contents = await myfile.read()
```
Normal bir `def` *path operation function* içindeyseniz `UploadFile.file`’a doğrudan erişebilirsiniz, örneğin:
```Python
contents = myfile.file.read()
```
/// note | `async` Teknik Detaylar
`async` method’ları kullandığınızda, **FastAPI** dosya method’larını bir threadpool içinde çalıştırır ve bunları await eder.
///
/// note | Starlette Teknik Detaylar
**FastAPI**’nin `UploadFile`’ı doğrudan **Starlette**’in `UploadFile`’ından türetilmiştir; ancak **Pydantic** ve FastAPI’nin diğer parçalarıyla uyumlu olması için bazı gerekli eklemeler yapar.
///
## "Form Data" Nedir { #what-is-form-data }
HTML formları (``) veriyi server’a gönderirken normalde JSON’dan farklı, veri için "özel" bir encoding kullanır.
**FastAPI**, JSON yerine bu veriyi doğru yerden okuyacağından emin olur.
/// note | Teknik Detaylar
Formlardan gelen veri, dosya içermiyorsa normalde "media type" olarak `application/x-www-form-urlencoded` ile encode edilir.
Ancak form dosya içeriyorsa `multipart/form-data` olarak encode edilir. `File` kullanırsanız, **FastAPI** dosyaları body’nin doğru kısmından alması gerektiğini bilir.
Bu encoding’ler ve form alanları hakkında daha fazla okumak isterseniz [MDN web dokümanlarındaki `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) sayfasına bakın.
///
/// warning | Uyarı
Bir *path operation* içinde birden fazla `File` ve `Form` parametresi tanımlayabilirsiniz, ancak JSON olarak almayı beklediğiniz `Body` alanlarını ayrıca tanımlayamazsınız; çünkü request body `application/json` yerine `multipart/form-data` ile encode edilmiş olur.
Bu, **FastAPI**’nin bir kısıtı değildir; HTTP protocol’ünün bir parçasıdır.
///
## Opsiyonel Dosya Upload { #optional-file-upload }
Standart type annotation’ları kullanıp varsayılan değeri `None` yaparak bir dosyayı opsiyonel hale getirebilirsiniz:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## Ek Metadata ile `UploadFile` { #uploadfile-with-additional-metadata }
Ek metadata ayarlamak için `UploadFile` ile birlikte `File()` da kullanabilirsiniz. Örneğin:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Birden Fazla Dosya Upload { #multiple-file-uploads }
Aynı anda birden fazla dosya upload etmek mümkündür.
Bu dosyalar, "form data" ile gönderilen aynı "form field" ile ilişkilendirilir.
Bunu kullanmak için `bytes` veya `UploadFile` listesini tanımlayın:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Tanımladığınız gibi, `bytes` veya `UploadFile`’lardan oluşan bir `list` alırsınız.
/// note | Teknik Detaylar
`from starlette.responses import HTMLResponse` da kullanabilirsiniz.
**FastAPI**, geliştiriciye kolaylık olsun diye `starlette.responses` modülünü `fastapi.responses` olarak da sağlar. Ancak mevcut response’ların çoğu doğrudan Starlette’ten gelir.
///
### Ek Metadata ile Birden Fazla Dosya Upload { #multiple-file-uploads-with-additional-metadata }
Daha önce olduğu gibi, `UploadFile` için bile ek parametreler ayarlamak amacıyla `File()` kullanabilirsiniz:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Özet { #recap }
Request’te (form data olarak gönderilen) upload edilecek dosyaları tanımlamak için `File`, `bytes` ve `UploadFile` kullanın.
================================================
FILE: docs/tr/docs/tutorial/request-form-models.md
================================================
# Form Model'leri { #form-models }
FastAPI'de **form field**'larını tanımlamak için **Pydantic model**'lerini kullanabilirsiniz.
/// info | Bilgi
Form'ları kullanmak için önce [`python-multipart`](https://github.com/Kludex/python-multipart)'ı yükleyin.
Bir [virtual environment](../virtual-environments.md) oluşturduğunuzdan, onu etkinleştirdiğinizden ve ardından paketi kurduğunuzdan emin olun. Örneğin:
```console
$ pip install python-multipart
```
///
/// note | Not
Bu özellik FastAPI `0.113.0` sürümünden itibaren desteklenmektedir. 🤓
///
## Form'lar için Pydantic Model'leri { #pydantic-models-for-forms }
Sadece, **form field** olarak almak istediğiniz alanlarla bir **Pydantic model** tanımlayın ve ardından parametreyi `Form` olarak bildirin:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI**, request içindeki **form data**'dan **her bir field** için veriyi **çıkarır** ve size tanımladığınız Pydantic model'ini verir.
## Dokümanları Kontrol Edin { #check-the-docs }
Bunu `/docs` altındaki doküman arayüzünde doğrulayabilirsiniz:
## Fazladan Form Field'larını Yasaklayın { #forbid-extra-form-fields }
Bazı özel kullanım senaryolarında (muhtemelen çok yaygın değildir), form field'larını yalnızca Pydantic model'inde tanımlananlarla **sınırlamak** isteyebilirsiniz. Ve **fazladan** gelen field'ları **yasaklayabilirsiniz**.
/// note | Not
Bu özellik FastAPI `0.114.0` sürümünden itibaren desteklenmektedir. 🤓
///
Herhangi bir `extra` field'ı `forbid` etmek için Pydantic'in model konfigürasyonunu kullanabilirsiniz:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Bir client fazladan veri göndermeye çalışırsa, bir **error** response alır.
Örneğin, client şu form field'larını göndermeye çalışırsa:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
`extra` field'ının izinli olmadığını söyleyen bir error response alır:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Özet { #summary }
FastAPI'de form field'larını tanımlamak için Pydantic model'lerini kullanabilirsiniz. 😎
================================================
FILE: docs/tr/docs/tutorial/request-forms-and-files.md
================================================
# Request Formları ve Dosyalar { #request-forms-and-files }
`File` ve `Form` kullanarak aynı anda hem dosyaları hem de form alanlarını tanımlayabilirsiniz.
/// info | Bilgi
Yüklenen dosyaları ve/veya form verisini almak için önce [`python-multipart`](https://github.com/Kludex/python-multipart) paketini kurun.
Bir [sanal ortam](../virtual-environments.md) oluşturduğunuzdan, onu aktive ettiğinizden ve ardından paketi kurduğunuzdan emin olun, örneğin:
```console
$ pip install python-multipart
```
///
## `File` ve `Form` Import Edin { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## `File` ve `Form` Parametrelerini Tanımlayın { #define-file-and-form-parameters }
Dosya ve form parametrelerini, `Body` veya `Query` için yaptığınız şekilde oluşturun:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Dosyalar ve form alanları form data olarak upload edilir ve siz de dosyaları ve form alanlarını alırsınız.
Ayrıca bazı dosyaları `bytes` olarak, bazılarını da `UploadFile` olarak tanımlayabilirsiniz.
/// warning | Uyarı
Bir *path operation* içinde birden fazla `File` ve `Form` parametresi tanımlayabilirsiniz; ancak request'in body'si `application/json` yerine `multipart/form-data` ile encode edileceği için, JSON olarak almayı beklediğiniz `Body` alanlarını aynı anda tanımlayamazsınız.
Bu **FastAPI** kısıtı değildir; HTTP protokolünün bir parçasıdır.
///
## Özet { #recap }
Aynı request içinde hem veri hem de dosya almanız gerektiğinde `File` ve `Form`'u birlikte kullanın.
================================================
FILE: docs/tr/docs/tutorial/request-forms.md
================================================
# Form Verisi { #form-data }
JSON yerine form alanlarını almanız gerektiğinde `Form` kullanabilirsiniz.
/// info | Bilgi
Formları kullanmak için önce [`python-multipart`](https://github.com/Kludex/python-multipart) paketini kurun.
Bir [virtual environment](../virtual-environments.md) oluşturduğunuzdan, onu etkinleştirdiğinizden emin olun ve ardından örneğin şöyle kurun:
```console
$ pip install python-multipart
```
///
## `Form`'u Import Edin { #import-form }
`Form`'u `fastapi`'den import edin:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## `Form` Parametrelerini Tanımlayın { #define-form-parameters }
Form parametrelerini `Body` veya `Query` için yaptığınız gibi oluşturun:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Örneğin OAuth2 spesifikasyonunun kullanılabileceği ("password flow" olarak adlandırılan) yollardan birinde, form alanları olarak bir `username` ve `password` göndermek zorunludur.
Spesifikasyon, alanların adının tam olarak `username` ve `password` olmasını ve JSON değil form alanları olarak gönderilmesini gerektirir.
`Form` ile `Body` (ve `Query`, `Path`, `Cookie`) ile yaptığınız aynı konfigürasyonları tanımlayabilirsiniz; validasyon, örnekler, alias (örn. `username` yerine `user-name`) vb. dahil.
/// info | Bilgi
`Form`, doğrudan `Body`'den miras alan bir sınıftır.
///
/// tip | İpucu
Form gövdelerini tanımlamak için `Form`'u açıkça kullanmanız gerekir; çünkü bunu yapmazsanız parametreler query parametreleri veya body (JSON) parametreleri olarak yorumlanır.
///
## "Form Alanları" Hakkında { #about-form-fields }
HTML formlarının (``) verileri sunucuya gönderme şekli normalde bu veri için JSON'dan farklı "özel" bir encoding kullanır.
**FastAPI** bu veriyi JSON yerine doğru yerden okuyacaktır.
/// note | Teknik Detaylar
Formlardan gelen veri normalde "media type" `application/x-www-form-urlencoded` kullanılarak encode edilir.
Ancak form dosyalar içerdiğinde `multipart/form-data` olarak encode edilir. Dosyaları ele almayı bir sonraki bölümde okuyacaksınız.
Bu encoding'ler ve form alanları hakkında daha fazla okumak isterseniz, [MDN `POST` için web dokümanları](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) sayfasına gidin.
///
/// warning | Uyarı
Bir *path operation* içinde birden fazla `Form` parametresi tanımlayabilirsiniz, ancak JSON olarak almayı beklediğiniz `Body` alanlarını da ayrıca tanımlayamazsınız; çünkü bu durumda request'in body'si `application/x-www-form-urlencoded` ile encode edilmiş olur.
Bu **FastAPI**'ın bir kısıtlaması değildir, HTTP protokolünün bir parçasıdır.
///
## Özet { #recap }
Form verisi girdi parametrelerini tanımlamak için `Form` kullanın.
================================================
FILE: docs/tr/docs/tutorial/response-model.md
================================================
# Response Model - Dönüş Tipi { #response-model-return-type }
*Path operation function* **dönüş tipini** (return type) type annotation ile belirtip response için kullanılacak tipi tanımlayabilirsiniz.
Fonksiyon **parametreleri** için input data’da kullandığınız **type annotations** yaklaşımının aynısını burada da kullanabilirsiniz; Pydantic model’leri, list’ler, dict’ler, integer, boolean gibi skaler değerler vb.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI bu dönüş tipini şunlar için kullanır:
* Dönen veriyi **doğrulamak** (validate).
* Veri geçersizse (ör. bir field eksikse), bu *sizin* uygulama kodunuzun bozuk olduğu, olması gerekeni döndürmediği anlamına gelir; bu yüzden yanlış veri döndürmek yerine server error döner. Böylece siz ve client’larınız, beklenen veri ve veri şeklinin geleceğinden emin olabilirsiniz.
* OpenAPI’deki *path operation* içine response için bir **JSON Schema** eklemek.
* Bu, **otomatik dokümantasyon** tarafından kullanılır.
* Ayrıca otomatik client code generation araçları tarafından da kullanılır.
* Dönen veriyi Pydantic kullanarak JSON’a **serileştirmek**; Pydantic **Rust** ile yazıldığı için **çok daha hızlıdır**.
Ama en önemlisi:
* Çıktı verisini, dönüş tipinde tanımlı olana göre **sınırlar ve filtreler**.
* Bu, özellikle **güvenlik** açısından önemlidir; aşağıda daha fazlasını göreceğiz.
## `response_model` Parametresi { #response-model-parameter }
Bazı durumlarda, tam olarak dönüş tipinin söylediği gibi olmayan bir veriyi döndürmeniz gerekebilir ya da isteyebilirsiniz.
Örneğin, **bir dict** veya bir veritabanı objesi döndürmek isteyip, ama **onu bir Pydantic model olarak declare etmek** isteyebilirsiniz. Böylece Pydantic model, döndürdüğünüz obje (ör. dict veya veritabanı objesi) için dokümantasyon, doğrulama vb. işlerin tamamını yapar.
Eğer dönüş tipi annotation’ını eklerseniz, araçlar ve editörler (doğru şekilde) fonksiyonunuzun, declare ettiğiniz tipten (ör. Pydantic model) farklı bir tip (ör. dict) döndürdüğünü söyleyip hata verir.
Bu gibi durumlarda, dönüş tipi yerine *path operation decorator* parametresi olan `response_model`’i kullanabilirsiniz.
`response_model` parametresini herhangi bir *path operation* içinde kullanabilirsiniz:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* vb.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Not
`response_model`’in "decorator" metodunun (`get`, `post` vb.) bir parametresi olduğuna dikkat edin. Body ve diğer parametreler gibi, sizin *path operation function*’ınızın parametresi değildir.
///
`response_model`, Pydantic model field’ı için declare edeceğiniz aynı tipi alır; yani bir Pydantic model olabilir ama örneğin `List[Item]` gibi Pydantic model’lerden oluşan bir `list` de olabilir.
FastAPI bu `response_model`’i; dokümantasyon, doğrulama vb. her şey için ve ayrıca çıktı verisini **tip tanımına göre dönüştürmek ve filtrelemek** için kullanır.
/// tip | İpucu
Editörünüzde, mypy vb. ile sıkı type kontrolü yapıyorsanız, fonksiyon dönüş tipini `Any` olarak declare edebilirsiniz.
Böylece editöre bilerek her şeyi döndürebileceğinizi söylemiş olursunuz. Ancak FastAPI, `response_model` ile dokümantasyon, doğrulama, filtreleme vb. işlemleri yine de yapar.
///
### `response_model` Önceliği { #response-model-priority }
Hem dönüş tipi hem de `response_model` declare ederseniz, FastAPI’de `response_model` önceliklidir ve o kullanılır.
Böylece, response model’den farklı bir tip döndürdüğünüz durumlarda bile editör ve mypy gibi araçlar için fonksiyonlarınıza doğru type annotation’lar ekleyebilir, aynı zamanda FastAPI’nin `response_model` üzerinden veri doğrulama, dokümantasyon vb. yapmasını sağlayabilirsiniz.
Ayrıca `response_model=None` kullanarak, ilgili *path operation* için response model oluşturulmasını devre dışı bırakabilirsiniz. Bu, Pydantic field’ı olarak geçerli olmayan şeyler için type annotation eklediğinizde gerekebilir; aşağıdaki bölümlerden birinde bunun örneğini göreceksiniz.
## Aynı input verisini geri döndürmek { #return-the-same-input-data }
Burada `UserIn` adında bir model declare ediyoruz; bu model plaintext bir password içerecek:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Bilgi
`EmailStr` kullanmak için önce [`email-validator`](https://github.com/JoshData/python-email-validator) paketini kurun.
Bir [virtual environment](../virtual-environments.md) oluşturduğunuzdan, onu aktive ettiğinizden emin olun ve ardından örneğin şöyle kurun:
```console
$ pip install email-validator
```
veya şöyle:
```console
$ pip install "pydantic[email]"
```
///
Bu model ile hem input’u declare ediyoruz hem de output’u aynı model ile declare ediyoruz:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Artık bir browser password ile user oluşturduğunda, API response içinde aynı password’ü geri döndürecek.
Bu örnekte sorun olmayabilir; çünkü password’ü gönderen kullanıcı zaten aynı kişi.
Ancak aynı modeli başka bir *path operation* için kullanırsak, kullanıcının password’lerini her client’a gönderiyor olabiliriz.
/// danger
Tüm riskleri bildiğinizden ve ne yaptığınızdan emin olmadığınız sürece, bir kullanıcının plain password’ünü asla saklamayın ve bu şekilde response içinde göndermeyin.
///
## Bir output modeli ekleyin { #add-an-output-model }
Bunun yerine, plaintext password içeren bir input modeli ve password’ü içermeyen bir output modeli oluşturabiliriz:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Burada *path operation function* password içeren aynı input user’ı döndürüyor olsa bile:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...`response_model` olarak, password’ü içermeyen `UserOut` modelimizi declare ettik:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Dolayısıyla **FastAPI**, output model’de declare edilmemiş tüm verileri (Pydantic kullanarak) filtrelemekle ilgilenir.
### `response_model` mi Return Type mı? { #response-model-or-return-type }
Bu durumda iki model farklı olduğu için fonksiyon dönüş tipini `UserOut` olarak annotate etseydik, editör ve araçlar farklı class’lar olduğu için geçersiz bir tip döndürdüğümüzü söyleyip hata verecekti.
Bu yüzden bu örnekte `response_model` parametresinde declare etmek zorundayız.
...ama bunu nasıl aşabileceğinizi görmek için aşağıyı okumaya devam edin.
## Return Type ve Veri Filtreleme { #return-type-and-data-filtering }
Önceki örnekten devam edelim. Fonksiyonu **tek bir tip ile annotate etmek** istiyoruz; ama fonksiyondan gerçekte **daha fazla veri** içeren bir şey döndürebilmek istiyoruz.
FastAPI’nin response model’i kullanarak veriyi **filtrelemeye** devam etmesini istiyoruz. Yani fonksiyon daha fazla veri döndürse bile response, sadece response model’de declare edilmiş field’ları içersin.
Önceki örnekte class’lar farklı olduğu için `response_model` parametresini kullanmak zorundaydık. Ancak bu, editör ve araçların fonksiyon dönüş tipi kontrolünden gelen desteğini alamadığımız anlamına da geliyor.
Ama bu tarz durumların çoğunda modelin amacı, bu örnekteki gibi bazı verileri **filtrelemek/kaldırmak** olur.
Bu gibi durumlarda class’lar ve inheritance kullanarak, fonksiyon **type annotations** sayesinde editör ve araçlarda daha iyi destek alabilir, aynı zamanda FastAPI’nin **veri filtrelemesini** de koruyabiliriz.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Bununla birlikte, code type’lar açısından doğru olduğu için editörler ve mypy araç desteği verir; ayrıca FastAPI’den veri filtrelemeyi de alırız.
Bu nasıl çalışıyor? Bir bakalım. 🤓
### Type Annotations ve Araç Desteği { #type-annotations-and-tooling }
Önce editörler, mypy ve diğer araçlar bunu nasıl görür, ona bakalım.
`BaseUser` temel field’lara sahiptir. Ardından `UserIn`, `BaseUser`’dan miras alır ve `password` field’ını ekler; yani iki modelin field’larının tamamını içerir.
Fonksiyonun dönüş tipini `BaseUser` olarak annotate ediyoruz ama gerçekte bir `UserIn` instance’ı döndürüyoruz.
Editör, mypy ve diğer araçlar buna itiraz etmez; çünkü typing açısından `UserIn`, `BaseUser`’ın subclass’ıdır. Bu da, bir `BaseUser` bekleniyorken `UserIn`’in *geçerli* bir tip olduğu anlamına gelir.
### FastAPI Veri Filtreleme { #fastapi-data-filtering }
FastAPI açısından ise dönüş tipini görür ve döndürdüğünüz şeyin **yalnızca** tipte declare edilen field’ları içerdiğinden emin olur.
FastAPI, Pydantic ile içeride birkaç işlem yapar; böylece class inheritance kurallarının dönen veri filtrelemede aynen kullanılmasına izin vermez. Aksi halde beklediğinizden çok daha fazla veriyi response’ta döndürebilirdiniz.
Bu sayede iki dünyanın da en iyisini alırsınız: **araç desteği** veren type annotations ve **veri filtreleme**.
## Dokümanlarda görün { #see-it-in-the-docs }
Otomatik dokümanları gördüğünüzde, input model ve output model’in her birinin kendi JSON Schema’sına sahip olduğunu kontrol edebilirsiniz:
Ve her iki model de etkileşimli API dokümantasyonunda kullanılır:
## Diğer Return Type Annotation’ları { #other-return-type-annotations }
Bazı durumlarda Pydantic field olarak geçerli olmayan bir şey döndürebilir ve bunu fonksiyonda annotate edebilirsiniz; amaç sadece araçların (editör, mypy vb.) sağladığı desteği almaktır.
### Doğrudan Response Döndürmek { #return-a-response-directly }
En yaygın durum, [ileri seviye dokümanlarda daha sonra anlatıldığı gibi doğrudan bir Response döndürmektir](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Bu basit durum FastAPI tarafından otomatik olarak ele alınır; çünkü dönüş tipi annotation’ı `Response` class’ıdır (veya onun bir subclass’ı).
Araçlar da memnun olur; çünkü hem `RedirectResponse` hem `JSONResponse`, `Response`’un subclass’ıdır. Yani type annotation doğrudur.
### Bir Response Subclass’ını Annotate Etmek { #annotate-a-response-subclass }
Type annotation içinde `Response`’un bir subclass’ını da kullanabilirsiniz:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Bu da çalışır; çünkü `RedirectResponse`, `Response`’un subclass’ıdır ve FastAPI bu basit durumu otomatik olarak yönetir.
### Geçersiz Return Type Annotation’ları { #invalid-return-type-annotations }
Ancak geçerli bir Pydantic tipi olmayan başka rastgele bir obje (ör. bir veritabanı objesi) döndürür ve fonksiyonu da öyle annotate ederseniz, FastAPI bu type annotation’dan bir Pydantic response model oluşturmaya çalışır ve başarısız olur.
Aynı şey, farklı tipler arasında bir birleşim kullandığınızda ve bu tiplerden biri veya birkaçı geçerli bir Pydantic tipi değilse de olur; örneğin şu kullanım patlar 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...bu, type annotation Pydantic tipi olmadığı ve tek bir `Response` class’ı (veya subclass’ı) olmadığı için başarısız olur; bu, bir `Response` ile bir `dict` arasında union’dır (ikiden herhangi biri).
### Response Model’i Devre Dışı Bırakmak { #disable-response-model }
Yukarıdaki örnekten devam edersek; FastAPI’nin varsayılan olarak yaptığı veri doğrulama, dokümantasyon, filtreleme vb. işlemleri istemiyor olabilirsiniz.
Ancak yine de editörler ve type checker’lar (ör. mypy) gibi araçların desteğini almak için fonksiyonda dönüş tipi annotation’ını korumak isteyebilirsiniz.
Bu durumda `response_model=None` ayarlayarak response model üretimini devre dışı bırakabilirsiniz:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Bu, FastAPI’nin response model üretimini atlamasını sağlar; böylece FastAPI uygulamanızı etkilemeden ihtiyacınız olan herhangi bir return type annotation’ını kullanabilirsiniz. 🤓
## Response Model encoding parametreleri { #response-model-encoding-parameters }
Response model’inizde şu şekilde default değerler olabilir:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (veya Python 3.10’da `str | None = None`) için default `None`’dır.
* `tax: float = 10.5` için default `10.5`’tir.
* `tags: List[str] = []` için default, boş bir list’tir: `[]`.
Ancak gerçekte kaydedilmedilerse, bunları sonuçtan çıkarmak isteyebilirsiniz.
Örneğin NoSQL veritabanında çok sayıda optional attribute içeren modelleriniz varsa, default değerlerle dolu çok uzun JSON response’ları göndermek istemeyebilirsiniz.
### `response_model_exclude_unset` parametresini kullanın { #use-the-response-model-exclude-unset-parameter }
*Path operation decorator* parametresi olarak `response_model_exclude_unset=True` ayarlayabilirsiniz:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
böylece response’a default değerler dahil edilmez; yalnızca gerçekten set edilmiş değerler gelir.
Dolayısıyla ID’si `foo` olan item için bu *path operation*’a request atarsanız, response (default değerler olmadan) şöyle olur:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Bilgi
Ayrıca şunları da kullanabilirsiniz:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
Bunlar, `exclude_defaults` ve `exclude_none` için [Pydantic dokümanlarında](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) anlatıldığı gibidir.
///
#### Default’u olan field’lar için değer içeren data { #data-with-values-for-fields-with-defaults }
Ama data’nız modelde default değeri olan field’lar için değer içeriyorsa, örneğin ID’si `bar` olan item gibi:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
bunlar response’a dahil edilir.
#### Default değerlerle aynı değerlere sahip data { #data-with-the-same-values-as-the-defaults }
Eğer data, default değerlerle aynı değerlere sahipse, örneğin ID’si `baz` olan item gibi:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI yeterince akıllıdır (aslında Pydantic yeterince akıllıdır) ve `description`, `tax`, `tags` default ile aynı olsa bile bunların explicit olarak set edildiğini (default’tan alınmadığını) anlar.
Bu yüzden JSON response içinde yer alırlar.
/// tip | İpucu
Default değerlerin yalnızca `None` olmak zorunda olmadığını unutmayın.
Bir list (`[]`), `10.5` gibi bir `float` vb. olabilirler.
///
### `response_model_include` ve `response_model_exclude` { #response-model-include-and-response-model-exclude }
Ayrıca *path operation decorator* parametreleri `response_model_include` ve `response_model_exclude`’u da kullanabilirsiniz.
Bunlar; dahil edilecek attribute isimlerini (geri kalanını atlayarak) ya da hariç tutulacak attribute isimlerini (geri kalanını dahil ederek) belirten `str` değerlerinden oluşan bir `set` alır.
Tek bir Pydantic model’iniz varsa ve output’tan bazı verileri hızlıca çıkarmak istiyorsanız, bu yöntem pratik bir kısayol olabilir.
/// tip | İpucu
Ancak yine de, bu parametreler yerine yukarıdaki yaklaşımı (birden fazla class kullanmayı) tercih etmeniz önerilir.
Çünkü `response_model_include` veya `response_model_exclude` ile bazı attribute’ları atlıyor olsanız bile, uygulamanızın OpenAPI’sinde (ve dokümanlarda) üretilen JSON Schema hâlâ tam modelin JSON Schema’sı olacaktır.
Bu durum, benzer şekilde çalışan `response_model_by_alias` için de geçerlidir.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | İpucu
`{"name", "description"}` sözdizimi, bu iki değere sahip bir `set` oluşturur.
Bu, `set(["name", "description"])` ile eşdeğerdir.
///
#### `set` yerine `list` kullanmak { #using-lists-instead-of-sets }
Yanlışlıkla `set` yerine `list` veya `tuple` kullanırsanız, FastAPI bunu yine `set`’e çevirir ve doğru şekilde çalışır:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Özet { #recap }
Response model’leri tanımlamak ve özellikle private data’nın filtrelendiğinden emin olmak için *path operation decorator* parametresi `response_model`’i kullanın.
Yalnızca explicit olarak set edilmiş değerleri döndürmek için `response_model_exclude_unset` kullanın.
================================================
FILE: docs/tr/docs/tutorial/response-status-code.md
================================================
# Response Status Code { #response-status-code }
Bir response model tanımlayabildiğiniz gibi, herhangi bir *path operation* içinde `status_code` parametresiyle response için kullanılacak HTTP status code'u da belirtebilirsiniz:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* vb.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Not
`status_code`'un, "decorator" metodunun (`get`, `post`, vb.) bir parametresi olduğuna dikkat edin. Tüm parametreler ve body gibi, sizin *path operation function*'ınızın bir parametresi değildir.
///
`status_code` parametresi, HTTP status code'u içeren bir sayı alır.
/// info | Bilgi
Alternatif olarak `status_code`, Python'un [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus)'ı gibi bir `IntEnum` da alabilir.
///
Bu sayede:
* Response'da o status code döner.
* OpenAPI şemasında (dolayısıyla kullanıcı arayüzlerinde de) bu şekilde dokümante edilir:
/// note | Not
Bazı response code'lar (bir sonraki bölümde göreceğiz) response'un bir body'ye sahip olmadığını belirtir.
FastAPI bunu bilir ve response body olmadığını söyleyen OpenAPI dokümantasyonunu üretir.
///
## HTTP status code'lar hakkında { #about-http-status-codes }
/// note | Not
HTTP status code'ların ne olduğunu zaten biliyorsanız, bir sonraki bölüme geçin.
///
HTTP'de, response'un bir parçası olarak 3 basamaklı sayısal bir status code gönderirsiniz.
Bu status code'ların tanınmalarını sağlayan bir isimleri de vardır; ancak önemli olan kısım sayıdır.
Kısaca:
* `100 - 199` "Information" içindir. Doğrudan nadiren kullanırsınız. Bu status code'lara sahip response'lar body içeremez.
* **`200 - 299`** "Successful" response'lar içindir. En sık kullanacağınız aralık budur.
* `200`, varsayılan status code'dur ve her şeyin "OK" olduğunu ifade eder.
* Başka bir örnek `201` ("Created") olabilir. Genellikle veritabanında yeni bir kayıt oluşturduktan sonra kullanılır.
* Özel bir durum ise `204` ("No Content")'tür. Client'a döndürülecek içerik olmadığında kullanılır; bu nedenle response body olmamalıdır.
* **`300 - 399`** "Redirection" içindir. Bu status code'lara sahip response'lar, `304` ("Not Modified") hariç, body içerebilir de içermeyebilir de; `304` kesinlikle body içermemelidir.
* **`400 - 499`** "Client error" response'ları içindir. Muhtemelen en sık kullanacağınız ikinci aralık budur.
* Örneğin `404`, "Not Found" response'u içindir.
* Client kaynaklı genel hatalar için doğrudan `400` kullanabilirsiniz.
* `500 - 599` server hataları içindir. Neredeyse hiç doğrudan kullanmazsınız. Uygulama kodunuzun bir bölümünde ya da server'da bir şeyler ters giderse, otomatik olarak bu status code'lardan biri döner.
/// tip | İpucu
Her bir status code hakkında daha fazla bilgi almak ve hangi kodun ne için kullanıldığını görmek için [MDN dokümantasyonu: HTTP status code'lar hakkında](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)'na göz atın.
///
## İsimleri hatırlamak için kısayol { #shortcut-to-remember-the-names }
Önceki örneğe tekrar bakalım:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201`, "Created" için kullanılan status code'dur.
Ancak bu kodların her birinin ne anlama geldiğini ezberlemek zorunda değilsiniz.
`fastapi.status` içindeki kolaylık değişkenlerini (convenience variables) kullanabilirsiniz.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Bunlar sadece kolaylık sağlar; aynı sayıyı taşırlar. Ancak bu şekilde editörün autocomplete özelliğiyle kolayca bulabilirsiniz:
/// note | Teknik Detaylar
`from starlette import status` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.status`'u `fastapi.status` olarak da sunar. Ancak bu aslında doğrudan Starlette'den gelir.
///
## Varsayılanı değiştirmek { #changing-the-default }
Daha sonra, [İleri Düzey Kullanıcı Kılavuzu](../advanced/response-change-status-code.md) içinde, burada tanımladığınız varsayılanın dışında farklı bir status code nasıl döndüreceğinizi göreceksiniz.
================================================
FILE: docs/tr/docs/tutorial/schema-extra-example.md
================================================
# Request Örnek Verilerini Tanımlama { #declare-request-example-data }
Uygulamanızın alabileceği veriler için örnekler (examples) tanımlayabilirsiniz.
Bunu yapmanın birkaç yolu var.
## Pydantic modellerinde ek JSON Schema verisi { #extra-json-schema-data-in-pydantic-models }
Oluşturulan JSON Schema’ya eklenecek şekilde bir Pydantic model için `examples` tanımlayabilirsiniz.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Bu ek bilgi, o modelin çıktı **JSON Schema**’sına olduğu gibi eklenir ve API dokümanlarında kullanılır.
[Pydantic dokümanları: Configuration](https://docs.pydantic.dev/latest/api/config/) bölümünde anlatıldığı gibi, bir `dict` alan `model_config` niteliğini kullanabilirsiniz.
Üretilen JSON Schema’da görünmesini istediğiniz (ör. `examples` dahil) her türlü ek veriyi içeren bir `dict` ile `"json_schema_extra"` ayarlayabilirsiniz.
/// tip | İpucu
Aynı tekniği JSON Schema’yı genişletmek ve kendi özel ek bilgilerinizi eklemek için de kullanabilirsiniz.
Örneğin, bir frontend kullanıcı arayüzü için metadata eklemek vb. amaçlarla kullanılabilir.
///
/// info | Bilgi
OpenAPI 3.1.0 (FastAPI 0.99.0’dan beri kullanılıyor), **JSON Schema** standardının bir parçası olan `examples` için destek ekledi.
Bundan önce yalnızca tek bir örnek için `example` anahtar kelimesini destekliyordu. Bu hâlâ OpenAPI 3.1.0 tarafından desteklenir; ancak artık deprecated durumdadır ve JSON Schema standardının parçası değildir. Bu nedenle `example` kullanımını `examples`’a taşımanız önerilir. 🤓
Daha fazlasını sayfanın sonunda okuyabilirsiniz.
///
## `Field` ek argümanları { #field-additional-arguments }
Pydantic modelleriyle `Field()` kullanırken ek `examples` de tanımlayabilirsiniz:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## JSON Schema - OpenAPI içinde `examples` { #examples-in-json-schema-openapi }
Aşağıdakilerden herhangi birini kullanırken:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
OpenAPI içindeki **JSON Schema**’larına eklenecek ek bilgilerle birlikte bir `examples` grubu da tanımlayabilirsiniz.
### `examples` ile `Body` { #body-with-examples }
Burada `Body()` içinde beklenen veri için tek bir örnek içeren `examples` geçiriyoruz:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Doküman arayüzünde örnek { #example-in-the-docs-ui }
Yukarıdaki yöntemlerden herhangi biriyle `/docs` içinde şöyle görünür:
### Birden fazla `examples` ile `Body` { #body-with-multiple-examples }
Elbette birden fazla `examples` da geçebilirsiniz:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Bunu yaptığınızda, örnekler bu body verisi için dahili **JSON Schema**’nın bir parçası olur.
Buna rağmen, bu yazı yazılırken, doküman arayüzünü gösteren araç olan Swagger UI, **JSON Schema** içindeki veriler için birden fazla örneği göstermeyi desteklemiyor. Ancak aşağıda bir çözüm yolu var.
### OpenAPI’ye özel `examples` { #openapi-specific-examples }
**JSON Schema** `examples`’ı desteklemeden önce OpenAPI, yine `examples` adlı farklı bir alanı destekliyordu.
Bu **OpenAPI’ye özel** `examples`, OpenAPI spesifikasyonunda başka bir bölümde yer alır. Her bir JSON Schema’nın içinde değil, **her bir *path operation* detayları** içinde bulunur.
Swagger UI da bu özel `examples` alanını bir süredir destekliyor. Dolayısıyla bunu, **doküman arayüzünde** farklı **örnekleri göstermek** için kullanabilirsiniz.
OpenAPI’ye özel bu `examples` alanının şekli, (bir `list` yerine) **birden fazla örnek** içeren bir `dict`’tir; her örnek ayrıca **OpenAPI**’ye eklenecek ekstra bilgiler içerir.
Bu, OpenAPI’nin içerdiği JSON Schema’ların içine girmez; bunun yerine doğrudan *path operation* üzerinde, dışarıda yer alır.
### `openapi_examples` Parametresini Kullanma { #using-the-openapi-examples-parameter }
FastAPI’de OpenAPI’ye özel `examples`’ı, şu araçlar için `openapi_examples` parametresiyle tanımlayabilirsiniz:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
`dict`’in anahtarları her bir örneği tanımlar; her bir değer ise başka bir `dict`’tir.
`examples` içindeki her bir örnek `dict`’i şunları içerebilir:
* `summary`: Örnek için kısa açıklama.
* `description`: Markdown metni içerebilen uzun açıklama.
* `value`: Gösterilecek gerçek örnek (ör. bir `dict`).
* `externalValue`: `value`’a alternatif; örneğe işaret eden bir URL. Ancak bu, `value` kadar çok araç tarafından desteklenmiyor olabilir.
Şöyle kullanabilirsiniz:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Doküman Arayüzünde OpenAPI Örnekleri { #openapi-examples-in-the-docs-ui }
`Body()`’ye `openapi_examples` eklendiğinde `/docs` şöyle görünür:
## Teknik Detaylar { #technical-details }
/// tip | İpucu
Zaten **FastAPI** sürümü **0.99.0 veya üzerini** kullanıyorsanız, büyük olasılıkla bu detayları **atlanabilirsiniz**.
Bunlar daha çok OpenAPI 3.1.0’ın henüz mevcut olmadığı eski sürümler için geçerlidir.
Bunu kısa bir OpenAPI ve JSON Schema **tarih dersi** gibi düşünebilirsiniz. 🤓
///
/// warning | Uyarı
Bunlar **JSON Schema** ve **OpenAPI** standartları hakkında oldukça teknik detaylardır.
Yukarıdaki fikirler sizin için zaten çalışıyorsa bu kadarı yeterli olabilir; muhtemelen bu detaylara ihtiyacınız yoktur, gönül rahatlığıyla atlayabilirsiniz.
///
OpenAPI 3.1.0’dan önce OpenAPI, **JSON Schema**’nın daha eski ve değiştirilmiş bir sürümünü kullanıyordu.
JSON Schema’da `examples` yoktu; bu yüzden OpenAPI, değiştirilmiş sürümüne kendi `example` alanını ekledi.
OpenAPI ayrıca spesifikasyonun diğer bölümlerine de `example` ve `examples` alanlarını ekledi:
* [`Parameter Object` (spesifikasyonda)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object) — FastAPI’de şunlar tarafından kullanılıyordu:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`; `content` alanında, `Media Type Object` üzerinde (spesifikasyonda)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object) — FastAPI’de şunlar tarafından kullanılıyordu:
* `Body()`
* `File()`
* `Form()`
/// info | Bilgi
Bu eski OpenAPI’ye özel `examples` parametresi, FastAPI `0.103.0` sürümünden beri `openapi_examples` olarak kullanılıyor.
///
### JSON Schema’nın `examples` alanı { #json-schemas-examples-field }
Sonrasında JSON Schema, spesifikasyonun yeni bir sürümüne [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) alanını ekledi.
Ardından yeni OpenAPI 3.1.0, bu yeni `examples` alanını içeren en güncel sürümü (JSON Schema 2020-12) temel aldı.
Ve artık, deprecated olan eski tekil (ve özel) `example` alanına kıyasla bu yeni `examples` alanı önceliklidir.
JSON Schema’daki bu yeni `examples` alanı, OpenAPI’de başka yerlerde kullanılan (yukarıda anlatılan) metadata’lı `dict` yapısından farklı olarak **sadece örneklerden oluşan bir `list`**’tir.
/// info | Bilgi
OpenAPI 3.1.0, JSON Schema ile bu yeni ve daha basit entegrasyonla yayımlandıktan sonra bile bir süre, otomatik dokümantasyonu sağlayan araç Swagger UI OpenAPI 3.1.0’ı desteklemiyordu (5.0.0 sürümünden beri destekliyor 🎉).
Bu nedenle, FastAPI’nin 0.99.0 öncesi sürümleri OpenAPI 3.1.0’dan daha düşük sürümleri kullanmaya devam etti.
///
### Pydantic ve FastAPI `examples` { #pydantic-and-fastapi-examples }
Bir Pydantic modelinin içine `schema_extra` ya da `Field(examples=["something"])` kullanarak `examples` eklediğinizde, bu örnek o Pydantic modelinin **JSON Schema**’sına eklenir.
Ve Pydantic modelinin bu **JSON Schema**’sı, API’nizin **OpenAPI**’sine dahil edilir; ardından doküman arayüzünde kullanılır.
FastAPI 0.99.0’dan önceki sürümlerde (0.99.0 ve üzeri daha yeni OpenAPI 3.1.0’ı kullanır) `Query()`, `Body()` vb. diğer araçlarla `example` veya `examples` kullandığınızda, bu örnekler o veriyi tanımlayan JSON Schema’ya (OpenAPI’nin kendi JSON Schema sürümüne bile) eklenmiyordu; bunun yerine doğrudan OpenAPI’deki *path operation* tanımına ekleniyordu (JSON Schema kullanan OpenAPI bölümlerinin dışında).
Ancak artık FastAPI 0.99.0 ve üzeri OpenAPI 3.1.0 kullandığı (JSON Schema 2020-12) ve Swagger UI 5.0.0 ve üzeriyle birlikte, her şey daha tutarlı ve örnekler JSON Schema’ya dahil ediliyor.
### Swagger UI ve OpenAPI’ye özel `examples` { #swagger-ui-and-openapi-specific-examples }
Swagger UI (2023-08-26 itibarıyla) birden fazla JSON Schema örneğini desteklemediği için, kullanıcıların dokümanlarda birden fazla örnek göstermesi mümkün değildi.
Bunu çözmek için FastAPI `0.103.0`, yeni `openapi_examples` parametresiyle aynı eski **OpenAPI’ye özel** `examples` alanını tanımlamayı **desteklemeye başladı**. 🤓
### Özet { #summary }
Eskiden tarihten pek hoşlanmadığımı söylerdim... şimdi bakın, "teknoloji tarihi" dersi anlatıyorum. 😅
Kısacası, **FastAPI 0.99.0 veya üzerine yükseltin**; her şey çok daha **basit, tutarlı ve sezgisel** olur ve bu tarihsel detayların hiçbirini bilmeniz gerekmez. 😎
================================================
FILE: docs/tr/docs/tutorial/security/first-steps.md
================================================
# Güvenlik - İlk Adımlar { #security-first-steps }
**backend** API’nizin bir domain’de olduğunu düşünelim.
Ve başka bir domain’de ya da aynı domain’in farklı bir path’inde (veya bir mobil uygulamada) bir **frontend**’iniz var.
Ve frontend’in, **username** ve **password** kullanarak backend ile kimlik doğrulaması yapabilmesini istiyorsunuz.
Bunu **FastAPI** ile **OAuth2** kullanarak oluşturabiliriz.
Ama ihtiyacınız olan küçük bilgi parçalarını bulmak için uzun spesifikasyonun tamamını okuma zahmetine girmeyelim.
Güvenliği yönetmek için **FastAPI**’nin sunduğu araçları kullanalım.
## Nasıl Görünüyor { #how-it-looks }
Önce kodu kullanıp nasıl çalıştığına bakalım, sonra neler olup bittiğini anlamak için geri döneriz.
## `main.py` Oluşturun { #create-main-py }
Örneği `main.py` adlı bir dosyaya kopyalayın:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Çalıştırın { #run-it }
/// info | Bilgi
The [`python-multipart`](https://github.com/Kludex/python-multipart) paketi, `pip install "fastapi[standard]"` komutunu çalıştırdığınızda **FastAPI** ile birlikte otomatik olarak kurulur.
Ancak `pip install fastapi` komutunu kullanırsanız, `python-multipart` paketi varsayılan olarak dahil edilmez.
Elle kurmak için bir [virtual environment](../../virtual-environments.md) oluşturduğunuzdan, onu aktive ettiğinizden emin olun ve ardından şununla kurun:
```console
$ pip install python-multipart
```
Bunun nedeni, **OAuth2**’nin `username` ve `password` göndermek için "form data" kullanmasıdır.
///
Örneği şu şekilde çalıştırın:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Kontrol Edin { #check-it }
Etkileşimli dokümantasyona gidin: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Şuna benzer bir şey göreceksiniz:
/// check | Authorize butonu!
Artık parıl parıl yeni bir "Authorize" butonunuz var.
Ayrıca *path operation*’ınızın sağ üst köşesinde tıklayabileceğiniz küçük bir kilit simgesi de bulunuyor.
///
Ve ona tıklarsanız, `username` ve `password` (ve diğer opsiyonel alanları) girebileceğiniz küçük bir yetkilendirme formu görürsünüz:
/// note | Not
Formda ne yazdığınızın önemi yok; şimdilik çalışmayacak. Ama birazdan oraya da geleceğiz.
///
Bu, elbette son kullanıcılar için bir frontend değil; ancak tüm API’nizi etkileşimli şekilde belgelemek için harika bir otomatik araçtır.
Frontend ekibi tarafından kullanılabilir (bu ekip siz de olabilirsiniz).
Üçüncü taraf uygulamalar ve sistemler tarafından kullanılabilir.
Ve aynı uygulamayı debug etmek, kontrol etmek ve test etmek için sizin tarafınızdan da kullanılabilir.
## `password` Flow { #the-password-flow }
Şimdi biraz geri dönüp bunların ne olduğuna bakalım.
`password` "flow"u, OAuth2’de güvenlik ve authentication’ı yönetmek için tanımlanmış yöntemlerden ("flow"lardan) biridir.
OAuth2, backend’in veya API’nin, kullanıcıyı authenticate eden server’dan bağımsız olabilmesi için tasarlanmıştır.
Ancak bu örnekte, aynı **FastAPI** uygulaması hem API’yi hem de authentication’ı yönetecek.
O yüzden basitleştirilmiş bu bakış açısından üzerinden geçelim:
* Kullanıcı frontend’de `username` ve `password` yazar ve `Enter`’a basar.
* Frontend (kullanıcının browser’ında çalışır), bu `username` ve `password` değerlerini API’mizdeki belirli bir URL’ye gönderir (`tokenUrl="token"` ile tanımlanan).
* API, `username` ve `password` değerlerini kontrol eder ve bir "token" ile response döner (henüz bunların hiçbirini implement etmedik).
* "Token", daha sonra bu kullanıcıyı doğrulamak için kullanabileceğimiz içerik taşıyan bir string’dir.
* Normalde token’ın bir süre sonra süresi dolacak şekilde ayarlanması beklenir.
* Böylece kullanıcının bir noktada tekrar giriş yapması gerekir.
* Ayrıca token çalınırsa risk daha düşük olur. Çoğu durumda, sonsuza kadar çalışacak kalıcı bir anahtar gibi değildir.
* Frontend bu token’ı geçici olarak bir yerde saklar.
* Kullanıcı frontend’de tıklayarak web uygulamasının başka bir bölümüne gider.
* Frontend’in API’den daha fazla veri alması gerekir.
* Ancak o endpoint için authentication gereklidir.
* Bu yüzden API’mizle authenticate olmak için `Authorization` header’ını, `Bearer ` + token değeriyle gönderir.
* Token `foobar` içeriyorsa `Authorization` header’ının içeriği `Bearer foobar` olur.
## **FastAPI**’nin `OAuth2PasswordBearer`’ı { #fastapis-oauth2passwordbearer }
**FastAPI**, bu güvenlik özelliklerini implement etmek için farklı soyutlama seviyelerinde çeşitli araçlar sağlar.
Bu örnekte **OAuth2**’yi, **Password** flow ile, **Bearer** token kullanarak uygulayacağız. Bunu `OAuth2PasswordBearer` sınıfı ile yaparız.
/// info | Bilgi
"Bearer" token tek seçenek değildir.
Ama bizim kullanım senaryomuz için en iyi seçenek odur.
Ayrıca bir OAuth2 uzmanı değilseniz ve ihtiyaçlarınıza daha uygun başka bir seçeneğin neden gerekli olduğunu net olarak bilmiyorsanız, çoğu kullanım senaryosu için de en uygun seçenek olacaktır.
Bu durumda bile **FastAPI**, onu oluşturabilmeniz için gereken araçları sunar.
///
`OAuth2PasswordBearer` sınıfının bir instance’ını oluştururken `tokenUrl` parametresini veririz. Bu parametre, client’ın (kullanıcının browser’ında çalışan frontend’in) token almak için `username` ve `password` göndereceği URL’yi içerir.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | İpucu
Burada `tokenUrl="token"`, henüz oluşturmadığımız göreli bir URL olan `token`’ı ifade eder. Göreli URL olduğu için `./token` ile eşdeğerdir.
Göreli URL kullandığımız için, API’niz `https://example.com/` adresinde olsaydı `https://example.com/token` anlamına gelirdi. Ama API’niz `https://example.com/api/v1/` adresinde olsaydı, bu kez `https://example.com/api/v1/token` anlamına gelirdi.
Göreli URL kullanmak, [Behind a Proxy](../../advanced/behind-a-proxy.md) gibi daha ileri kullanım senaryolarında bile uygulamanızın çalışmaya devam etmesini garanti etmek açısından önemlidir.
///
Bu parametre o endpoint’i / *path operation*’ı oluşturmaz; fakat `/token` URL’sinin client’ın token almak için kullanması gereken URL olduğunu bildirir. Bu bilgi OpenAPI’de, dolayısıyla etkileşimli API dokümantasyon sistemlerinde kullanılır.
Birazdan gerçek path operation’ı da oluşturacağız.
/// info | Teknik Detaylar
Eğer çok katı bir "Pythonista" iseniz, `token_url` yerine `tokenUrl` şeklindeki parametre adlandırma stilini sevmeyebilirsiniz.
Bunun nedeni, OpenAPI spesifikasyonundaki isimle aynı adın kullanılmasıdır. Böylece bu güvenlik şemalarından herhangi biri hakkında daha fazla araştırma yapmanız gerekirse, adı kopyalayıp yapıştırarak kolayca daha fazla bilgi bulabilirsiniz.
///
`oauth2_scheme` değişkeni, `OAuth2PasswordBearer`’ın bir instance’ıdır; ama aynı zamanda "callable"dır.
Şu şekilde çağrılabilir:
```Python
oauth2_scheme(some, parameters)
```
Dolayısıyla `Depends` ile kullanılabilir.
### Kullanın { #use-it }
Artık `Depends` ile bir dependency olarak `oauth2_scheme`’i geçebilirsiniz.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Bu dependency, *path operation function* içindeki `token` parametresine atanacak bir `str` sağlar.
**FastAPI**, bu dependency’yi OpenAPI şemasında (ve otomatik API dokümanlarında) bir "security scheme" tanımlamak için kullanabileceğini bilir.
/// info | Teknik Detaylar
**FastAPI**, bir dependency içinde tanımlanan `OAuth2PasswordBearer` sınıfını OpenAPI’de security scheme tanımlamak için kullanabileceğini bilir; çünkü bu sınıf `fastapi.security.oauth2.OAuth2`’den kalıtım alır, o da `fastapi.security.base.SecurityBase`’den kalıtım alır.
OpenAPI (ve otomatik API dokümanları) ile entegre olan tüm security araçları `SecurityBase`’den kalıtım alır; **FastAPI** bu sayede onları OpenAPI’ye nasıl entegre edeceğini anlayabilir.
///
## Ne Yapar { #what-it-does }
Request içinde `Authorization` header’ını arar, değerin `Bearer ` + bir token olup olmadığını kontrol eder ve token’ı `str` olarak döndürür.
Eğer `Authorization` header’ını görmezse ya da değer `Bearer ` token’ı içermiyorsa, doğrudan 401 status code hatasıyla (`UNAUTHORIZED`) response döner.
Token’ın var olup olmadığını kontrol edip ayrıca hata döndürmenize bile gerek yoktur. Fonksiyonunuz çalışıyorsa, token içinde bir `str` olacağından emin olabilirsiniz.
Bunu şimdiden etkileşimli dokümanlarda deneyebilirsiniz:
Henüz token’ın geçerliliğini doğrulamıyoruz, ama başlangıç için bu bile yeterli.
## Özet { #recap }
Yani sadece 3 veya 4 ekstra satırla, şimdiden ilkel de olsa bir güvenlik katmanı elde etmiş oldunuz.
================================================
FILE: docs/tr/docs/tutorial/security/get-current-user.md
================================================
# Mevcut Kullanıcıyı Alma { #get-current-user }
Önceki bölümde güvenlik sistemi (dependency injection sistemine dayanır) *path operation function*'a `str` olarak bir `token` veriyordu:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Ancak bu hâlâ pek kullanışlı değil.
Bize mevcut kullanıcıyı verecek şekilde düzenleyelim.
## Bir kullanıcı modeli oluşturun { #create-a-user-model }
Önce bir Pydantic kullanıcı modeli oluşturalım.
Body'leri bildirmek için Pydantic'i nasıl kullanıyorsak, aynı şekilde onu başka her yerde de kullanabiliriz:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## `get_current_user` dependency'si oluşturun { #create-a-get-current-user-dependency }
Bir `get_current_user` dependency'si oluşturalım.
Dependency'lerin alt dependency'leri olabileceğini hatırlıyor musunuz?
`get_current_user`, daha önce oluşturduğumuz `oauth2_scheme` ile aynı dependency'yi kullanacak.
Daha önce *path operation* içinde doğrudan yaptığımız gibi, yeni dependency'miz `get_current_user`, alt dependency olan `oauth2_scheme` üzerinden `str` olarak bir `token` alacak:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Kullanıcıyı alın { #get-the-user }
`get_current_user`, oluşturduğumuz (sahte) bir yardımcı (utility) fonksiyonu kullanacak; bu fonksiyon `str` olarak bir token alır ve Pydantic `User` modelimizi döndürür:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Mevcut kullanıcıyı enjekte edin { #inject-the-current-user }
Artık *path operation* içinde `get_current_user` ile aynı `Depends` yaklaşımını kullanabiliriz:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
`current_user` tipini Pydantic `User` modeli olarak belirttiğimize dikkat edin.
Bu sayede fonksiyonun içinde otomatik tamamlama ve tip kontrollerinin tamamından faydalanırız.
/// tip | İpucu
Request body'lerinin de Pydantic modelleri ile bildirildiğini hatırlıyor olabilirsiniz.
Burada `Depends` kullandığınız için **FastAPI** karışıklık yaşamaz.
///
/// check | Ek bilgi
Bu dependency sisteminin tasarımı, hepsi `User` modeli döndüren farklı dependency'lere (farklı "dependable"lara) sahip olmamıza izin verir.
Bu tipte veri döndürebilen yalnızca tek bir dependency ile sınırlı değiliz.
///
## Diğer modeller { #other-models }
Artık *path operation function* içinde mevcut kullanıcıyı doğrudan alabilir ve güvenlik mekanizmalarını `Depends` kullanarak **Dependency Injection** seviyesinde yönetebilirsiniz.
Ayrıca güvenlik gereksinimleri için herhangi bir model veya veri kullanabilirsiniz (bu örnekte bir Pydantic `User` modeli).
Ancak belirli bir data model, class ya da type kullanmak zorunda değilsiniz.
Modelinizde bir `id` ve `email` olsun, ama `username` olmasın mı istiyorsunuz? Elbette. Aynı araçları kullanabilirsiniz.
Sadece bir `str` mı istiyorsunuz? Ya da sadece bir `dict`? Veya doğrudan bir veritabanı class model instance'ı? Hepsi aynı şekilde çalışır.
Uygulamanıza giriş yapan kullanıcılar yok da robotlar, botlar veya yalnızca bir access token'a sahip başka sistemler mi var? Yine, her şey aynı şekilde çalışır.
Uygulamanız için neye ihtiyacınız varsa o türden bir model, class ve veritabanı kullanın. **FastAPI**, dependency injection sistemiyle bunları destekler.
## Kod boyutu { #code-size }
Bu örnek biraz uzun görünebilir. Güvenlik, data model'ler, utility fonksiyonlar ve *path operation*'ları aynı dosyada bir araya getirdiğimizi unutmayın.
Ama kritik nokta şu:
Güvenlik ve dependency injection tarafını bir kez yazarsınız.
İstediğiniz kadar karmaşık hâle getirebilirsiniz. Yine de hepsi tek bir yerde ve sadece bir kez yazılmış olur. Üstelik tüm esneklikle.
Sonrasında aynı güvenlik sistemini kullanan binlerce endpoint (*path operation*) olabilir.
Ve bunların hepsi (ya da istediğiniz bir kısmı) bu dependency'leri veya oluşturacağınız başka dependency'leri yeniden kullanmaktan faydalanabilir.
Hatta bu binlerce *path operation* 3 satır kadar kısa olabilir:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Özet { #recap }
Artık *path operation function* içinde mevcut kullanıcıyı doğrudan alabilirsiniz.
Yolun yarısına geldik.
Kullanıcının/istemcinin gerçekten `username` ve `password` göndermesini sağlayacak bir *path operation* eklememiz gerekiyor.
Sırada bu var.
================================================
FILE: docs/tr/docs/tutorial/security/index.md
================================================
# Güvenlik { #security }
Güvenlik, authentication ve authorization’ı ele almanın birçok yolu vardır.
Ve bu konu genellikle karmaşık ve "zor"dur.
Birçok framework ve sistemde yalnızca security ve authentication’ı yönetmek bile ciddi miktarda emek ve kod gerektirir (çoğu durumda yazılan toplam kodun %50’si veya daha fazlası olabilir).
**FastAPI**, tüm security spesifikasyonlarını baştan sona inceleyip öğrenmek zorunda kalmadan **Security** konusunu kolay, hızlı ve standart bir şekilde ele almanıza yardımcı olacak çeşitli araçlar sunar.
Ama önce, küçük birkaç kavrama bakalım.
## Acelem var? { #in-a-hurry }
Bu terimlerin hiçbirini umursamıyorsanız ve sadece kullanıcı adı ve parola tabanlı authentication ile security’yi *hemen şimdi* eklemeniz gerekiyorsa, bir sonraki bölümlere geçin.
## OAuth2 { #oauth2 }
OAuth2, authentication ve authorization’ı yönetmek için çeşitli yöntemleri tanımlayan bir spesifikasyondur.
Oldukça kapsamlı bir spesifikasyondur ve birkaç karmaşık use case’i kapsar.
"Üçüncü taraf" kullanarak authentication yapmanın yollarını da içerir.
"Facebook, Google, X (Twitter), GitHub ile giriş yap" bulunan sistemlerin arka planda kullandığı şey de budur.
### OAuth 1 { #oauth-1 }
OAuth 1 de vardı; OAuth2’den çok farklıdır ve daha karmaşıktır, çünkü iletişimi nasıl şifreleyeceğinize dair doğrudan spesifikasyonlar içeriyordu.
Günümüzde pek popüler değildir veya pek kullanılmaz.
OAuth2 ise iletişimin nasıl şifreleneceğini belirtmez; uygulamanızın HTTPS ile sunulmasını bekler.
/// tip | İpucu
**deployment** bölümünde Traefik ve Let's Encrypt kullanarak ücretsiz şekilde HTTPS’i nasıl kuracağınızı göreceksiniz.
///
## OpenID Connect { #openid-connect }
OpenID Connect, **OAuth2** tabanlı başka bir spesifikasyondur.
OAuth2’de nispeten belirsiz kalan bazı noktaları tanımlayarak onu daha birlikte çalışabilir (interoperable) hâle getirmeye çalışır.
Örneğin, Google ile giriş OpenID Connect kullanır (arka planda OAuth2 kullanır).
Ancak Facebook ile giriş OpenID Connect’i desteklemez. Kendine özgü bir OAuth2 çeşidi vardır.
### OpenID ("OpenID Connect" değil) { #openid-not-openid-connect }
Bir de "OpenID" spesifikasyonu vardı. Bu da **OpenID Connect** ile aynı problemi çözmeye çalışıyordu ama OAuth2 tabanlı değildi.
Dolayısıyla tamamen ayrı, ek bir sistemdi.
Günümüzde pek popüler değildir veya pek kullanılmaz.
## OpenAPI { #openapi }
OpenAPI (önceden Swagger olarak biliniyordu), API’ler inşa etmek için açık spesifikasyondur (artık Linux Foundation’ın bir parçası).
**FastAPI**, **OpenAPI** tabanlıdır.
Bu sayede birden fazla otomatik etkileşimli dokümantasyon arayüzü, code generation vb. mümkün olur.
OpenAPI, birden fazla security "scheme" tanımlamanın bir yolunu sunar.
Bunları kullanarak, etkileşimli dokümantasyon sistemleri de dahil olmak üzere tüm bu standart tabanlı araçlardan faydalanabilirsiniz.
OpenAPI şu security scheme’lerini tanımlar:
* `apiKey`: uygulamaya özel bir anahtar; şuradan gelebilir:
* Bir query parameter.
* Bir header.
* Bir cookie.
* `http`: standart HTTP authentication sistemleri, örneğin:
* `bearer`: `Authorization` header’ı; değeri `Bearer ` + bir token olacak şekilde. Bu, OAuth2’den gelir.
* HTTP Basic authentication.
* HTTP Digest, vb.
* `oauth2`: OAuth2 ile security’yi yönetmenin tüm yolları ("flow" olarak adlandırılır).
* Bu flow’ların birçoğu, bir OAuth 2.0 authentication provider (Google, Facebook, X (Twitter), GitHub vb.) oluşturmak için uygundur:
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Ancak, aynı uygulamanın içinde doğrudan authentication yönetmek için mükemmel şekilde kullanılabilecek özel bir "flow" vardır:
* `password`: sonraki bazı bölümlerde bunun örnekleri ele alınacak.
* `openIdConnect`: OAuth2 authentication verisinin otomatik olarak nasıl keşfedileceğini tanımlamanın bir yolunu sunar.
* Bu otomatik keşif, OpenID Connect spesifikasyonunda tanımlanan şeydir.
/// tip | İpucu
Google, Facebook, X (Twitter), GitHub vb. gibi diğer authentication/authorization provider’larını entegre etmek de mümkündür ve nispeten kolaydır.
En karmaşık kısım, bu tür bir authentication/authorization provider’ı inşa etmektir; ancak **FastAPI** ağır işleri sizin yerinize yaparken bunu kolayca yapabilmeniz için araçlar sunar.
///
## **FastAPI** yardımcı araçları { #fastapi-utilities }
FastAPI, `fastapi.security` modülünde bu security scheme’lerinin her biri için, bu mekanizmaları kullanmayı kolaylaştıran çeşitli araçlar sağlar.
Sonraki bölümlerde, **FastAPI**’nin sunduğu bu araçları kullanarak API’nize nasıl security ekleyeceğinizi göreceksiniz.
Ayrıca bunun etkileşimli dokümantasyon sistemine nasıl otomatik olarak entegre edildiğini de göreceksiniz.
================================================
FILE: docs/tr/docs/tutorial/security/oauth2-jwt.md
================================================
# Password ile OAuth2 (ve hashing), JWT token'ları ile Bearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Artık tüm security flow elimizde olduğuna göre, uygulamayı gerçekten güvenli hâle getirelim: JWT token'ları ve güvenli password hashing kullanacağız.
Bu kodu uygulamanızda gerçekten kullanabilirsiniz; password hash'lerini veritabanınıza kaydedebilirsiniz, vb.
Bir önceki bölümde bıraktığımız yerden başlayıp üzerine ekleyerek ilerleyeceğiz.
## JWT Hakkında { #about-jwt }
JWT, "JSON Web Tokens" anlamına gelir.
Bir JSON nesnesini, boşluk içermeyen uzun ve yoğun bir string'e kodlamak için kullanılan bir standarttır. Şuna benzer:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Şifrelenmiş değildir; yani herkes içeriğindeki bilgiyi geri çıkarabilir.
Ancak imzalanmıştır. Bu yüzden, sizin ürettiğiniz bir token'ı aldığınızda, gerçekten onu sizin ürettiğinizi doğrulayabilirsiniz.
Bu şekilde, örneğin 1 haftalık süre sonu (expiration) olan bir token oluşturabilirsiniz. Sonra kullanıcı ertesi gün token ile geri geldiğinde, kullanıcının hâlâ sisteminizde oturum açmış olduğunu bilirsiniz.
Bir hafta sonra token'ın süresi dolar; kullanıcı yetkilendirilmez ve yeni bir token almak için tekrar giriş yapmak zorunda kalır. Ayrıca kullanıcı (veya üçüncü bir taraf) token'ı değiştirip süre sonunu farklı göstermek isterse bunu tespit edebilirsiniz; çünkü imzalar eşleşmez.
JWT token'larıyla oynayıp nasıl çalıştıklarını görmek isterseniz [https://jwt.io](https://jwt.io/) adresine bakın.
## `PyJWT` Kurulumu { #install-pyjwt }
Python'da JWT token'larını üretmek ve doğrulamak için `PyJWT` kurmamız gerekiyor.
Bir [sanal ortam](../../virtual-environments.md) oluşturduğunuzdan emin olun, aktif edin ve ardından `pyjwt` kurun:
```console
$ pip install pyjwt
---> 100%
```
/// info | Bilgi
RSA veya ECDSA gibi dijital imza algoritmaları kullanmayı planlıyorsanız, `pyjwt[crypto]` bağımlılığı olan `cryptography` kütüphanesini kurmalısınız.
Daha fazla bilgi için [PyJWT Kurulum dokümantasyonu](https://pyjwt.readthedocs.io/en/latest/installation.html) sayfasını okuyabilirsiniz.
///
## Password hashing { #password-hashing }
"Hashing", bazı içerikleri (bu örnekte bir password) anlamsız görünen bir bayt dizisine (pratikte bir string) dönüştürmek demektir.
Aynı içeriği (aynı password'ü) her seferinde verirseniz, her seferinde aynı anlamsız çıktıyı elde edersiniz.
Ancak bu anlamsız çıktıdan geri password'e dönüştürme yapılamaz.
### Neden password hashing kullanılır { #why-use-password-hashing }
Veritabanınız çalınırsa, hırsız kullanıcılarınızın düz metin (plaintext) password'lerini değil, sadece hash'leri elde eder.
Dolayısıyla, o password'ü başka bir sistemde denemek kolay olmaz (pek çok kullanıcı her yerde aynı password'ü kullandığı için bu tehlikeli olurdu).
## `pwdlib` Kurulumu { #install-pwdlib }
pwdlib, password hash'leriyle çalışmak için çok iyi bir Python paketidir.
Birçok güvenli hashing algoritmasını ve bunlarla çalışmak için yardımcı araçları destekler.
Önerilen algoritma "Argon2"dir.
Bir [sanal ortam](../../virtual-environments.md) oluşturduğunuzdan emin olun, aktif edin ve sonra Argon2 ile birlikte pwdlib'i kurun:
/// tip | İpucu
`pwdlib` ile, **Django** tarafından oluşturulmuş password'leri, bir **Flask** security eklentisinin ürettiklerini veya başka birçok kaynaktan gelen password'leri okuyabilecek şekilde bile yapılandırabilirsiniz.
Böylece örneğin bir Django uygulamasındaki verileri aynı veritabanında bir FastAPI uygulamasıyla paylaşabilirsiniz. Ya da aynı veritabanını kullanarak bir Django uygulamasını kademeli şekilde taşıyabilirsiniz.
Ayrıca kullanıcılarınız, aynı anda hem Django uygulamanızdan hem de **FastAPI** uygulamanızdan login olabilir.
///
## Password'leri hash'leme ve doğrulama { #hash-and-verify-the-passwords }
Gerekli araçları `pwdlib` içinden import edelim.
Önerilen ayarlarla bir PasswordHash instance'ı oluşturalım; bunu password'leri hash'lemek ve doğrulamak için kullanacağız.
/// tip | İpucu
pwdlib, bcrypt hashing algoritmasını da destekler; ancak legacy algoritmaları içermez. Eski hash'lerle çalışmak için passlib kütüphanesini kullanmanız önerilir.
Örneğin, başka bir sistemin (Django gibi) ürettiği password'leri okuyup doğrulayabilir, ancak yeni password'leri Argon2 veya Bcrypt gibi farklı bir algoritmayla hash'leyebilirsiniz.
Ve aynı anda hepsiyle uyumlu kalabilirsiniz.
///
Kullanıcıdan gelen password'ü hash'lemek için bir yardımcı (utility) fonksiyon oluşturalım.
Sonra, alınan password'ün kayıttaki hash ile eşleşip eşleşmediğini doğrulayan başka bir yardımcı fonksiyon yazalım.
Bir tane de kullanıcıyı authenticate edip geri döndüren bir yardımcı fonksiyon ekleyelim.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
`authenticate_user`, veritabanında var olmayan bir username ile çağrıldığında, yine de sahte (dummy) bir hash'e karşı `verify_password` çalıştırıyoruz.
Bu, username geçerli olsun ya da olmasın endpoint'in yaklaşık aynı sürede yanıt vermesini sağlar; böylece mevcut username'leri saymaya yarayabilecek zamanlama saldırılarını (timing attacks) engeller.
/// note | Not
Yeni (sahte) veritabanı `fake_users_db`'ye bakarsanız, hash'lenmiş password'ün artık nasıl göründüğünü görebilirsiniz: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## JWT token'larını yönetme { #handle-jwt-tokens }
Kurulu modülleri import edelim.
JWT token'larını imzalamak için kullanılacak rastgele bir secret key oluşturalım.
Güvenli, rastgele bir secret key üretmek için şu komutu kullanın:
Çıktıyı `SECRET_KEY` değişkenine kopyalayın (örnektekini kullanmayın).
JWT token'ını imzalamak için kullanılan algoritmayı tutacak `ALGORITHM` adlı bir değişken oluşturup değerini `"HS256"` yapın.
Token'ın süre sonu (expiration) için bir değişken oluşturun.
Response için token endpoint'inde kullanılacak bir Pydantic Model tanımlayın.
Yeni bir access token üretmek için bir yardımcı fonksiyon oluşturun.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Dependency'leri güncelleme { #update-the-dependencies }
`get_current_user` fonksiyonunu, öncekiyle aynı token'ı alacak şekilde güncelleyelim; ancak bu sefer JWT token'larını kullanacağız.
Gelen token'ı decode edin, doğrulayın ve mevcut kullanıcıyı döndürün.
Token geçersizse, hemen bir HTTP hatası döndürün.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## `/token` *path operation*'ını güncelleme { #update-the-token-path-operation }
Token'ın süre sonu için bir `timedelta` oluşturun.
Gerçek bir JWT access token üretip döndürün.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### JWT "subject" `sub` Hakkında Teknik Detaylar { #technical-details-about-the-jwt-subject-sub }
JWT spesifikasyonu, token'ın konusu (subject) için `sub` adlı bir anahtar olduğunu söyler.
Bunu kullanmak zorunlu değildir; ancak kullanıcı kimliğini koymak için uygun yer burasıdır, bu yüzden burada onu kullanıyoruz.
JWT, sadece bir kullanıcıyı tanımlamak ve API'nizde doğrudan işlem yapmasına izin vermek dışında başka amaçlarla da kullanılabilir.
Örneğin bir "araba"yı veya bir "blog post"u tanımlayabilirsiniz.
Sonra o varlık için izinler ekleyebilirsiniz; örneğin (araba için) "drive" ya da (blog için) "edit".
Ardından bu JWT token'ını bir kullanıcıya (veya bot'a) verebilirsiniz; onlar da, hesapları olmasına bile gerek kalmadan, sadece API'nizin bunun için ürettiği JWT token'ıyla bu aksiyonları gerçekleştirebilir (arabayı sürmek veya blog post'u düzenlemek gibi).
Bu fikirlerle JWT, çok daha gelişmiş senaryolarda kullanılabilir.
Bu durumlarda, birden fazla varlığın aynı ID'ye sahip olması mümkündür; örneğin `foo` (kullanıcı `foo`, araba `foo`, blog post `foo`).
Dolayısıyla ID çakışmalarını önlemek için, kullanıcı için JWT token oluştururken `sub` anahtarının değerine bir önek ekleyebilirsiniz; örneğin `username:`. Bu örnekte `sub` değeri şöyle olabilirdi: `username:johndoe`.
Unutmamanız gereken önemli nokta şudur: `sub` anahtarı, tüm uygulama genelinde benzersiz bir tanımlayıcı olmalı ve string olmalıdır.
## Kontrol Edelim { #check-it }
Server'ı çalıştırın ve docs'a gidin: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Şuna benzer bir arayüz göreceksiniz:
Uygulamayı, öncekiyle aynı şekilde authorize edin.
Şu kimlik bilgilerini kullanarak:
Username: `johndoe`
Password: `secret`
/// check | Ek bilgi
Kodun hiçbir yerinde düz metin password "`secret`" yok; sadece hash'lenmiş hâli var.
///
`/users/me/` endpoint'ini çağırın; response şöyle olacaktır:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Developer tools'u açarsanız, gönderilen verinin sadece token'ı içerdiğini görebilirsiniz. Password sadece kullanıcıyı authenticate edip access token almak için yapılan ilk request'te gönderilir, sonrasında gönderilmez:
/// note | Not
`Authorization` header'ına dikkat edin; değeri `Bearer ` ile başlıyor.
///
## `scopes` ile İleri Seviye Kullanım { #advanced-usage-with-scopes }
OAuth2'nin "scopes" kavramı vardır.
Bunları kullanarak bir JWT token'a belirli bir izin seti ekleyebilirsiniz.
Sonra bu token'ı bir kullanıcıya doğrudan veya bir üçüncü tarafa verip, API'nizle belirli kısıtlarla etkileşime girmesini sağlayabilirsiniz.
Nasıl kullanıldıklarını ve **FastAPI** ile nasıl entegre olduklarını, ileride **Advanced User Guide** içinde öğreneceksiniz.
## Özet { #recap }
Şimdiye kadar gördüklerinizle, OAuth2 ve JWT gibi standartları kullanarak güvenli bir **FastAPI** uygulaması kurabilirsiniz.
Neredeyse her framework'te security'yi ele almak oldukça hızlı bir şekilde karmaşık bir konu hâline gelir.
Bunu çok basitleştiren birçok paket, veri modeli, veritabanı ve mevcut özelliklerle ilgili pek çok ödün vermek zorunda kalır. Hatta bazıları işi aşırı basitleştirirken arka planda güvenlik açıkları da barındırır.
---
**FastAPI**, hiçbir veritabanı, veri modeli veya araç konusunda ödün vermez.
Projenize en uygun olanları seçebilmeniz için size tam esneklik sağlar.
Ayrıca `pwdlib` ve `PyJWT` gibi iyi bakımı yapılan ve yaygın kullanılan paketleri doğrudan kullanabilirsiniz; çünkü **FastAPI**, haricî paketleri entegre etmek için karmaşık mekanizmalara ihtiyaç duymaz.
Buna rağmen, esneklikten, sağlamlıktan veya güvenlikten ödün vermeden süreci mümkün olduğunca basitleştiren araçları sağlar.
Ve OAuth2 gibi güvenli, standart protokolleri nispeten basit bir şekilde kullanabilir ve uygulayabilirsiniz.
Aynı standartları izleyerek, daha ince taneli (fine-grained) bir izin sistemi için OAuth2 "scopes" kullanımını **Advanced User Guide** içinde daha detaylı öğrenebilirsiniz. Scopes'lu OAuth2; Facebook, Google, GitHub, Microsoft, X (Twitter) vb. pek çok büyük kimlik doğrulama sağlayıcısının, üçüncü taraf uygulamaların kullanıcıları adına API'leriyle etkileşebilmesine izin vermek için kullandığı mekanizmadır.
================================================
FILE: docs/tr/docs/tutorial/security/simple-oauth2.md
================================================
# Password ve Bearer ile Basit OAuth2 { #simple-oauth2-with-password-and-bearer }
Şimdi önceki bölümün üzerine inşa edip, eksik parçaları ekleyerek tam bir güvenlik akışı oluşturalım.
## `username` ve `password`’ü Alma { #get-the-username-and-password }
`username` ve `password`’ü almak için **FastAPI** security yardımcı araçlarını kullanacağız.
OAuth2, (bizim kullandığımız) "password flow" kullanılırken client/kullanıcının form verisi olarak `username` ve `password` alanlarını göndermesi gerektiğini belirtir.
Ayrıca spesifikasyon, bu alanların adlarının tam olarak böyle olması gerektiğini söyler. Yani `user-name` veya `email` işe yaramaz.
Ancak merak etmeyin, frontend’de son kullanıcılarınıza dilediğiniz gibi gösterebilirsiniz.
Veritabanı model(ler)inizde de istediğiniz başka isimleri kullanabilirsiniz.
Fakat login *path operation*’ı için, spesifikasyonla uyumlu olmak (ve örneğin entegre API dokümantasyon sistemini kullanabilmek) adına bu isimleri kullanmamız gerekiyor.
Spesifikasyon ayrıca `username` ve `password`’ün form verisi olarak gönderilmesi gerektiğini de söyler (yani burada JSON yok).
### `scope` { #scope }
Spesifikasyon, client’ın "`scope`" adlı başka bir form alanı da gönderebileceğini söyler.
Form alanının adı `scope`’tur (tekil), ama aslında boşluklarla ayrılmış "scope"’lardan oluşan uzun bir string’dir.
Her bir "scope" sadece bir string’dir (boşluk içermez).
Genelde belirli güvenlik izinlerini (permission) belirtmek için kullanılırlar, örneğin:
* `users:read` veya `users:write` yaygın örneklerdir.
* `instagram_basic` Facebook / Instagram tarafından kullanılır.
* `https://www.googleapis.com/auth/drive` Google tarafından kullanılır.
/// info | Bilgi
OAuth2’de bir "scope", gerekli olan belirli bir izni ifade eden basit bir string’dir.
`:` gibi başka karakterler içermesi veya URL olması önemli değildir.
Bu detaylar implementasyon’a özeldir.
OAuth2 açısından bunlar sadece string’lerdir.
///
## `username` ve `password`’ü Almak İçin Kod { #code-to-get-the-username-and-password }
Şimdi bunu yönetmek için **FastAPI**’nin sağladığı araçları kullanalım.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Önce `OAuth2PasswordRequestForm`’u import edin ve `/token` için *path operation* içinde `Depends` ile dependency olarak kullanın:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm`, şu alanları içeren bir form body tanımlayan bir class dependency’sidir:
* `username`.
* `password`.
* Boşlukla ayrılmış string’lerden oluşan büyük bir string olarak opsiyonel `scope` alanı.
* Opsiyonel `grant_type`.
/// tip | İpucu
OAuth2 spesifikasyonu aslında `grant_type` alanını sabit bir `password` değeriyle *zorunlu kılar*, ancak `OAuth2PasswordRequestForm` bunu zorlamaz.
Bunu zorlamak istiyorsanız, `OAuth2PasswordRequestForm` yerine `OAuth2PasswordRequestFormStrict` kullanın.
///
* Opsiyonel `client_id` (bu örnekte ihtiyacımız yok).
* Opsiyonel `client_secret` (bu örnekte ihtiyacımız yok).
/// info | Bilgi
`OAuth2PasswordRequestForm`, `OAuth2PasswordBearer` gibi **FastAPI**’ye özel “özel bir sınıf” değildir.
`OAuth2PasswordBearer`, bunun bir security scheme olduğunu **FastAPI**’ye bildirir. Bu yüzden OpenAPI’ye o şekilde eklenir.
Ama `OAuth2PasswordRequestForm` sadece bir class dependency’dir; bunu kendiniz de yazabilirdiniz ya da doğrudan `Form` parametreleri tanımlayabilirdiniz.
Fakat çok yaygın bir kullanım olduğu için **FastAPI** bunu işleri kolaylaştırmak adına doğrudan sağlar.
///
### Form Verisini Kullanma { #use-the-form-data }
/// tip | İpucu
`OAuth2PasswordRequestForm` dependency class’ının instance’ında boşluklarla ayrılmış uzun string olarak bir `scope` attribute’u olmaz; bunun yerine gönderilen her scope için gerçek string listesini içeren `scopes` attribute’u olur.
Bu örnekte `scopes` kullanmıyoruz, ama ihtiyacınız olursa bu özellik hazır.
///
Şimdi form alanındaki `username`’i kullanarak (sahte) veritabanından kullanıcı verisini alın.
Böyle bir kullanıcı yoksa, "Incorrect username or password" diyerek bir hata döndürelim.
Hata için `HTTPException` exception’ını kullanıyoruz:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Password’ü Kontrol Etme { #check-the-password }
Bu noktada veritabanından kullanıcı verisine sahibiz, ancak password’ü henüz kontrol etmedik.
Önce bu veriyi Pydantic `UserInDB` modeline koyalım.
Asla düz metin (plaintext) password kaydetmemelisiniz; bu yüzden (sahte) password hashing sistemini kullanacağız.
Password’ler eşleşmezse, aynı hatayı döndürürüz.
#### Password hashing { #password-hashing }
"Hashing" şudur: bir içeriği (bu örnekte password) anlaşılmaz görünen bayt dizisine (yani bir string’e) dönüştürmek.
Aynı içeriği (aynı password’ü) her verdiğinizde, birebir aynı anlamsız görünen çıktıyı elde edersiniz.
Ama bu anlamsız çıktıyı tekrar password’e geri çeviremezsiniz.
##### Neden password hashing kullanılır { #why-use-password-hashing }
Veritabanınız çalınırsa, hırsız kullanıcılarınızın düz metin password’lerine değil, sadece hash’lere sahip olur.
Dolayısıyla hırsız, aynı password’leri başka bir sistemde denemeye çalışamaz (birçok kullanıcı her yerde aynı password’ü kullandığı için bu tehlikeli olurdu).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### `**user_dict` Hakkında { #about-user-dict }
`UserInDB(**user_dict)` şu anlama gelir:
*`user_dict` içindeki key ve value’ları doğrudan key-value argümanları olarak geçir; şu ifadeyle eşdeğerdir:*
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Bilgi
`**user_dict` için daha kapsamlı bir açıklama için [**Extra Models** dokümantasyonundaki ilgili bölüme](../extra-models.md#about-user-in-dict) geri dönüp bakın.
///
## Token’ı Döndürme { #return-the-token }
`token` endpoint’inin response’u bir JSON object olmalıdır.
Bir `token_type` içermelidir. Biz "Bearer" token’ları kullandığımız için token type "`bearer`" olmalıdır.
Ayrıca `access_token` içermelidir; bunun değeri access token’ımızı içeren bir string olmalıdır.
Bu basit örnekte tamamen güvensiz davranıp token olarak aynı `username`’i döndüreceğiz.
/// tip | İpucu
Bir sonraki bölümde, password hashing ve JWT token’ları ile gerçekten güvenli bir implementasyon göreceksiniz.
Ama şimdilik ihtiyacımız olan spesifik detaylara odaklanalım.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | İpucu
Spesifikasyona göre, bu örnekteki gibi `access_token` ve `token_type` içeren bir JSON döndürmelisiniz.
Bunu kendi kodunuzda kendiniz yapmalı ve bu JSON key’lerini kullandığınızdan emin olmalısınız.
Spesifikasyonlara uyum için, doğru yapmanız gereken neredeyse tek şey budur.
Geri kalanını **FastAPI** sizin yerinize yönetir.
///
## Dependency’leri Güncelleme { #update-the-dependencies }
Şimdi dependency’lerimizi güncelleyeceğiz.
`current_user`’ı *sadece* kullanıcı aktifse almak istiyoruz.
Bu yüzden, `get_current_user`’ı dependency olarak kullanan ek bir dependency olan `get_current_active_user`’ı oluşturuyoruz.
Bu iki dependency de kullanıcı yoksa veya pasifse sadece HTTP hatası döndürecek.
Dolayısıyla endpoint’imizde kullanıcıyı ancak kullanıcı varsa, doğru şekilde authenticate edildiyse ve aktifse alacağız:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Bilgi
Burada `Bearer` değerine sahip ek `WWW-Authenticate` header’ını döndürmemiz de spesifikasyonun bir parçasıdır.
Herhangi bir HTTP (hata) durum kodu 401 "UNAUTHORIZED", ayrıca `WWW-Authenticate` header’ı da döndürmelidir.
Bearer token’lar (bizim durumumuz) için bu header’ın değeri `Bearer` olmalıdır.
Aslında bu ekstra header’ı atlayabilirsiniz, yine de çalışır.
Ama spesifikasyonlara uyumlu olması için burada eklenmiştir.
Ayrıca, bunu bekleyen ve kullanan araçlar olabilir (şimdi veya ileride) ve bu da sizin ya da kullanıcılarınız için faydalı olabilir.
Standartların faydası da bu...
///
## Çalışır Halini Görün { #see-it-in-action }
Etkileşimli dokümanları açın: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Authenticate Olma { #authenticate }
"Authorize" butonuna tıklayın.
Şu bilgileri kullanın:
User: `johndoe`
Password: `secret`
Sistemde authenticate olduktan sonra şöyle görürsünüz:
### Kendi Kullanıcı Verinizi Alma { #get-your-own-user-data }
Şimdi `/users/me` path’inde `GET` operasyonunu kullanın.
Kullanıcınızın verisini şöyle alırsınız:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Kilit ikonuna tıklayıp logout olursanız ve sonra aynı operasyonu tekrar denerseniz, şu şekilde bir HTTP 401 hatası alırsınız:
```JSON
{
"detail": "Not authenticated"
}
```
### Pasif Kullanıcı { #inactive-user }
Şimdi pasif bir kullanıcıyla deneyin; şu bilgilerle authenticate olun:
User: `alice`
Password: `secret2`
Ve `/users/me` path’inde `GET` operasyonunu kullanmayı deneyin.
Şöyle bir "Inactive user" hatası alırsınız:
```JSON
{
"detail": "Inactive user"
}
```
## Özet { #recap }
Artık API’niz için `username` ve `password` tabanlı, eksiksiz bir güvenlik sistemi implement etmek için gerekli araçlara sahipsiniz.
Bu araçlarla güvenlik sistemini herhangi bir veritabanıyla ve herhangi bir user veya veri modeliyle uyumlu hale getirebilirsiniz.
Eksik kalan tek detay, bunun henüz gerçekten "güvenli" olmamasıdır.
Bir sonraki bölümde güvenli bir password hashing kütüphanesini ve JWT token’larını nasıl kullanacağınızı göreceksiniz.
================================================
FILE: docs/tr/docs/tutorial/server-sent-events.md
================================================
# Server-Sent Events (SSE) { #server-sent-events-sse }
İstemciye veri akışını **Server-Sent Events** (SSE) ile sağlayabilirsiniz.
Bu, [JSON Lines Akışı](stream-json-lines.md) ile benzerdir ancak tarayıcılar tarafından yerel olarak desteklenen [`EventSource` API'si](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) ile `text/event-stream` formatını kullanır.
/// info | Bilgi
FastAPI 0.135.0'da eklendi.
///
## Server-Sent Events Nedir? { #what-are-server-sent-events }
SSE, HTTP üzerinden sunucudan istemciye veri akışı için bir standarttır.
Her olay, aralarında boş satırlar bulunan ve `data`, `event`, `id` ve `retry` gibi "alanlar" içeren küçük bir metin bloğudur.
Şuna benzer:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE; yapay zekâ sohbet akışı, canlı bildirimler, log ve gözlemlenebilirlik (observability) gibi senaryolarda ve sunucunun istemciye güncellemeleri ittiği diğer durumlarda yaygın olarak kullanılır.
/// tip | İpucu
İkili (binary) veri akışı yapmak istiyorsanız, gelişmiş kılavuza bakın: [Veri Akışı](../advanced/stream-data.md).
///
## FastAPI ile SSE Akışı { #stream-sse-with-fastapi }
FastAPI ile SSE akışı yapmak için, *path operation function* içinde `yield` kullanın ve `response_class=EventSourceResponse` olarak ayarlayın.
`EventSourceResponse`'u `fastapi.sse` içinden içe aktarın:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Yield edilen her öğe JSON olarak kodlanır ve bir SSE olayının `data:` alanında gönderilir.
Dönüş tipini `AsyncIterable[Item]` olarak bildirirseniz, FastAPI bunu Pydantic ile veriyi **doğrulamak**, **belgelemek** ve **serileştirmek** için kullanır.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | İpucu
Pydantic serileştirmeyi **Rust** tarafında yapacağından, dönüş tipi bildirmediğiniz duruma göre çok daha yüksek **performans** elde edersiniz.
///
### Async Olmayan Path Operation Fonksiyonları { #non-async-path-operation-functions }
Normal `def` fonksiyonlarını (yani `async` olmadan) da kullanabilir ve aynı şekilde `yield` kullanabilirsiniz.
FastAPI, event loop'u bloke etmeyecek şekilde doğru biçimde çalışmasını sağlar.
Bu örnekte fonksiyon async olmadığı için doğru dönüş tipi `Iterable[Item]` olur:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Dönüş Tipi Olmadan { #no-return-type }
Dönüş tipini belirtmeyebilirsiniz. FastAPI, veriyi dönüştürmek ve göndermek için [`jsonable_encoder`](./encoder.md) kullanır.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
`event`, `id`, `retry` veya `comment` gibi SSE alanlarını ayarlamanız gerekirse, düz veri yerine `ServerSentEvent` nesneleri yield edebilirsiniz.
`ServerSentEvent`'i `fastapi.sse` içinden içe aktarın:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
`data` alanı her zaman JSON olarak kodlanır. Pydantic modelleri dâhil, JSON olarak serileştirilebilen herhangi bir değeri geçebilirsiniz.
## Ham Veri { #raw-data }
Veriyi JSON kodlaması olmadan göndermeniz gerekiyorsa, `data` yerine `raw_data` kullanın.
Bu, önceden biçimlendirilmiş metin, log satırları veya `[DONE]` gibi özel "işaretçi" değerleri göndermek için kullanışlıdır.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Not
`data` ve `raw_data` birbirini dışlar. Her `ServerSentEvent` için bunlardan yalnızca birini ayarlayabilirsiniz.
///
## `Last-Event-ID` ile Devam Etme { #resuming-with-last-event-id }
Bir tarayıcı bağlantı koptuktan sonra yeniden bağlandığında, son aldığı `id`'yi `Last-Event-ID` header'ında gönderir.
Bunu bir header parametresi olarak okuyup, istemcinin kaldığı yerden akışı sürdürmek için kullanabilirsiniz:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## POST ile SSE { #sse-with-post }
SSE, sadece `GET` değil, **tüm HTTP metodlarıyla** çalışır.
Bu, SSE'yi `POST` üzerinden akıtan [MCP](https://modelcontextprotocol.io) gibi protokoller için kullanışlıdır:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Teknik Detaylar { #technical-details }
FastAPI, bazı SSE en iyi uygulamalarını kutudan çıktığı gibi uygular.
- [HTML spesifikasyonu: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes) önerisine uygun olarak, bazı proxy'lerin bağlantıyı kapatmasını önlemek için, 15 saniye boyunca hiç mesaj gelmezse **"keep alive" `ping` yorumu** gönderir.
- Akışın **cache'lenmesini önlemek** için `Cache-Control: no-cache` header'ını ayarlar.
- Nginx gibi bazı proxy'lerde **buffering'i önlemek** için özel `X-Accel-Buffering: no` header'ını ayarlar.
Bunun için ekstra bir şey yapmanız gerekmez, doğrudan çalışır. 🤓
================================================
FILE: docs/tr/docs/tutorial/sql-databases.md
================================================
# SQL (İlişkisel) Veritabanları { #sql-relational-databases }
**FastAPI**, SQL (ilişkisel) bir veritabanı kullanmanızı zorunlu kılmaz. Ancak isterseniz **istediğiniz herhangi bir veritabanını** kullanabilirsiniz.
Burada [SQLModel](https://sqlmodel.tiangolo.com/) kullanarak bir örnek göreceğiz.
**SQLModel**, [SQLAlchemy](https://www.sqlalchemy.org/) ve Pydantic’in üzerine inşa edilmiştir. **FastAPI**’nin yazarı tarafından, **SQL veritabanları** kullanması gereken FastAPI uygulamalarıyla mükemmel uyum sağlaması için geliştirilmiştir.
/// tip | İpucu
İstediğiniz başka bir SQL veya NoSQL veritabanı kütüphanesini kullanabilirsiniz (bazı durumlarda "ORMs" olarak adlandırılır). FastAPI sizi hiçbir şeye zorlamaz. 😎
///
SQLModel, SQLAlchemy tabanlı olduğu için SQLAlchemy’nin **desteklediği herhangi bir veritabanını** kolayca kullanabilirsiniz (bu da SQLModel tarafından da desteklendikleri anlamına gelir), örneğin:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server, vb.
Bu örnekte **SQLite** kullanacağız; çünkü tek bir dosya kullanır ve Python’da yerleşik desteği vardır. Yani bu örneği kopyalayıp olduğu gibi çalıştırabilirsiniz.
Daha sonra, production uygulamanız için **PostgreSQL** gibi bir veritabanı sunucusu kullanmak isteyebilirsiniz.
/// tip | İpucu
Frontend ve daha fazla araçla birlikte **FastAPI** + **PostgreSQL** içeren resmi bir proje oluşturucu (project generator) var: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Bu çok basit ve kısa bir eğitimdir. Veritabanları genelinde, SQL hakkında veya daha ileri özellikler hakkında öğrenmek isterseniz [SQLModel dokümantasyonuna](https://sqlmodel.tiangolo.com/) gidin.
## `SQLModel` Kurulumu { #install-sqlmodel }
Önce [virtual environment](../virtual-environments.md) oluşturduğunuzdan emin olun, aktive edin ve ardından `sqlmodel`’i yükleyin:
```console
$ pip install sqlmodel
---> 100%
```
## Tek Model ile Uygulamayı Oluşturma { #create-the-app-with-a-single-model }
Önce, tek bir **SQLModel** modeliyle uygulamanın en basit ilk sürümünü oluşturacağız.
Aşağıda, **birden fazla model** kullanarak güvenliği ve esnekliği artırıp geliştireceğiz. 🤓
### Modelleri Oluşturma { #create-models }
`SQLModel`’i import edin ve bir veritabanı modeli oluşturun:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
`Hero` sınıfı, bir Pydantic modeline çok benzer (hatta altta aslında *bir Pydantic modelidir*).
Birkaç fark var:
* `table=True`, SQLModel’e bunun bir *table model* olduğunu söyler; SQL veritabanında bir **table**’ı temsil etmelidir, sadece bir *data model* değildir (diğer normal Pydantic sınıflarında olduğu gibi).
* `Field(primary_key=True)`, SQLModel’e `id`’nin SQL veritabanındaki **primary key** olduğunu söyler (SQL primary key’leri hakkında daha fazlasını SQLModel dokümantasyonunda öğrenebilirsiniz).
**Not:** primary key alanı için `int | None` kullanıyoruz; böylece Python kodunda *`id` olmadan bir nesne oluşturabiliriz* (`id=None`) ve veritabanının *kaydederken bunu üreteceğini* varsayarız. SQLModel, veritabanının `id` sağlayacağını anlar ve *veritabanı şemasında sütunu null olamayan bir `INTEGER`* olarak tanımlar. Detaylar için [primary key’ler hakkında SQLModel dokümantasyonuna](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) bakın.
* `Field(index=True)`, SQLModel’e bu sütun için bir **SQL index** oluşturmasını söyler; bu da bu sütuna göre filtrelenmiş verileri okurken veritabanında daha hızlı arama yapılmasını sağlar.
SQLModel, `str` olarak tanımlanan bir şeyin SQL tarafında `TEXT` (veya veritabanına bağlı olarak `VARCHAR`) tipinde bir sütun olacağını bilir.
### Engine Oluşturma { #create-an-engine }
Bir SQLModel `engine`’i (altta aslında bir SQLAlchemy `engine`’idir) veritabanına olan **bağlantıları tutan** yapıdır.
Tüm kodunuzun aynı veritabanına bağlanması için **tek bir `engine` nesnesi** kullanırsınız.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
`check_same_thread=False` kullanmak, FastAPI’nin aynı SQLite veritabanını farklı thread’lerde kullanmasına izin verir. Bu gereklidir; çünkü **tek bir request** **birden fazla thread** kullanabilir (örneğin dependency’lerde).
Merak etmeyin; kodun yapısı gereği, ileride **her request için tek bir SQLModel *session*** kullandığımızdan emin olacağız. Zaten `check_same_thread` de temelde bunu mümkün kılmaya çalışır.
### Table’ları Oluşturma { #create-the-tables }
Sonra `SQLModel.metadata.create_all(engine)` kullanan bir fonksiyon ekleyerek tüm *table model*’ler için **table’ları oluştururuz**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Session Dependency’si Oluşturma { #create-a-session-dependency }
Bir **`Session`**, **nesneleri memory’de** tutar ve verideki gerekli değişiklikleri takip eder; ardından veritabanıyla iletişim kurmak için **`engine` kullanır**.
`yield` ile, her request için yeni bir `Session` sağlayacak bir FastAPI **dependency** oluşturacağız. Bu da her request’te tek session kullanmamızı garanti eder. 🤓
Ardından bu dependency’yi kullanacak kodun geri kalanını sadeleştirmek için `Annotated` ile `SessionDep` dependency’sini oluştururuz.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Startup’ta Veritabanı Table’larını Oluşturma { #create-database-tables-on-startup }
Uygulama başlarken veritabanı table’larını oluşturacağız.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Burada bir uygulama startup event’inde table’ları oluşturuyoruz.
Production’da büyük ihtimalle uygulamayı başlatmadan önce çalışan bir migration script’i kullanırsınız. 🤓
/// tip | İpucu
SQLModel, Alembic’i saran migration araçlarına sahip olacak; ancak şimdilik [Alembic](https://alembic.sqlalchemy.org/en/latest/)’i doğrudan kullanabilirsiniz.
///
### Hero Oluşturma { #create-a-hero }
Her SQLModel modeli aynı zamanda bir Pydantic modeli olduğu için, Pydantic modelleriyle kullanabildiğiniz **type annotation**’larda aynı şekilde kullanabilirsiniz.
Örneğin `Hero` tipinde bir parametre tanımlarsanız, bu parametre **JSON body**’den okunur.
Aynı şekilde, bunu fonksiyonun **return type**’ı olarak da tanımlayabilirsiniz; böylece verinin şekli otomatik API docs arayüzünde görünür.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Burada `SessionDep` dependency’sini (bir `Session`) kullanarak yeni `Hero`’yu `Session` instance’ına ekliyoruz, değişiklikleri veritabanına commit ediyoruz, `hero` içindeki veriyi refresh ediyoruz ve sonra geri döndürüyoruz.
### Hero’ları Okuma { #read-heroes }
`select()` kullanarak veritabanından `Hero`’ları **okuyabiliriz**. Sonuçları sayfalama (pagination) yapmak için `limit` ve `offset` ekleyebiliriz.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Tek Bir Hero Okuma { #read-one-hero }
Tek bir `Hero` **okuyabiliriz**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Hero Silme { #delete-a-hero }
Bir `Hero`’yu **silebiliriz**.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Uygulamayı Çalıştırma { #run-the-app }
Uygulamayı çalıştırabilirsiniz:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Sonra `/docs` arayüzüne gidin; **FastAPI**’nin API’yi **dokümante etmek** için bu **modelleri** kullandığını göreceksiniz. Ayrıca veriyi **serialize** ve **validate** etmek için de onları kullanacaktır.
## Birden Fazla Model ile Uygulamayı Güncelleme { #update-the-app-with-multiple-models }
Şimdi bu uygulamayı biraz **refactor** edelim ve **güvenliği** ile **esnekliği** artıralım.
Önceki uygulamaya bakarsanız, UI’da şu ana kadar client’ın oluşturulacak `Hero`’nun `id` değerini belirlemesine izin verdiğini görebilirsiniz. 😱
Buna izin vermemeliyiz; DB’de zaten atanmış bir `id`’yi ezebilirler. `id` belirlemek **client** tarafından değil, **backend** veya **veritabanı** tarafından yapılmalıdır.
Ayrıca hero için bir `secret_name` oluşturuyoruz ama şimdiye kadar her yerde geri döndürüyoruz; bu pek de **secret** sayılmaz... 😅
Bunları birkaç **ek model** ekleyerek düzelteceğiz. SQLModel’in parlayacağı yer de burası. ✨
### Birden Fazla Model Oluşturma { #create-multiple-models }
**SQLModel**’de, `table=True` olan herhangi bir model sınıfı bir **table model**’dir.
`table=True` olmayan her model sınıfı ise bir **data model**’dir; bunlar aslında sadece Pydantic modelleridir (bazı küçük ek özelliklerle). 🤓
SQLModel ile **inheritance** kullanarak her durumda tüm alanları tekrar tekrar yazmaktan **kaçınabiliriz**.
#### `HeroBase` - temel sınıf { #herobase-the-base-class }
Önce tüm modeller tarafından **paylaşılan alanları** içeren bir `HeroBase` modeliyle başlayalım:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - *table model* { #hero-the-table-model }
Sonra gerçek *table model* olan `Hero`’yu, diğer modellerde her zaman bulunmayan **ek alanlarla** oluşturalım:
* `id`
* `secret_name`
`Hero`, `HeroBase`’ten miras aldığı için `HeroBase`’te tanımlanan alanlara da sahiptir. Dolayısıyla `Hero` için tüm alanlar:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - public *data model* { #heropublic-the-public-data-model }
Sonraki adımda `HeroPublic` modelini oluştururuz; bu model API client’larına **geri döndürülecek** modeldir.
`HeroBase` ile aynı alanlara sahiptir; dolayısıyla `secret_name` içermez.
Sonunda kahramanlarımızın kimliği korunmuş oldu! 🥷
Ayrıca `id: int` alanını yeniden tanımlar. Bunu yaparak API client’larıyla bir **contract** (sözleşme) oluşturmuş oluruz; böylece `id` alanının her zaman var olacağını ve `int` olacağını (asla `None` olmayacağını) bilirler.
/// tip | İpucu
Return model’in bir değerin her zaman mevcut olduğunu ve her zaman `int` olduğunu (`None` değil) garanti etmesi API client’ları için çok faydalıdır; bu kesinlik sayesinde daha basit kod yazabilirler.
Ayrıca **otomatik üretilen client**’ların arayüzleri de daha basit olur; böylece API’nizle çalışan geliştiriciler için süreç çok daha rahat olur. 😎
///
`HeroPublic` içindeki tüm alanlar `HeroBase` ile aynıdır; tek fark `id`’nin `int` olarak tanımlanmasıdır (`None` değil):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - hero oluşturmak için *data model* { #herocreate-the-data-model-to-create-a-hero }
Şimdi `HeroCreate` modelini oluştururuz; bu model client’tan gelen veriyi **validate** etmek için kullanılır.
`HeroBase` ile aynı alanlara sahiptir ve ek olarak `secret_name` içerir.
Artık client’lar **yeni bir hero oluştururken** `secret_name` gönderecek; bu değer veritabanında saklanacak, ancak API response’larında client’a geri döndürülmeyecek.
/// tip | İpucu
**Password**’ları bu şekilde ele alırsınız: alırsınız ama API’de geri döndürmezsiniz.
Ayrıca password değerlerini saklamadan önce **hash** etmelisiniz; **asla plain text olarak saklamayın**.
///
`HeroCreate` alanları:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - hero güncellemek için *data model* { #heroupdate-the-data-model-to-update-a-hero }
Uygulamanın önceki sürümünde bir hero’yu **güncellemenin** bir yolu yoktu; ancak artık **birden fazla model** ile bunu yapabiliriz. 🎉
`HeroUpdate` *data model* biraz özeldir: yeni bir hero oluşturmak için gereken alanların **tamamına** sahiptir, ancak tüm alanlar **opsiyoneldir** (hepsinin bir default değeri vardır). Bu sayede hero güncellerken sadece güncellemek istediğiniz alanları gönderebilirsiniz.
Tüm **alanlar aslında değiştiği** için (tip artık `None` içeriyor ve default değerleri `None` oluyor), onları **yeniden tanımlamamız** gerekir.
Aslında `HeroBase`’ten miras almamız gerekmiyor; çünkü tüm alanları yeniden tanımlıyoruz. Tutarlılık için miras almayı bırakıyorum ama bu gerekli değil. Daha çok kişisel tercih meselesi. 🤷
`HeroUpdate` alanları:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### `HeroCreate` ile Oluşturma ve `HeroPublic` Döndürme { #create-with-herocreate-and-return-a-heropublic }
Artık **birden fazla model** olduğuna göre, onları kullanan uygulama kısımlarını güncelleyebiliriz.
Request’te bir `HeroCreate` *data model* alırız ve bundan bir `Hero` *table model* oluştururuz.
Bu yeni *table model* `Hero`, client’ın gönderdiği alanlara sahip olur ve ayrıca veritabanının ürettiği bir `id` alır.
Sonra fonksiyondan bu *table model* `Hero`’yu olduğu gibi döndürürüz. Ancak `response_model`’i `HeroPublic` *data model* olarak belirlediğimiz için **FastAPI**, veriyi validate ve serialize etmek için `HeroPublic` kullanır.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | İpucu
Burada **return type annotation** `-> HeroPublic` yerine `response_model=HeroPublic` kullanıyoruz; çünkü gerçekte döndürdüğümüz değer *bir* `HeroPublic` değil.
Eğer `-> HeroPublic` yazsaydık, editörünüz ve linter’ınız (haklı olarak) `HeroPublic` yerine `Hero` döndürdüğünüz için şikayet edecekti.
Bunu `response_model` içinde belirterek **FastAPI**’ye işini yapmasını söylüyoruz; type annotation’lara ve editörünüzün/diğer araçların sağladığı desteğe karışmamış oluyoruz.
///
### `HeroPublic` ile Hero’ları Okuma { #read-heroes-with-heropublic }
Daha öncekiyle aynı şekilde `Hero`’ları **okuyabiliriz**; yine `response_model=list[HeroPublic]` kullanarak verinin doğru biçimde validate ve serialize edilmesini garanti ederiz.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### `HeroPublic` ile Tek Bir Hero Okuma { #read-one-hero-with-heropublic }
Tek bir hero’yu **okuyabiliriz**:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### `HeroUpdate` ile Hero Güncelleme { #update-a-hero-with-heroupdate }
Bir hero’yu **güncelleyebiliriz**. Bunun için HTTP `PATCH` operasyonu kullanırız.
Kodda, client’ın gönderdiği tüm verilerle bir `dict` alırız; **yalnızca client’ın gönderdiği veriler**, yani sadece default değer oldukları için orada bulunan değerler hariç. Bunu yapmak için `exclude_unset=True` kullanırız. Asıl numara bu. 🪄
Sonra `hero_db.sqlmodel_update(hero_data)` ile `hero_db`’yi `hero_data` içindeki verilerle güncelleriz.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Hero’yu Tekrar Silme { #delete-a-hero-again }
Bir hero’yu **silmek** büyük ölçüde aynı kalıyor.
Bu örnekte her şeyi refactor etme isteğimizi bastıracağız. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Uygulamayı Tekrar Çalıştırma { #run-the-app-again }
Uygulamayı tekrar çalıştırabilirsiniz:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
`/docs` API UI’a giderseniz artık güncellendiğini göreceksiniz; hero oluştururken client’tan `id` beklemeyecek, vb.
## Özet { #recap }
Bir SQL veritabanıyla etkileşim kurmak için [**SQLModel**](https://sqlmodel.tiangolo.com/) kullanabilir ve *data model* ile *table model* yaklaşımıyla kodu sadeleştirebilirsiniz.
**SQLModel** dokümantasyonunda çok daha fazlasını öğrenebilirsiniz; **FastAPI** ile SQLModel kullanımı için daha uzun bir mini [tutorial](https://sqlmodel.tiangolo.com/tutorial/fastapi/) da bulunuyor. 🚀
================================================
FILE: docs/tr/docs/tutorial/static-files.md
================================================
# Statik Dosyalar { #static-files }
`StaticFiles` kullanarak bir dizindeki statik dosyaları otomatik olarak sunabilirsiniz.
## `StaticFiles` Kullanımı { #use-staticfiles }
* `StaticFiles`'ı import edin.
* Belirli bir path'te bir `StaticFiles()` örneğini "mount" edin.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Teknik Detaylar
`from starlette.staticfiles import StaticFiles` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.staticfiles`'ı `fastapi.staticfiles` olarak da sağlar. Ancak aslında doğrudan Starlette'den gelir.
///
### "Mounting" Nedir { #what-is-mounting }
"Mounting", belirli bir path'te tamamen "bağımsız" bir uygulama eklemek ve sonrasında tüm alt path'leri handle etmesini sağlamak demektir.
Bu, bir `APIRouter` kullanmaktan farklıdır; çünkü mount edilen uygulama tamamen bağımsızdır. Ana uygulamanızın OpenAPI ve docs'ları, mount edilen uygulamadan hiçbir şey içermez, vb.
Bununla ilgili daha fazla bilgiyi [Gelişmiş Kullanıcı Kılavuzu](../advanced/index.md) içinde okuyabilirsiniz.
## Detaylar { #details }
İlk `"/static"`, bu "alt uygulamanın" "mount" edileceği alt path'i ifade eder. Dolayısıyla `"/static"` ile başlayan herhangi bir path bunun tarafından handle edilir.
`directory="static"`, statik dosyalarınızı içeren dizinin adını ifade eder.
`name="static"`, **FastAPI**'nin dahili olarak kullanabileceği bir isim verir.
Bu parametrelerin hepsi "`static`" ile aynı olmak zorunda değildir; kendi uygulamanızın ihtiyaçlarına ve özel detaylarına göre ayarlayın.
## Daha Fazla Bilgi { #more-info }
Daha fazla detay ve seçenek için [Starlette'in Statik Dosyalar hakkındaki dokümanlarını](https://www.starlette.dev/staticfiles/) inceleyin.
================================================
FILE: docs/tr/docs/tutorial/stream-json-lines.md
================================================
# JSON Lines Akışı { #stream-json-lines }
Bir veri dizisini “akış” olarak göndermek istediğiniz durumlar olabilir; bunu **JSON Lines** ile yapabilirsiniz.
/// info | Bilgi
FastAPI 0.134.0 ile eklendi.
///
## Akış (Stream) Nedir? { #what-is-a-stream }
Verileri “streaming” olarak göndermek, uygulamanızın tüm öğe dizisi hazır olmasını beklemeden, öğeleri istemciye göndermeye başlaması demektir.
Yani ilk öğeyi gönderirsiniz, istemci onu alıp işlemeye başlar, bu sırada siz bir sonraki öğeyi üretmeye devam edebilirsiniz.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Hatta, sürekli veri gönderdiğiniz sonsuz bir akış da olabilir.
## JSON Lines { #json-lines }
Bu tür durumlarda, her satıra bir JSON nesnesi gönderdiğiniz bir biçim olan “**JSON Lines**” kullanmak yaygındır.
Response’un `application/json` yerine `application/jsonl` içerik türü (Content-Type) olur ve body aşağıdaki gibi görünür:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
Bir JSON dizisine (Python list eşdeğeri) çok benzer; ancak öğeler `[]` içine alınmak ve araya `,` konmak yerine, her satırda **bir JSON nesnesi** vardır; bunlar yeni satır karakteri ile ayrılır.
/// info | Bilgi
Önemli nokta, uygulamanız her satırı sırayla üretebilirken, istemcinin de önceki satırları tüketmeye devam edebilmesidir.
///
/// note | Teknik Detaylar
Her JSON nesnesi yeni bir satırla ayrıldığı için, içeriklerinde gerçek yeni satır karakterleri bulunamaz; ancak JSON standardının bir parçası olan kaçışlı yeni satırlar (`\n`) bulunabilir.
Genelde bununla sizin uğraşmanız gerekmez, otomatik olarak halledilir, okumaya devam edin. 🤓
///
## Kullanım Senaryoları { #use-cases }
Bunu bir **AI LLM** servisinden, **loglar**dan veya **telemetri**den ya da **JSON** öğeleri halinde yapılandırılabilen başka tür verilerden akış yapmak için kullanabilirsiniz.
/// tip | İpucu
İkili (binary) veri akışı yapmak istiyorsanız, örneğin video veya ses, gelişmiş kılavuza bakın: [Veri Akışı](../advanced/stream-data.md).
///
## FastAPI ile JSON Lines Akışı { #stream-json-lines-with-fastapi }
FastAPI ile JSON Lines akışı yapmak için, *path operation function* içinde `return` kullanmak yerine, her öğeyi sırayla üretmek için `yield` kullanabilirsiniz.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Göndermek istediğiniz her JSON öğesi `Item` tipindeyse (bir Pydantic modeli) ve fonksiyon async ise, dönüş tipini `AsyncIterable[Item]` olarak belirtebilirsiniz:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Dönüş tipini belirtirseniz, FastAPI bu tipi kullanarak veriyi **doğrular**, OpenAPI’de **dokümante** eder, **filtreler** ve Pydantic ile **serileştirir**.
/// tip | İpucu
Pydantic serileştirmeyi **Rust** tarafında yapacağı için, dönüş tipi belirtmediğiniz duruma göre çok daha yüksek **performans** elde edersiniz.
///
### Async Olmayan path operation function'lar { #non-async-path-operation-functions }
`async` olmadan normal `def` fonksiyonları da kullanabilir ve aynı şekilde `yield` yazabilirsiniz.
FastAPI, event loop’u bloklamayacak şekilde doğru çalışmasını garanti eder.
Bu durumda fonksiyon async olmadığı için doğru dönüş tipi `Iterable[Item]` olur:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Dönüş Tipi Olmadan { #no-return-type }
Dönüş tipini belirtmeyebilirsiniz de. Bu durumda FastAPI, veriyi JSON’a serileştirilebilir bir yapıya dönüştürmek için [`jsonable_encoder`](./encoder.md)’ı kullanır ve ardından JSON Lines olarak gönderir.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Server-Sent Events (SSE) { #server-sent-events-sse }
FastAPI, Server-Sent Events (SSE) için de birinci sınıf destek sağlar; benzerlerdir ancak birkaç ekstra ayrıntı vardır. Bir sonraki bölümden öğrenebilirsiniz: [Server-Sent Events (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/tr/docs/tutorial/testing.md
================================================
# Test Etme { #testing }
[Starlette](https://www.starlette.dev/testclient/) sayesinde **FastAPI** uygulamalarını test etmek kolay ve keyiflidir.
Temelde [HTTPX](https://www.python-httpx.org) üzerine kuruludur; HTTPX de Requests’i temel alarak tasarlandığı için oldukça tanıdık ve sezgiseldir.
Bununla birlikte **FastAPI** ile [pytest](https://docs.pytest.org/)'i doğrudan kullanabilirsiniz.
## `TestClient` Kullanımı { #using-testclient }
/// info | Bilgi
`TestClient` kullanmak için önce [`httpx`](https://www.python-httpx.org)'i kurun.
Bir [Sanal Ortam](../virtual-environments.md) oluşturduğunuzdan, onu aktifleştirdiğinizden ve sonra kurulumu yaptığınızdan emin olun; örneğin:
```console
$ pip install httpx
```
///
`TestClient`'ı import edin.
**FastAPI** uygulamanızı ona vererek bir `TestClient` oluşturun.
Adı `test_` ile başlayan fonksiyonlar oluşturun (bu, `pytest`'in standart konvansiyonudur).
`TestClient` nesnesini `httpx` ile kullandığınız şekilde kullanın.
Kontrol etmeniz gereken şeyler için standart Python ifadeleriyle basit `assert` satırları yazın (bu da `pytest` standardıdır).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | İpucu
Test fonksiyonlarının `async def` değil, normal `def` olduğuna dikkat edin.
Client'a yapılan çağrılar da `await` kullanılmadan, normal çağrılardır.
Bu sayede `pytest`'i ek bir karmaşıklık olmadan doğrudan kullanabilirsiniz.
///
/// note | Teknik Detaylar
İsterseniz `from starlette.testclient import TestClient` da kullanabilirsiniz.
**FastAPI**, geliştirici olarak size kolaylık olsun diye `starlette.testclient`'ı `fastapi.testclient` üzerinden de sunar. Ancak asıl kaynak doğrudan Starlette'tır.
///
/// tip | İpucu
FastAPI uygulamanıza request göndermenin dışında testlerinizde `async` fonksiyonlar çağırmak istiyorsanız (örn. asenkron veritabanı fonksiyonları), ileri seviye bölümdeki [Asenkron Testler](../advanced/async-tests.md) dokümanına göz atın.
///
## Testleri Ayırma { #separating-tests }
Gerçek bir uygulamada testlerinizi büyük ihtimalle farklı bir dosyada tutarsınız.
Ayrıca **FastAPI** uygulamanız birden fazla dosya/modül vb. ile de oluşturulmuş olabilir.
### **FastAPI** Uygulama Dosyası { #fastapi-app-file }
[Daha Büyük Uygulamalar](bigger-applications.md)'te anlatılan şekilde bir dosya yapınız olduğunu varsayalım:
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
`main.py` dosyasında **FastAPI** uygulamanız bulunuyor olsun:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Test Dosyası { #testing-file }
Sonra testlerinizin olduğu bir `test_main.py` dosyanız olabilir. Bu dosya aynı Python package içinde (yani `__init__.py` dosyası olan aynı dizinde) durabilir:
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Bu dosya aynı package içinde olduğu için, `main` modülünden (`main.py`) `app` nesnesini import etmek üzere relative import kullanabilirsiniz:
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...ve test kodunu da öncekiyle aynı şekilde yazabilirsiniz.
## Test Etme: Genişletilmiş Örnek { #testing-extended-example }
Şimdi bu örneği genişletelim ve farklı parçaların nasıl test edildiğini görmek için daha fazla detay ekleyelim.
### Genişletilmiş **FastAPI** Uygulama Dosyası { #extended-fastapi-app-file }
Aynı dosya yapısıyla devam edelim:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Diyelim ki **FastAPI** uygulamanızın bulunduğu `main.py` dosyasında artık başka **path operations** da var.
Hata döndürebilecek bir `GET` operation'ı var.
Birden fazla farklı hata döndürebilecek bir `POST` operation'ı var.
Her iki *path operation* da `X-Token` header'ını gerektiriyor.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Genişletilmiş Test Dosyası { #extended-testing-file }
Sonrasında `test_main.py` dosyanızı genişletilmiş testlerle güncelleyebilirsiniz:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Client'ın request içinde bir bilgi göndermesi gerektiğinde ve bunu nasıl yapacağınızı bilemediğinizde, `httpx` ile nasıl yapılacağını aratabilirsiniz (Google) ya da HTTPX’in tasarımı Requests’e dayandığı için `requests` ile nasıl yapıldığını da arayabilirsiniz.
Sonra testlerinizde aynısını uygularsınız.
Örn.:
* Bir *path* veya *query* parametresi geçirmek için, URL’nin kendisine ekleyin.
* JSON body göndermek için, `json` parametresine bir Python nesnesi (örn. bir `dict`) verin.
* JSON yerine *Form Data* göndermeniz gerekiyorsa, bunun yerine `data` parametresini kullanın.
* *headers* göndermek için, `headers` parametresine bir `dict` verin.
* *cookies* için, `cookies` parametresine bir `dict` verin.
Backend'e veri geçme hakkında daha fazla bilgi için (`httpx` veya `TestClient` kullanarak) [HTTPX dokümantasyonu](https://www.python-httpx.org)'na bakın.
/// info | Bilgi
`TestClient`'ın Pydantic model'lerini değil, JSON'a dönüştürülebilen verileri aldığını unutmayın.
Testinizde bir Pydantic model'iniz varsa ve test sırasında verisini uygulamaya göndermek istiyorsanız, [JSON Uyumlu Encoder](encoder.md) içinde açıklanan `jsonable_encoder`'ı kullanabilirsiniz.
///
## Çalıştırma { #run-it }
Bundan sonra yapmanız gereken tek şey `pytest`'i kurmaktır.
Bir [Sanal Ortam](../virtual-environments.md) oluşturduğunuzdan, onu aktifleştirdiğinizden ve sonra kurulumu yaptığınızdan emin olun; örneğin:
```console
$ pip install pytest
---> 100%
```
Dosyaları ve testleri otomatik olarak bulur, çalıştırır ve sonuçları size raporlar.
Testleri şu şekilde çalıştırın:
================================================
FILE: docs/tr/docs/virtual-environments.md
================================================
# Virtual Environments { #virtual-environments }
Python projeleriyle çalışırken, her proje için kurduğunuz package'leri birbirinden izole etmek adına büyük ihtimalle bir **virtual environment** (veya benzer bir mekanizma) kullanmalısınız.
/// info | Bilgi
Virtual environment'leri, nasıl oluşturulduklarını ve nasıl kullanıldıklarını zaten biliyorsanız bu bölümü atlamak isteyebilirsiniz. 🤓
///
/// tip | İpucu
**Virtual environment**, **environment variable** ile aynı şey değildir.
**Environment variable**, sistemde bulunan ve programların kullanabildiği bir değişkendir.
**Virtual environment** ise içinde bazı dosyalar bulunan bir klasördür.
///
/// info | Bilgi
Bu sayfada **virtual environment**'leri nasıl kullanacağınızı ve nasıl çalıştıklarını öğreneceksiniz.
Eğer Python'ı kurmak dahil her şeyi sizin yerinize yöneten bir **tool** kullanmaya hazırsanız, [uv](https://github.com/astral-sh/uv)'yi deneyin.
///
## Proje Oluşturun { #create-a-project }
Önce projeniz için bir klasör oluşturun.
Ben genelde home/user klasörümün içinde `code` adlı bir klasör oluştururum.
Sonra bunun içinde her proje için ayrı bir klasör oluştururum.
```console
// Go to the home directory
$ cd
// Create a directory for all your code projects
$ mkdir code
// Enter into that code directory
$ cd code
// Create a directory for this project
$ mkdir awesome-project
// Enter into that project directory
$ cd awesome-project
```
## Virtual Environment Oluşturun { #create-a-virtual-environment }
Bir Python projesi üzerinde **ilk kez** çalışmaya başladığınızda, **virtual environment**'i projenizin içinde oluşturun.
/// tip | İpucu
Bunu her çalıştığınızda değil, **proje başına sadece bir kez** yapmanız yeterlidir.
///
//// tab | `venv`
Bir virtual environment oluşturmak için, Python ile birlikte gelen `venv` modülünü kullanabilirsiniz.
```console
$ python -m venv .venv
```
/// details | Bu komut ne anlama geliyor
* `python`: `python` adlı programı kullan
* `-m`: bir modülü script gibi çalıştır; bir sonraki kısımda hangi modül olduğunu söyleyeceğiz
* `venv`: normalde Python ile birlikte kurulu gelen `venv` modülünü kullan
* `.venv`: virtual environment'i yeni `.venv` klasörünün içine oluştur
///
////
//// tab | `uv`
Eğer [`uv`](https://github.com/astral-sh/uv) kuruluysa, onunla da virtual environment oluşturabilirsiniz.
```console
$ uv venv
```
/// tip | İpucu
Varsayılan olarak `uv`, `.venv` adlı bir klasörde virtual environment oluşturur.
Ancak ek bir argümanla klasör adını vererek bunu özelleştirebilirsiniz.
///
////
Bu komut `.venv` adlı bir klasörün içinde yeni bir virtual environment oluşturur.
/// details | `.venv` veya başka bir ad
Virtual environment'i başka bir klasörde de oluşturabilirsiniz; ancak buna `.venv` demek yaygın bir konvansiyondur.
///
## Virtual Environment'i Aktif Edin { #activate-the-virtual-environment }
Oluşturduğunuz virtual environment'i aktif edin; böylece çalıştırdığınız her Python komutu veya kurduğunuz her package onu kullanır.
/// tip | İpucu
Projede çalışmak için **yeni bir terminal oturumu** başlattığınız **her seferinde** bunu yapın.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ya da Windows'ta Bash kullanıyorsanız (örn. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | İpucu
Bu environment'e **yeni bir package** kurduğunuz her seferinde environment'i yeniden **aktif edin**.
Böylece, o package'in kurduğu bir **terminal (CLI) programı** kullanıyorsanız, global olarak kurulu (ve muhtemelen ihtiyacınız olandan farklı bir versiyona sahip) başka bir program yerine, virtual environment'inizdeki programı kullanmış olursunuz.
///
## Virtual Environment'in Aktif Olduğunu Kontrol Edin { #check-the-virtual-environment-is-active }
Virtual environment'in aktif olduğunu (bir önceki komutun çalıştığını) kontrol edin.
/// tip | İpucu
Bu **opsiyoneldir**; ancak her şeyin beklendiği gibi çalıştığını ve hedeflediğiniz virtual environment'i kullandığınızı **kontrol etmek** için iyi bir yöntemdir.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Eğer `python` binary'sini projenizin içinde (bu örnekte `awesome-project`) `.venv/bin/python` yolunda gösteriyorsa, tamamdır. 🎉
////
//// tab | Windows PowerShell
Eğer `python` binary'sini projenizin içinde (bu örnekte `awesome-project`) `.venv\Scripts\python` yolunda gösteriyorsa, tamamdır. 🎉
////
## `pip`'i Yükseltin { #upgrade-pip }
/// tip | İpucu
[`uv`](https://github.com/astral-sh/uv) kullanıyorsanız, `pip` yerine onunla kurulum yaparsınız; dolayısıyla `pip`'i yükseltmeniz gerekmez. 😎
///
Package'leri kurmak için `pip` kullanıyorsanız (Python ile varsayılan olarak gelir), en güncel sürüme **yükseltmeniz** gerekir.
Bir package kurarken görülen birçok garip hata, önce `pip`'i yükseltince çözülür.
/// tip | İpucu
Bunu genelde virtual environment'i oluşturduktan hemen sonra **bir kez** yaparsınız.
///
Virtual environment'in aktif olduğundan emin olun (yukarıdaki komutla) ve sonra şunu çalıştırın:
/// tip | İpucu
Bazen pip'i yükseltmeye çalışırken **`No module named pip`** hatası alabilirsiniz.
Böyle olursa, aşağıdaki komutla pip'i kurup yükseltin:
Bu komut pip kurulu değilse kurar ve ayrıca kurulu pip sürümünün `ensurepip` içinde bulunan sürüm kadar güncel olmasını garanti eder.
///
## `.gitignore` Ekleyin { #add-gitignore }
**Git** kullanıyorsanız (kullanmalısınız), `.venv` içindeki her şeyi Git'ten hariç tutmak için bir `.gitignore` dosyası ekleyin.
/// tip | İpucu
Virtual environment'i [`uv`](https://github.com/astral-sh/uv) ile oluşturduysanız, bunu zaten sizin için yaptı; bu adımı atlayabilirsiniz. 😎
///
/// tip | İpucu
Bunu virtual environment'i oluşturduktan hemen sonra **bir kez** yapın.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Bu komut ne anlama geliyor
* `echo "*"`: terminale `*` metnini "yazar" (sonraki kısım bunu biraz değiştiriyor)
* `>`: `>` işaretinin solundaki komutun terminale yazdıracağı çıktı, ekrana basılmak yerine sağ taraftaki dosyaya yazılsın
* `.gitignore`: metnin yazılacağı dosyanın adı
Git'te `*` "her şey" demektir. Yani `.venv` klasörü içindeki her şeyi ignore eder.
Bu komut, içeriği şu olan bir `.gitignore` dosyası oluşturur:
```gitignore
*
```
///
## Package'leri Kurun { #install-packages }
Environment'i aktif ettikten sonra, içine package kurabilirsiniz.
/// tip | İpucu
Projede ihtiyaç duyduğunuz package'leri ilk kez kurarken veya yükseltirken bunu **bir kez** yapın.
Bir sürümü yükseltmeniz veya yeni bir package eklemeniz gerekirse **tekrar** yaparsınız.
///
### Package'leri Doğrudan Kurun { #install-packages-directly }
Acele ediyorsanız ve projenizin package gereksinimlerini bir dosyada belirtmek istemiyorsanız, doğrudan kurabilirsiniz.
/// tip | İpucu
Programınızın ihtiyaç duyduğu package'leri ve versiyonlarını bir dosyada tutmak (ör. `requirements.txt` veya `pyproject.toml`) (çok) iyi bir fikirdir.
///
//// tab | `pip`
////
### `requirements.txt`'ten Kurun { #install-from-requirements-txt }
Bir `requirements.txt` dosyanız varsa, içindeki package'leri kurmak için artık onu kullanabilirsiniz.
//// tab | `pip`
////
/// details | `requirements.txt`
Bazı package'ler içeren bir `requirements.txt` şöyle görünebilir:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Programınızı Çalıştırın { #run-your-program }
Virtual environment'i aktif ettikten sonra programınızı çalıştırabilirsiniz; program, virtual environment'in içindeki Python'ı ve oraya kurduğunuz package'leri kullanır.
```console
$ python main.py
Hello World
```
## Editörünüzü Yapılandırın { #configure-your-editor }
Muhtemelen bir editör kullanırsınız; otomatik tamamlamayı ve satır içi hataları alabilmek için, editörünüzü oluşturduğunuz aynı virtual environment'i kullanacak şekilde yapılandırdığınızdan emin olun (muhtemelen otomatik algılar).
Örneğin:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | İpucu
Bunu genelde yalnızca **bir kez**, virtual environment'i oluşturduğunuzda yapmanız gerekir.
///
## Virtual Environment'i Devre Dışı Bırakın { #deactivate-the-virtual-environment }
Projeniz üzerinde işiniz bittiğinde virtual environment'i **deactivate** edebilirsiniz.
```console
$ deactivate
```
Böylece `python` çalıştırdığınızda, o virtual environment içinden (ve oraya kurulu package'lerle) çalıştırmaya çalışmaz.
## Çalışmaya Hazırsınız { #ready-to-work }
Artık projeniz üzerinde çalışmaya başlayabilirsiniz.
/// tip | İpucu
Yukarıdaki her şeyin aslında ne olduğunu anlamak ister misiniz?
Okumaya devam edin. 👇🤓
///
## Neden Virtual Environment { #why-virtual-environments }
FastAPI ile çalışmak için [Python](https://www.python.org/) kurmanız gerekir.
Sonrasında FastAPI'yi ve kullanmak istediğiniz diğer tüm **package**'leri **kurmanız** gerekir.
Package kurmak için genelde Python ile gelen `pip` komutunu (veya benzeri alternatifleri) kullanırsınız.
Ancak `pip`'i doğrudan kullanırsanız, package'ler **global Python environment**'ınıza (Python'ın global kurulumuna) yüklenir.
### Problem { #the-problem }
Peki package'leri global Python environment'a kurmanın sorunu ne?
Bir noktada, muhtemelen **farklı package**'lere bağımlı birçok farklı program yazacaksınız. Ayrıca üzerinde çalıştığınız bazı projeler, aynı package'in **farklı versiyonlarına** ihtiyaç duyacak. 😱
Örneğin `philosophers-stone` adında bir proje oluşturduğunuzu düşünün; bu program, `harry` adlı başka bir package'e **`1` versiyonu ile** bağlı. Yani `harry`'yi kurmanız gerekir.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Sonra daha ileri bir zamanda `prisoner-of-azkaban` adlı başka bir proje oluşturuyorsunuz; bu proje de `harry`'ye bağlı, fakat bu proje **`harry` versiyon `3`** istiyor.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Şimdi sorun şu: package'leri local bir **virtual environment** yerine global (global environment) olarak kurarsanız, `harry`'nin hangi versiyonunu kuracağınıza karar vermek zorunda kalırsınız.
`philosophers-stone`'u çalıştırmak istiyorsanız önce `harry` versiyon `1`'i kurmanız gerekir; örneğin:
```console
$ pip install "harry==1"
```
Sonuç olarak global Python environment'ınızda `harry` versiyon `1` kurulu olur.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Fakat `prisoner-of-azkaban`'ı çalıştırmak istiyorsanız, `harry` versiyon `1`'i kaldırıp `harry` versiyon `3`'ü kurmanız gerekir (ya da sadece `3`'ü kurmak, otomatik olarak `1`'i kaldırabilir).
```console
$ pip install "harry==3"
```
Sonuç olarak global Python environment'ınızda `harry` versiyon `3` kurulu olur.
Ve `philosophers-stone`'u tekrar çalıştırmaya kalkarsanız, `harry` versiyon `1`e ihtiyaç duyduğu için **çalışmama** ihtimali vardır.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | İpucu
Python package'lerinde **yeni versiyonlarda** **breaking change**'lerden kaçınmak oldukça yaygındır; ancak yine de daha güvenlisi, yeni versiyonları bilinçli şekilde kurmak ve mümkünse test'leri çalıştırıp her şeyin doğru çalıştığını doğrulamaktır.
///
Şimdi bunu, **projelerinizin bağımlı olduğu** daha **birçok** başka **package** ile birlikte düşünün. Yönetmesi epey zorlaşır. Sonunda bazı projeleri package'lerin **uyumsuz versiyonlarıyla** çalıştırıp, bir şeylerin neden çalışmadığını anlamamak gibi durumlara düşebilirsiniz.
Ayrıca işletim sisteminize (örn. Linux, Windows, macOS) bağlı olarak Python zaten kurulu gelmiş olabilir. Bu durumda, sisteminizin **ihtiyaç duyduğu** bazı package'ler belirli versiyonlarla önceden kurulu olabilir. Global Python environment'a package kurarsanız, işletim sistemiyle gelen bazı programları **bozma** ihtimaliniz olabilir.
## Package'ler Nereye Kuruluyor { #where-are-packages-installed }
Python'ı kurduğunuzda, bilgisayarınızda bazı dosyalar içeren klasörler oluşturulur.
Bu klasörlerin bir kısmı, kurduğunuz tüm package'leri barındırmaktan sorumludur.
Şunu çalıştırdığınızda:
```console
// Don't run this now, it's just an example 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Bu, FastAPI kodunu içeren sıkıştırılmış bir dosyayı genellikle [PyPI](https://pypi.org/project/fastapi/)'dan indirir.
Ayrıca FastAPI'nin bağımlı olduğu diğer package'ler için de dosyaları **indirir**.
Sonra tüm bu dosyaları **açar (extract)** ve bilgisayarınızdaki bir klasöre koyar.
Varsayılan olarak bu indirilip çıkarılan dosyaları, Python kurulumunuzla birlikte gelen klasöre yerleştirir; yani **global environment**'a.
## Virtual Environment Nedir { #what-are-virtual-environments }
Global environment'da tüm package'leri bir arada tutmanın sorunlarına çözüm, çalıştığınız her proje için ayrı bir **virtual environment** kullanmaktır.
Virtual environment, global olana çok benzeyen bir **klasördür**; bir projenin ihtiyaç duyduğu package'leri buraya kurarsınız.
Böylece her projenin kendi virtual environment'i (`.venv` klasörü) ve kendi package'leri olur.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Virtual Environment'i Aktif Etmek Ne Demek { #what-does-activating-a-virtual-environment-mean }
Bir virtual environment'i örneğin şununla aktif ettiğinizde:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Ya da Windows'ta Bash kullanıyorsanız (örn. [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Bu komut, sonraki komutlarda kullanılabilecek bazı [environment variable](environment-variables.md)'ları oluşturur veya değiştirir.
Bunlardan biri `PATH` değişkenidir.
/// tip | İpucu
`PATH` environment variable hakkında daha fazla bilgiyi [Environment Variables](environment-variables.md#path-environment-variable) bölümünde bulabilirsiniz.
///
Bir virtual environment'i aktive etmek, onun `.venv/bin` (Linux ve macOS'ta) veya `.venv\Scripts` (Windows'ta) yolunu `PATH` environment variable'ına ekler.
Diyelim ki environment'i aktive etmeden önce `PATH` değişkeni şöyleydi:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Bu, sistemin programları şu klasörlerde arayacağı anlamına gelir:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Bu, sistemin programları şurada arayacağı anlamına gelir:
* `C:\Windows\System32`
////
Virtual environment'i aktive ettikten sonra `PATH` değişkeni şuna benzer hale gelir:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Bu, sistemin artık programları önce şurada aramaya başlayacağı anlamına gelir:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
diğer klasörlere bakmadan önce.
Dolayısıyla terminale `python` yazdığınızda, sistem Python programını şurada bulur:
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
ve onu kullanır.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Bu, sistemin artık programları önce şurada aramaya başlayacağı anlamına gelir:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
diğer klasörlere bakmadan önce.
Dolayısıyla terminale `python` yazdığınızda, sistem Python programını şurada bulur:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
ve onu kullanır.
////
Önemli bir detay: virtual environment yolu `PATH` değişkeninin **en başına** eklenir. Sistem, mevcut başka herhangi bir Python'ı bulmadan **önce** bunu bulur. Böylece `python` çalıştırdığınızda, başka bir `python` (örneğin global environment'tan gelen `python`) yerine **virtual environment'taki** Python kullanılır.
Virtual environment'i aktive etmek birkaç şeyi daha değiştirir; ancak yaptığı en önemli işlerden biri budur.
## Virtual Environment'i Kontrol Etmek { #checking-a-virtual-environment }
Bir virtual environment'in aktif olup olmadığını örneğin şununla kontrol ettiğinizde:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Bu, kullanılacak `python` programının **virtual environment'in içindeki** Python olduğu anlamına gelir.
Linux ve macOS'ta `which`, Windows PowerShell'de ise `Get-Command` kullanırsınız.
Bu komutun çalışma mantığı şudur: `PATH` environment variable içindeki **her yolu sırayla** dolaşır, `python` adlı programı arar. Bulduğunda, size o programın **dosya yolunu** gösterir.
En önemli kısım şu: `python` dediğinizde çalışacak olan "`python`" tam olarak budur.
Yani doğru virtual environment'da olup olmadığınızı doğrulayabilirsiniz.
/// tip | İpucu
Bir virtual environment'i aktive etmek kolaydır; sonra o Python ile kalıp **başka bir projeye geçmek** de kolaydır.
Bu durumda ikinci proje, başka bir projenin virtual environment'ından gelen **yanlış Python**'ı kullandığınız için **çalışmayabilir**.
Hangi `python`'ın kullanıldığını kontrol edebilmek bu yüzden faydalıdır. 🤓
///
## Neden Virtual Environment'i Deactivate Edelim { #why-deactivate-a-virtual-environment }
Örneğin `philosophers-stone` projesi üzerinde çalışıyor olabilirsiniz; **o virtual environment'i aktive eder**, package kurar ve o environment ile çalışırsınız.
Sonra **başka bir proje** olan `prisoner-of-azkaban` üzerinde çalışmak istersiniz.
O projeye gidersiniz:
```console
$ cd ~/code/prisoner-of-azkaban
```
Eğer `philosophers-stone` için olan virtual environment'i deactivate etmezseniz, terminalde `python` çalıştırdığınızda `philosophers-stone`'dan gelen Python'ı kullanmaya çalışır.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Error importing sirius, it's not installed 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Ama virtual environment'i deactivate edip `prisoner-of-askaban` için yeni olanı aktive ederseniz, `python` çalıştırdığınızda `prisoner-of-azkaban` içindeki virtual environment'dan gelen Python kullanılır.
```console
$ cd ~/code/prisoner-of-azkaban
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎
$ deactivate
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Now when you run python, it will find the package sirius installed in this virtual environment ✨
$ python main.py
I solemnly swear 🐺
```
## Alternatifler { #alternatives }
Bu, başlamanız için basit bir rehber ve alttaki mekanizmaların nasıl çalıştığını öğretmeyi amaçlıyor.
Virtual environment'leri, package bağımlılıklarını (requirements) ve projeleri yönetmek için birçok **alternatif** vardır.
Hazır olduğunuzda ve package bağımlılıkları, virtual environment'ler vb. dahil **tüm projeyi yönetmek** için bir tool kullanmak istediğinizde, [uv](https://github.com/astral-sh/uv)'yi denemenizi öneririm.
`uv` birçok şey yapabilir, örneğin:
* Sizin için **Python kurabilir**, farklı sürümler dahil
* Projelerinizin **virtual environment**'ini yönetebilir
* **Package** kurabilir
* Projeniz için package **bağımlılıklarını ve versiyonlarını** yönetebilir
* Bağımlılıkları dahil, kurulacak package ve versiyonların **tam (exact)** bir setini garanti edebilir; böylece geliştirirken bilgisayarınızda çalıştırdığınız projeyi production'da da birebir aynı şekilde çalıştırabileceğinizden emin olursunuz; buna **locking** denir
* Ve daha birçok şey
## Sonuç { #conclusion }
Buradaki her şeyi okuduysanız ve anladıysanız, artık birçok geliştiriciden **çok daha fazla** virtual environment bilgisine sahipsiniz. 🤓
Bu detayları bilmek, ileride karmaşık görünen bir sorunu debug ederken büyük olasılıkla işinize yarayacak; çünkü **altta nasıl çalıştığını** biliyor olacaksınız. 😎
================================================
FILE: docs/tr/llm-prompt.md
================================================
### Target language
Translate to Turkish (Türkçe).
Language code: tr.
### Core principle
Don't translate word-by-word. Rewrite naturally in Turkish as if writing the doc from scratch. Preserve meaning, but prioritize fluency over literal accuracy.
### Grammar and tone
- Use instructional Turkish, consistent with existing Turkish docs.
- Use imperative/guide language (e.g. "açalım", "gidin", "kopyalayalım", "bir bakalım").
- Avoid filler words and overly long sentences.
- Ensure sentences make sense in Turkish context — adjust structure, conjunctions, and verb forms as needed for natural flow (e.g. use "Ancak" instead of "Ve" when connecting contrasting sentences, use "-maktadır/-mektedir" for formal statements).
### Headings
- Follow existing Turkish heading style (Title Case where used; no trailing period).
### Quotes
- Keep quote style consistent with existing Turkish docs (typically ASCII quotes in text).
- Never modify quotes inside inline code, code blocks, URLs, or file paths.
### Ellipsis
- Keep ellipsis style (`...`) consistent with existing Turkish docs.
- Never modify `...` in code, URLs, or CLI examples.
### Consistency
- Use the same translation for the same term throughout the document.
- If you translate a concept one way, keep it consistent across all occurrences.
### Links and references
- Never modify link syntax like `{.internal-link target=_blank}`.
- Keep markdown link structure intact: `[text](url){.internal-link}`.
### Preferred translations / glossary
Do not translate technical terms like path, route, request, response, query, body, cookie, and header, keep them as is.
- Suffixing is very important, when adding Turkish suffixes to the English words, do that based on the pronunciation of the word and with an apostrophe.
- Suffixes also changes based on what word comes next in Turkish too, here is an example:
"Server'a gelen request'leri intercept... " or this could have been "request'e", "request'i" etc.
- Some words are tricky like "path'e" can't be used like "path'a" but it could have been "path'i" "path'leri" etc.
- You can use a more instructional style, that is consistent with the document, you can add the Turkish version of the term in parenthesis if it is not something very obvious, or an advanced concept, but do not over do it, do it only the first time it is mentioned, but keep the English term as the primary word.
### `///` admonitions
- Keep the admonition keyword in English (do not translate `note`, `tip`, etc.).
- If a title is present, prefer these canonical titles:
- `/// note | Not`
- `/// note | Teknik Detaylar`
- `/// tip | İpucu`
- `/// warning | Uyarı`
- `/// info | Bilgi`
- `/// check | Ek bilgi`
Prefer `İpucu` over `Ipucu`.
================================================
FILE: docs/tr/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/uk/docs/_llm-test.md
================================================
# Тестовий файл LLM { #llm-test-file }
Цей документ перевіряє, чи розуміє LLM, який перекладає документацію, `general_prompt` у `scripts/translate.py` та мовно-специфічну підсказку в `docs/{language code}/llm-prompt.md`. Мовно-специфічна підсказка додається до `general_prompt`.
Тести, додані тут, побачать усі автори мовно-специфічних підсказок.
Використовуйте так:
* Майте мовно-специфічну підсказку - `docs/{language code}/llm-prompt.md`.
* Зробіть свіжий переклад цього документа на бажану цільову мову (див., наприклад, команду `translate-page` у `translate.py`). Це створить переклад у `docs/{language code}/docs/_llm-test.md`.
* Перевірте, чи все гаразд у перекладі.
* За потреби покращіть вашу мовно-специфічну підсказку, загальну підсказку або англійський документ.
* Потім вручну виправте решту проблем у перекладі, щоб він був якісним.
* Перекладіть повторно, маючи якісний переклад на місці. Ідеальний результат - коли LLM більше не вносить змін до перекладу. Це означає, що загальна підсказка та ваша мовно-специфічна підсказка настільки добрі, наскільки це можливо (інколи він робитиме кілька, здавалося б, випадкових змін, причина в тому, що [LLM-и не є детерміністичними алгоритмами](https://doublespeak.chat/#/handbook#deterministic-output)).
Тести:
## Фрагменти коду { #code-snippets }
//// tab | Тест
Це фрагмент коду: `foo`. А це ще один фрагмент коду: `bar`. І ще один: `baz quux`.
////
//// tab | Інформація
Вміст фрагментів коду слід залишати без змін.
Див. розділ `### Content of code snippets` в загальній підсказці в `scripts/translate.py`.
////
## Цитати { #quotes }
//// tab | Тест
Вчора мій друг написав: «Якщо ви правильно напишете "incorrectly", ви написали це неправильно». На що я відповів: «Правильно, але 'incorrectly' - це неправильно, а не '"incorrectly"'».
/// note | Примітка
LLM, ймовірно, перекладе це неправильно. Цікаво лише те, чи збереже він виправлений переклад під час повторного перекладу.
///
////
//// tab | Інформація
Автор підсказки може обрати, чи конвертувати нейтральні лапки в типографські. Можна залишити як є.
Див., наприклад, розділ `### Quotes` у `docs/de/llm-prompt.md`.
////
## Лапки у фрагментах коду { #quotes-in-code-snippets }
//// tab | Тест
`pip install "foo[bar]"`
Приклади строкових літералів у фрагментах коду: `"this"`, `'that'`.
Складний приклад строкових літералів у фрагментах коду: `f"I like {'oranges' if orange else "apples"}"`
Хардкор: `Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | Інформація
... Однак лапки всередині фрагментів коду мають залишатися як є.
////
## Блоки коду { #code-blocks }
//// tab | Тест
Приклад коду Bash...
```bash
# Вивести привітання всесвіту
echo "Hello universe"
```
...і приклад коду консолі...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...і ще один приклад коду консолі...
```console
// Створіть каталог "Code"
$ mkdir code
// Перейдіть до цього каталогу
$ cd code
```
...і приклад коду Python...
```Python
wont_work() # Це не спрацює 😱
works(foo="bar") # Це працює 🎉
```
...і це все.
////
//// tab | Інформація
Код у блоках коду не слід змінювати, за винятком коментарів.
Див. розділ `### Content of code blocks` в загальній підсказці в `scripts/translate.py`.
////
## Вкладки та кольорові блоки { #tabs-and-colored-boxes }
//// tab | Тест
/// info | Інформація
Деякий текст
///
/// note | Примітка
Деякий текст
///
/// note | Технічні деталі
Деякий текст
///
/// check | Перевірте
Деякий текст
///
/// tip | Порада
Деякий текст
///
/// warning | Попередження
Деякий текст
///
/// danger | Обережно
Деякий текст
///
////
//// tab | Інформація
У вкладках і блоках `Info`/`Note`/`Warning`/тощо необхідно додати переклад їхнього заголовка після вертикальної риски (`|`).
Див. розділи `### Special blocks` і `### Tab blocks` в загальній підсказці в `scripts/translate.py`.
////
## Веб- і внутрішні посилання { #web-and-internal-links }
//// tab | Тест
Текст посилання має бути перекладений, адреса посилання має залишитися незмінною:
* [Посилання на заголовок вище](#code-snippets)
* [Внутрішнє посилання](index.md#installation)
* [Зовнішнє посилання](https://sqlmodel.tiangolo.com/)
* [Посилання на стиль](https://fastapi.tiangolo.com/css/styles.css)
* [Посилання на скрипт](https://fastapi.tiangolo.com/js/logic.js)
* [Посилання на зображення](https://fastapi.tiangolo.com/img/foo.jpg)
Текст посилання має бути перекладений, адреса посилання має вказувати на переклад:
* [Посилання на FastAPI](https://fastapi.tiangolo.com/uk/)
////
//// tab | Інформація
Посилання слід перекладати, але їхні адреси мають лишатися незмінними. Виняток - абсолютні посилання на сторінки документації FastAPI. У такому випадку вони мають вказувати на переклад.
Див. розділ `### Links` в загальній підсказці в `scripts/translate.py`.
////
## Елементи HTML «abbr» { #html-abbr-elements }
//// tab | Тест
Ось деякі речі, обгорнуті в елементи HTML «abbr» (деякі вигадані):
### Скорочення містить повну фразу { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### Скорочення містить повну фразу та пояснення { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | Інформація
Атрибути "title" елементів "abbr" перекладаються за певними інструкціями.
Переклади можуть додавати власні елементи "abbr", які LLM не повинен прибирати. Наприклад, щоб пояснити англійські слова.
Див. розділ `### HTML abbr elements` в загальній підсказці в `scripts/translate.py`.
////
## Елементи HTML «dfn» { #html-dfn-elements }
* кластер
* Глибоке навчання
## Заголовки { #headings }
//// tab | Тест
### Розробка вебзастосунку - навчальний посібник { #develop-a-webapp-a-tutorial }
Привіт.
### Підказки типів та - анотації { #type-hints-and-annotations }
Ще раз привіт.
### Супер- та підкласи { #super-and-subclasses }
Ще раз привіт.
////
//// tab | Інформація
Єдине жорстке правило для заголовків - LLM має залишати хеш-частину в фігурних дужках незмінною, щоб посилання не ламалися.
Див. розділ `### Headings` у загальній підсказці в `scripts/translate.py`.
Для деяких мовно-специфічних інструкцій див., наприклад, розділ `### Headings` у `docs/de/llm-prompt.md`.
////
## Терміни, що використовуються в документації { #terms-used-in-the-docs }
//// tab | Тест
* ви
* ваш
* напр.
* тощо
* `foo` як `int`
* `bar` як `str`
* `baz` як `list`
* навчальний посібник - Керівництво користувача
* просунутий посібник користувача
* документація SQLModel
* документація API
* автоматична документація
* Наука про дані
* глибоке навчання
* машинне навчання
* впровадження залежностей
* HTTP базова автентифікація
* HTTP дайджест
* формат ISO
* стандарт Схеми JSON
* Схема JSON
* визначення схеми
* потік паролю
* мобільний
* застаріле
* спроєктовано
* недійсний
* на льоту
* стандарт
* типове
* чутливий до регістру
* нечутливий до регістру
* обслуговувати застосунок
* обслуговувати сторінку
* застосунок
* застосунок
* запит
* відповідь
* відповідь з помилкою
* операція шляху
* декоратор операції шляху
* функція операції шляху
* тіло
* тіло запиту
* тіло відповіді
* тіло JSON
* тіло форми
* тіло файлу
* тіло функції
* параметр
* параметр тіла
* параметр шляху
* параметр запиту
* параметр кукі
* параметр заголовка
* параметр форми
* параметр функції
* подія
* подія запуску
* запуск сервера
* подія вимкнення
* подія тривалості життя
* обробник
* обробник події
* обробник винятків
* обробляти
* модель
* модель Pydantic
* модель даних
* модель бази даних
* модель форми
* об'єкт моделі
* клас
* базовий клас
* батьківський клас
* підклас
* дочірній клас
* споріднений клас
* метод класу
* заголовок
* заголовки
* заголовок авторизації
* заголовок `Authorization`
* направлений заголовок
* система впровадження залежностей
* залежність
* залежний
* залежний
* I/O-обмежений
* CPU-обмежений
* рівночасність
* паралелізм
* багатопроцесорність
* змінна оточення
* змінна оточення
* `PATH`
* змінна `PATH`
* автентифікація
* постачальник автентифікації
* авторизація
* форма авторизації
* постачальник авторизації
* користувач автентифікується
* система автентифікує користувача
* CLI
* інтерфейс командного рядка
* сервер
* клієнт
* хмарний постачальник
* хмарний сервіс
* розробка
* етапи розробки
* словник
* словник
* перелік
* перелік
* елемент переліку
* кодувальник
* декодувальник
* кодувати
* декодувати
* виняток
* породжувати
* вираз
* оператор
* фронтенд
* бекенд
* обговорення GitHub
* проблема GitHub
* продуктивність
* оптимізація продуктивності
* тип, що повертається
* повернене значення
* безпека
* схема безпеки
* завдання
* фонове завдання
* функція завдання
* шаблон
* рушій шаблонів
* анотація типу
* підказка типу
* серверний працівник
* працівник Uvicorn
* працівник Gunicorn
* процес працівника
* клас працівника
* робоче навантаження
* розгортання
* розгортати
* SDK
* набір для розробки програмного забезпечення
* `APIRouter`
* `requirements.txt`
* токен носія
* несумісна зміна
* помилка
* кнопка
* викликаємий
* код
* фіксація
* менеджер контексту
* співпрограма
* сеанс бази даних
* диск
* домен
* рушій
* фальшивий X
* метод HTTP GET
* предмет
* бібліотека
* тривалість життя
* блокування
* проміжне програмне забезпечення
* мобільний застосунок
* модуль
* монтування
* мережа
* джерело
* переписування
* корисне навантаження
* процесор
* властивість
* представник
* запит на витяг
* запит
* пам'ять з довільним доступом
* віддалена машина
* код статусу
* строка
* мітка
* веб-фреймворк
* дика карта
* повертати
* перевіряти
////
//// tab | Інформація
Це неповний і не нормативний список (переважно) технічних термінів, що зустрічаються в документації. Він може бути корисним автору підсказки, щоб зрозуміти, для яких термінів LLM потрібна допомога. Наприклад, коли він постійно повертає хороший переклад до менш вдалого. Або коли йому складно відмінювати термін вашою мовою.
Див., наприклад, розділ `### List of English terms and their preferred German translations` у `docs/de/llm-prompt.md`.
////
================================================
FILE: docs/uk/docs/about/index.md
================================================
# Про { #about }
Про FastAPI, його проєктування, натхнення та інше. 🤓
================================================
FILE: docs/uk/docs/advanced/additional-responses.md
================================================
# Додаткові відповіді в OpenAPI { #additional-responses-in-openapi }
/// warning | Попередження
Це доволі просунута тема.
Якщо ви лише починаєте з **FastAPI**, ймовірно, вам це не потрібно.
///
Ви можете оголосити додаткові відповіді з додатковими кодами статусу, типами медіа, описами тощо.
Ці додаткові відповіді буде включено до схеми OpenAPI, тож вони з'являться і в документації API.
Але для таких додаткових відповідей потрібно повертати `Response` на кшталт `JSONResponse` безпосередньо, із потрібним кодом статусу та вмістом.
## Додаткова відповідь з `model` { #additional-response-with-model }
Ви можете передати вашим декораторам операцій шляху параметр `responses`.
Він приймає `dict`: ключі - це коди статусу для кожної відповіді (наприклад, `200`), а значення - інші `dict` з інформацією для кожної з них.
Кожен із цих словників відповіді може мати ключ `model`, що містить Pydantic-модель, подібно до `response_model`.
**FastAPI** візьме цю модель, згенерує її Схему JSON і додасть у відповідне місце в OpenAPI.
Наприклад, щоб оголосити іншу відповідь з кодом статусу `404` і Pydantic-моделлю `Message`, ви можете написати:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | Примітка
Майте на увазі, що потрібно повертати `JSONResponse` безпосередньо.
///
/// info | Інформація
Ключ `model` не є частиною OpenAPI.
**FastAPI** візьме звідти Pydantic-модель, згенерує Схему JSON і помістить у відповідне місце.
Відповідне місце це:
- У ключі `content`, значенням якого є інший JSON-об'єкт (`dict`), що містить:
- Ключ із типом медіа, напр. `application/json`, значенням якого є інший JSON-об'єкт, що містить:
- Ключ `schema`, значенням якого є Схема JSON з моделі - ось це і є правильне місце.
- **FastAPI** додає тут посилання на глобальні Схеми JSON в іншому місці вашого OpenAPI замість прямого включення. Так інші застосунки та клієнти можуть напряму використовувати ці Схеми JSON, надавати кращі інструменти генерації коду тощо.
///
Згенеровані відповіді в OpenAPI для цієї операції шляху будуть такими:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
Схеми посилаються на інше місце всередині схеми OpenAPI:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Додаткові типи медіа для основної відповіді { #additional-media-types-for-the-main-response }
Можна використати цей самий параметр `responses`, щоб додати різні типи медіа для тієї ж основної відповіді.
Наприклад, можна додати додатковий тип медіа `image/png`, оголосивши, що ваша операція шляху може повертати JSON-об'єкт (з типом медіа `application/json`) або PNG-зображення:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | Примітка
Зверніть увагу, що потрібно повертати зображення безпосередньо за допомогою `FileResponse`.
///
/// info | Інформація
Поки ви явно не вкажете інший тип медіа в параметрі `responses`, FastAPI вважатиме, що відповідь має той самий тип медіа, що й основний клас відповіді (типово `application/json`).
Але якщо ви вказали власний клас відповіді з `None` як типом медіа, FastAPI використає `application/json` для будь-якої додаткової відповіді, що має пов'язану модель.
///
## Комбінування інформації { #combining-information }
Ви також можете поєднувати інформацію про відповіді з кількох місць, зокрема з параметрів `response_model`, `status_code` і `responses`.
Ви можете оголосити `response_model`, використовуючи типовий код статусу `200` (або власний за потреби), а потім оголосити додаткову інформацію для цієї ж відповіді в `responses`, безпосередньо в схемі OpenAPI.
**FastAPI** збереже додаткову інформацію з `responses` і поєднає її зі Схемою JSON з вашої моделі.
Наприклад, ви можете оголосити відповідь з кодом статусу `404`, яка використовує Pydantic-модель і має власний `description`.
І відповідь з кодом статусу `200`, яка використовує ваш `response_model`, але містить власний `example`:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
Усе це буде поєднано та включено до вашого OpenAPI і показано в документації API:
## Комбінуйте попередньо визначені та власні відповіді { #combine-predefined-responses-and-custom-ones }
Можливо, ви захочете мати кілька попередньо визначених відповідей, що застосовуються до багатьох операцій шляху, але поєднувати їх із власними відповідями, потрібними для кожної операції шляху.
Для таких випадків можна скористатися прийомом Python «розпакування» `dict` за допомогою `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Тут `new_dict` міститиме всі пари ключ-значення з `old_dict` плюс нову пару ключ-значення:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
Цей прийом можна використати, щоб перевикористовувати деякі попередньо визначені відповіді у ваших операціях шляху та поєднувати їх із додатковими власними.
Наприклад:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## Докладніше про відповіді OpenAPI { #more-information-about-openapi-responses }
Щоб побачити, що саме можна включати у відповіді, ознайомтеся з цими розділами специфікації OpenAPI:
- [Об'єкт відповідей OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), він включає `Response Object`.
- [Об'єкт відповіді OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), ви можете включити будь-що з цього безпосередньо в кожну відповідь у параметрі `responses`. Зокрема `description`, `headers`, `content` (усередині нього ви оголошуєте різні типи медіа та Схеми JSON) і `links`.
================================================
FILE: docs/uk/docs/advanced/additional-status-codes.md
================================================
# Додаткові коди статусу { #additional-status-codes }
За замовчуванням **FastAPI** повертатиме відповіді за допомогою `JSONResponse`, поміщаючи вміст, який ви повертаєте з вашої *операції шляху*, у цей `JSONResponse`.
Він використовуватиме код статусу за замовчуванням або той, який ви встановите у своїй *операції шляху*.
## Додаткові коди статусу { #additional-status-codes_1 }
Якщо ви хочете повертати додаткові коди статусу, окрім основного, зробіть це, повертаючи `Response` безпосередньо, наприклад `JSONResponse`, і встановіть додатковий код статусу напряму.
Наприклад, припустімо, ви хочете мати *операцію шляху*, яка дозволяє оновлювати предмети та повертає код статусу HTTP 200 «OK» у разі успіху.
Але ви також хочете приймати нові предмети. І коли таких предметів раніше не існувало, операція створює їх і повертає код статусу HTTP 201 «Created».
Щоб це реалізувати, імпортуйте `JSONResponse` і поверніть ваш вміст безпосередньо там, встановивши потрібний `status_code`:
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning | Попередження
Коли ви повертаєте `Response` безпосередньо, як у прикладі вище, він і буде повернений безпосередньо.
Він не буде серіалізований за допомогою моделі тощо.
Переконайтеся, що він містить саме ті дані, які вам потрібні, і що значення є коректним JSON (якщо ви використовуєте `JSONResponse`).
///
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import JSONResponse`.
**FastAPI** надає ті самі `starlette.responses` як `fastapi.responses` просто для вашої зручності як розробника. Але більшість доступних відповідей надходить безпосередньо зі Starlette. Те саме і зі `status`.
///
## OpenAPI і документація API { #openapi-and-api-docs }
Якщо ви повертаєте додаткові коди статусу та відповіді безпосередньо, вони не будуть включені до схеми OpenAPI (документації API), адже FastAPI не має способу заздалегідь знати, що саме ви повернете.
Але ви можете задокументувати це у своєму коді, використовуючи: [Додаткові відповіді](additional-responses.md).
================================================
FILE: docs/uk/docs/advanced/advanced-dependencies.md
================================================
# Просунуті залежності { #advanced-dependencies }
## Параметризовані залежності { #parameterized-dependencies }
Усі залежності, які ми бачили, - це фіксована функція або клас.
Але можуть бути випадки, коли ви хочете мати змогу задавати параметри залежності, не оголошуючи багато різних функцій або класів.
Уявімо, що ми хочемо мати залежність, яка перевіряє, чи параметр запиту `q` містить певний фіксований вміст.
Але ми хочемо мати змогу параметризувати цей фіксований вміст.
## Екземпляр «callable» { #a-callable-instance }
У Python є спосіб зробити екземпляр класу «callable».
Не сам клас (який уже є «callable»), а екземпляр цього класу.
Щоб це зробити, оголошуємо метод `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
У цьому випадку саме `__call__` **FastAPI** використає для перевірки додаткових параметрів і підзалежностей, і саме його буде викликано, щоб передати значення параметру у вашу *функцію операції шляху* пізніше.
## Параметризувати екземпляр { #parameterize-the-instance }
Тепер ми можемо використати `__init__`, щоб оголосити параметри екземпляра, які можна застосувати для «параметризації» залежності:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
У цьому випадку **FastAPI** ніколи не торкається `__init__` і не покладається на нього - ми використовуватимемо його безпосередньо у своєму коді.
## Створити екземпляр { #create-an-instance }
Ми можемо створити екземпляр цього класу так:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
Таким чином ми «параметризуємо» нашу залежність, яка тепер містить «bar» як атрибут `checker.fixed_content`.
## Використовувати екземпляр як залежність { #use-the-instance-as-a-dependency }
Далі ми можемо використати цей `checker` у `Depends(checker)` замість `Depends(FixedContentQueryChecker)`, адже залежністю є екземпляр `checker`, а не сам клас.
Коли залежність розв'язується, **FastAPI** викличе цей `checker` так:
```Python
checker(q="somequery")
```
...і передасть усе, що він поверне, як значення залежності у нашій *функції операції шляху* як параметр `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | Порада
Усе це може здаватися надуманим. І поки що може бути не дуже зрозуміло, навіщо це корисно.
Ці приклади навмисно прості, але показують, як усе працює.
У розділах про безпеку є утилітарні функції, реалізовані таким самим способом.
Якщо ви все це зрозуміли, ви вже знаєте, як під капотом працюють ті утиліти для безпеки.
///
## Залежності з `yield`, `HTTPException`, `except` та фоновими задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Попередження
Найімовірніше, вам не знадобляться ці технічні деталі.
Вони корисні головно, якщо у вас був застосунок FastAPI старіший за 0.121.0 і ви стикаєтеся з проблемами залежностей із `yield`.
///
Залежності з `yield` еволюціонували з часом, щоб охопити різні сценарії використання та виправити деякі проблеми, ось підсумок змін.
### Залежності з `yield` і `scope` { #dependencies-with-yield-and-scope }
У версії 0.121.0 **FastAPI** додано підтримку `Depends(scope="function")` для залежностей з `yield`.
З `Depends(scope="function")` завершальний код після `yield` виконується одразу після завершення *функції операції шляху*, до того як відповідь буде надіслана клієнту.
А при використанні `Depends(scope="request")` (типове значення) завершальний код після `yield` виконується після відправлення відповіді.
Докладніше читайте в документації: [Залежності з `yield` - Ранній вихід і `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Залежності з `yield` і `StreamingResponse`, технічні деталі { #dependencies-with-yield-and-streamingresponse-technical-details }
До **FastAPI** 0.118.0, якщо ви використовували залежність із `yield`, завершальний код виконувався після повернення з *функції операції шляху*, але безпосередньо перед відправленням відповіді.
Метою було уникнути утримання ресурсів довше, ніж потрібно, очікуючи, поки відповідь пройде мережею.
Це також означало, що якщо ви повертали `StreamingResponse`, завершальний код залежності з `yield` уже було б виконано.
Наприклад, якщо у залежності з `yield` була сесія бази даних, `StreamingResponse` не зміг би використовувати цю сесію під час потокової передачі даних, тому що сесію вже закрито в завершальному коді після `yield`.
Цю поведінку змінено у 0.118.0: завершальний код після `yield` знову виконується після відправлення відповіді.
/// info | Інформація
Як побачите нижче, це дуже схоже на поведінку до версії 0.106.0, але з кількома покращеннями та виправленнями помилок у крайових випадках.
///
#### Випадки використання з раннім завершальним кодом { #use-cases-with-early-exit-code }
Є кілька сценаріїв із певними умовами, які можуть виграти від старої поведінки - виконувати завершальний код залежностей з `yield` до надсилання відповіді.
Наприклад, уявіть, що у вас є код, який використовує сесію бази даних у залежності з `yield` лише для перевірки користувача, але сесія більше не використовується у *функції операції шляху*, тільки в залежності, і відправлення відповіді триває довго - як у `StreamingResponse`, що повільно надсилає дані, але з якоїсь причини не використовує базу даних.
У такому разі сесія БД утримувалася б до завершення відправлення відповіді, але якщо ви її не використовуєте, утримувати її немає потреби.
Ось як це може виглядати:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Завершальний код - автоматичне закриття `Session` у:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...виконається після того, як відповідь завершить надсилати повільні дані:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Але оскільки `generate_stream()` не використовує сесію бази даних, немає реальної потреби тримати сесію відкритою під час надсилання відповіді.
Якщо у вас саме такий випадок із SQLModel (або SQLAlchemy), ви можете явно закрити сесію, коли вона більше не потрібна:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Так сесія звільнить з'єднання з базою даних, і його зможуть використовувати інші запити.
Якщо у вас інший сценарій, де потрібно раннє завершення залежності з `yield`, створіть, будь ласка, [питання в обговореннях GitHub](https://github.com/fastapi/fastapi/discussions/new?category=questions) із вашим конкретним випадком і поясненням, чому вам корисне раннє закриття для залежностей з `yield`.
Якщо будуть переконливі приклади для раннього закриття в залежностях з `yield`, я розгляну додавання нового способу увімкнути раннє закриття.
### Залежності з `yield` і `except`, технічні деталі { #dependencies-with-yield-and-except-technical-details }
До **FastAPI** 0.110.0, якщо ви використовували залежність із `yield`, перехоплювали виняток через `except` у цій залежності і не піднімали його знову, виняток автоматично піднімався/пересилався до будь-яких обробників винятків або внутрішнього обробника помилок сервера.
Це змінено у версії 0.110.0, щоб усунути неконтрольоване споживання пам'яті від пересланих винятків без обробника (внутрішні помилки сервера) та зробити поведінку узгодженою зі звичайним Python-кодом.
### Фонові задачі та залежності з `yield`, технічні деталі { #background-tasks-and-dependencies-with-yield-technical-details }
До **FastAPI** 0.106.0 піднімати винятки після `yield` було неможливо: завершальний код у залежностях з `yield` виконувався після надсилання відповіді, тож [обробники винятків](../tutorial/handling-errors.md#install-custom-exception-handlers) уже відпрацювали б.
Так було спроєктовано головно для того, щоб дозволити використовувати ті самі об'єкти, «віддані» залежностями через `yield`, усередині фонових задач, оскільки завершальний код виконувався після завершення фонових задач.
У **FastAPI** 0.106.0 це змінено, щоб не утримувати ресурси під час очікування, поки відповідь піде мережею.
/// tip | Порада
Крім того, фонова задача зазвичай є незалежним набором логіки, який слід обробляти окремо, з власними ресурсами (наприклад, власним з'єднанням з базою даних).
Тож так у вас, ймовірно, буде чистіший код.
///
Якщо ви раніше покладалися на цю поведінку, тепер слід створювати ресурси для фонових задач усередині самої фонової задачі та використовувати всередині лише дані, що не залежать від ресурсів залежностей із `yield`.
Наприклад, замість використання тієї самої сесії бази даних ви створюватимете нову сесію в самій фоновій задачі та отримуватимете об'єкти з бази даних, використовуючи Цю нову сесію. І далі, замість передавання об'єкта з бази даних як параметра у функцію фонової задачі, ви передасте ідентифікатор цього об'єкта, а потім отримаєте об'єкт знову всередині функції фонової задачі.
================================================
FILE: docs/uk/docs/advanced/advanced-python-types.md
================================================
# Просунуті типи Python { #advanced-python-types }
Ось кілька додаткових ідей, які можуть бути корисні під час роботи з типами в Python.
## Використання `Union` або `Optional` { #using-union-or-optional }
Якщо ваш код з якоїсь причини не може використовувати `|`, наприклад, якщо це не анотація типів, а щось на кшталт `response_model=`, замість вертикальної риски (`|`) ви можете використати `Union` з `typing`.
Наприклад, ви можете оголосити, що щось може бути `str` або `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
У `typing` також є скорочення, щоб оголосити, що щось може бути `None`, - `Optional`.
Ось порада з моєї дуже «суб'єктивної» точки зору:
- 🚨 Уникайте використання `Optional[SomeType]`
- Натомість ✨ **використовуйте `Union[SomeType, None]`** ✨.
Обидва варіанти еквівалентні і під капотом однакові, але я рекомендую `Union` замість `Optional`, тому що слово «**optional**» ніби натякає, що значення є необов'язковим, а насправді означає «може бути `None`», навіть якщо воно не є необов'язковим і все ще обов'язкове.
Вважаю, `Union[SomeType, None]` більш явно це передає.
Йдеться лише про слова та назви. Але ці слова можуть впливати на те, як ви та ваша команда думаєте про код.
Як приклад, розгляньмо цю функцію:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
Параметр `name` визначено як `Optional[str]`, але він не є необов'язковим, ви не можете викликати функцію без цього параметра:
```Python
say_hi() # О ні, це викликає помилку! 😱
```
Параметр `name` все ще обов'язковий (не «необов'язковий»), тому що не має значення за замовчуванням. Водночас `name` приймає `None` як значення:
```Python
say_hi(name=None) # Це працює, None припустимий 🎉
```
Гарна новина: у більшості випадків ви зможете просто використовувати `|`, щоб визначати об'єднання типів:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
Тож зазвичай вам не доведеться перейматися такими назвами, як `Optional` і `Union`. 😎
================================================
FILE: docs/uk/docs/advanced/async-tests.md
================================================
# Асинхронні тести { #async-tests }
Ви вже бачили, як тестувати ваші застосунки **FastAPI** за допомогою наданого `TestClient`. До цього часу ви бачили лише, як писати синхронні тести, без використання функцій `async`.
Можливість використовувати асинхронні функції у тестах може бути корисною, наприклад, коли ви асинхронно звертаєтеся до бази даних. Уявіть, що ви хочете протестувати надсилання запитів до вашого застосунку FastAPI, а потім перевірити, що ваш бекенд успішно записав коректні дані в базу даних, використовуючи асинхронну бібліотеку для бази даних.
Розгляньмо, як це реалізувати.
## pytest.mark.anyio { #pytest-mark-anyio }
Якщо ми хочемо викликати асинхронні функції у тестах, самі тестові функції мають бути асинхронними. AnyIO надає зручний плагін, який дозволяє вказати, що деякі тестові функції слід виконувати асинхронно.
## HTTPX { #httpx }
Навіть якщо ваш застосунок **FastAPI** використовує звичайні функції `def` замість `async def`, під капотом це все одно `async`-застосунок.
`TestClient` робить певну «магію» всередині, щоб викликати асинхронний застосунок FastAPI у ваших звичайних тестових функціях `def`, використовуючи стандартний pytest. Але ця «магія» більше не працює, коли ми використовуємо його всередині асинхронних функцій. Запускаючи тести асинхронно, ми більше не можемо використовувати `TestClient` у наших тестових функціях.
`TestClient` побудований на основі [HTTPX](https://www.python-httpx.org), і на щастя, ми можемо використовувати його безпосередньо для тестування API.
## Приклад { #example }
Для простого прикладу розгляньмо структуру файлів, подібну до описаної в [Більші застосунки](../tutorial/bigger-applications.md) та [Тестування](../tutorial/testing.md):
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Файл `main.py` міститиме:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
Файл `test_main.py` міститиме тести для `main.py`, тепер це може виглядати так:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## Запуск { #run-it }
Ви можете запустити тести як зазвичай:
```console
$ pytest
---> 100%
```
## Докладно { #in-detail }
Маркер `@pytest.mark.anyio` повідомляє pytest, що цю тестову функцію слід викликати асинхронно:
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
/// tip | Порада
Зауважте, що тестова функція тепер `async def` замість просто `def`, як це було раніше при використанні `TestClient`.
///
Далі ми можемо створити `AsyncClient` із застосунком і надсилати до нього асинхронні запити, використовуючи `await`.
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
Це еквівалентно:
```Python
response = client.get('/')
```
...що ми раніше використовували для надсилання запитів за допомогою `TestClient`.
/// tip | Порада
Зауважте, що ми використовуємо async/await із новим `AsyncClient` - запит є асинхронним.
///
/// warning | Попередження
Якщо ваш застосунок залежить від подій тривалості життя, `AsyncClient` не ініціюватиме ці події. Щоб гарантувати їх ініціалізацію, використовуйте `LifespanManager` з [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).
///
## Інші асинхронні виклики функцій { #other-asynchronous-function-calls }
Оскільки тестова функція тепер асинхронна, ви також можете викликати (і `await`) інші `async`-функції окрім надсилання запитів до вашого застосунку FastAPI у тестах - так само, як ви робили б це будь-де у вашому коді.
/// tip | Порада
Якщо ви натрапили на `RuntimeError: Task attached to a different loop` під час інтеграції асинхронних викликів у ваші тести (наприклад, при використанні [MongoDB's MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), пам'ятайте створювати об'єкти, яким потрібен цикл подій, лише всередині асинхронних функцій, наприклад, у зворотному виклику `@app.on_event("startup")`.
///
================================================
FILE: docs/uk/docs/advanced/behind-a-proxy.md
================================================
# За представником { #behind-a-proxy }
У багатьох випадках ви будете використовувати **представника** на кшталт Traefik або Nginx перед вашим застосунком FastAPI.
Такі представники можуть обробляти сертифікати HTTPS та інші речі.
## Направлені заголовки представника { #proxy-forwarded-headers }
**Представник** перед вашим застосунком зазвичай встановлює деякі заголовки на льоту перед відправленням запитів до вашого **сервера**, щоб повідомити серверу, що запит було **направлено** через представника, вказати оригінальну (публічну) URL-адресу, включно з доменом, що використовується HTTPS тощо.
Програмний **сервер** (наприклад, **Uvicorn** через **FastAPI CLI**) здатний інтерпретувати ці заголовки і передавати цю інформацію вашому застосунку.
Але з міркувань безпеки, оскільки сервер не знає, що він працює за довіреним представником, він не інтерпретуватиме ці заголовки.
/// note | Технічні деталі
Заголовки представника:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
### Увімкнути направлені заголовки представника { #enable-proxy-forwarded-headers }
Ви можете запустити FastAPI CLI з *опцією CLI* `--forwarded-allow-ips` і передати IP-адреси, яким слід довіряти для читання цих направлених заголовків.
Якщо ви встановите `--forwarded-allow-ips="*"`, це означатиме довіру до всіх вхідних IP.
Якщо ваш **сервер** знаходиться за довіреним **представником** і тільки представник спілкується з ним, це змусить сервер приймати будь-яку IP-адресу цього **представника**.
```console
$ fastapi run --forwarded-allow-ips="*"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Перенаправлення з HTTPS { #redirects-with-https }
Наприклад, припустимо, ви визначили *операцію шляху* `/items/`:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Якщо клієнт спробує перейти до `/items`, за замовчуванням буде виконано перенаправлення на `/items/`.
Але до встановлення *опції CLI* `--forwarded-allow-ips` це могло б перенаправити на `http://localhost:8000/items/`.
Втім, можливо ваш застосунок розміщено на `https://mysuperapp.com`, і перенаправлення має бути на `https://mysuperapp.com/items/`.
Налаштувавши `--proxy-headers`, FastAPI зможе перенаправляти на правильну адресу. 😎
```
https://mysuperapp.com/items/
```
/// tip | Порада
Якщо хочете дізнатися більше про HTTPS, перегляньте посібник [Про HTTPS](../deployment/https.md).
///
### Як працюють направлені заголовки представника { #how-proxy-forwarded-headers-work }
Ось візуальне подання того, як **представник** додає направлені заголовки між клієнтом і **сервером застосунку**:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request Host: mysuperapp.com Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request X-Forwarded-For: [client IP] X-Forwarded-Proto: https X-Forwarded-Host: mysuperapp.com Path: /items
Note over Server: Server interprets headers (if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
**Представник** перехоплює початковий запит клієнта і додає спеціальні *направлені* заголовки (`X-Forwarded-*`) перед передачею запиту на **сервер застосунку**.
Ці заголовки зберігають інформацію про оригінальний запит, яка інакше була б втрачена:
- X-Forwarded-For: оригінальна IP-адреса клієнта
- X-Forwarded-Proto: оригінальний протокол (`https`)
- X-Forwarded-Host: оригінальний хост (`mysuperapp.com`)
Коли **FastAPI CLI** налаштовано з `--forwarded-allow-ips`, він довіряє цим заголовкам і використовує їх, наприклад, для побудови коректних URL-адрес у перенаправленнях.
## Представник із вилученим префіксом шляху { #proxy-with-a-stripped-path-prefix }
У вас може бути представник, що додає префікс шляху до вашого застосунку.
У таких випадках ви можете використати `root_path` для конфігурації застосунку.
`root_path` - це механізм, наданий специфікацією ASGI (на якій побудовано FastAPI через Starlette).
`root_path` використовується для обробки саме таких випадків.
Його також використано внутрішньо під час монтування підзастосунків.
Наявність представника з вилученим префіксом шляху означає, що ви могли б оголосити шлях `/app` у вашому коді, але потім додати шар зверху (представника), який розмістить ваш застосунок **FastAPI** під шляхом на кшталт `/api/v1`.
У такому разі початковий шлях `/app` фактично буде доступний за `/api/v1/app`.
Хоча весь ваш код написано з припущенням, що є лише `/app`.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
І представник буде **«зрізати»** **префікс шляху** на льоту перед передачею запиту серверу застосунку (ймовірно, Uvicorn через FastAPI CLI), зберігаючи переконання вашого застосунку, що його обслуговують за `/app`, тож вам не доведеться оновлювати весь код, щоб додати префікс `/api/v1`.
Дотепер усе працювало б як зазвичай.
Але коли ви відкриєте вбудований інтерфейс документації (фронтенд), він очікуватиме отримати схему OpenAPI за `/openapi.json`, замість `/api/v1/openapi.json`.
Отже, фронтенд (що працює у браузері) спробує звернутися до `/openapi.json` і не зможе отримати схему OpenAPI.
Оскільки у нас є представник з префіксом шляху `/api/v1` для нашого застосунку, фронтенду потрібно отримувати схему OpenAPI за `/api/v1/openapi.json`.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | Порада
IP `0.0.0.0` зазвичай означає, що програма слухає на всіх IP-адресах, доступних на цій машині/сервері.
///
Інтерфейсу документації також потрібно, щоб схема OpenAPI оголошувала, що цей `server` API розташований за `/api/v1` (за представником). Наприклад:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Ще дещо тут
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Ще дещо тут
}
}
```
У цьому прикладі «Proxy» може бути чимось на кшталт **Traefik**. А сервером буде щось на кшталт FastAPI CLI з **Uvicorn**, який запускає ваш застосунок FastAPI.
### Задання `root_path` { #providing-the-root-path }
Щоб цього досягти, ви можете використати опцію командного рядка `--root-path`, наприклад:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Якщо ви використовуєте Hypercorn, у нього також є опція `--root-path`.
/// note | Технічні деталі
Специфікація ASGI визначає `root_path` для такого випадку використання.
А опція командного рядка `--root-path` надає цей `root_path`.
///
### Перевірка поточного `root_path` { #checking-the-current-root-path }
Ви можете отримати поточний `root_path`, який використовує ваш застосунок для кожного запиту, він є частиною словника `scope` (це частина специфікації ASGI).
Тут ми додаємо його у повідомлення лише для демонстрації.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Потім, якщо ви запустите Uvicorn так:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Відповідь буде приблизно такою:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### Встановлення `root_path` у застосунку FastAPI { #setting-the-root-path-in-the-fastapi-app }
Альтернативно, якщо у вас немає способу передати опцію командного рядка на кшталт `--root-path` чи еквівалентну, ви можете встановити параметр `root_path` під час створення вашого застосунку FastAPI:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
Передача `root_path` до `FastAPI` еквівалентна передачі опції командного рядка `--root-path` до Uvicorn або Hypercorn.
### Про `root_path` { #about-root-path }
Майте на увазі, що сервер (Uvicorn) не використовуватиме `root_path` ні для чого, окрім передачі його застосунку.
Але якщо ви перейдете у вашому браузері на [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), ви побачите звичайну відповідь:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Тобто він не очікуватиме доступу за адресою `http://127.0.0.1:8000/api/v1/app`.
Uvicorn очікуватиме, що представник буде звертатися до Uvicorn на `http://127.0.0.1:8000/app`, і тоді саме представник відповідатиме за додавання додаткового префікса `/api/v1` зверху.
## Про представників із вилученим префіксом шляху { #about-proxies-with-a-stripped-path-prefix }
Майте на увазі, що представник із вилученим префіксом шляху - це лише один зі способів його налаштування.
Ймовірно, в багатьох випадках за замовчуванням представник не матиме вилученого префікса шляху.
У такому випадку (без вилученого префікса шляху) представник слухатиме щось на кшталт `https://myawesomeapp.com`, і коли браузер перейде на `https://myawesomeapp.com/api/v1/app`, а ваш сервер (наприклад, Uvicorn) слухає на `http://127.0.0.1:8000`, представник (без вилученого префікса) звернеться до Uvicorn за тим самим шляхом: `http://127.0.0.1:8000/api/v1/app`.
## Локальне тестування з Traefik { #testing-locally-with-traefik }
Ви можете легко провести експеримент локально з вилученим префіксом шляху, використовуючи [Traefik](https://docs.traefik.io/).
[Завантажте Traefik](https://github.com/containous/traefik/releases), це один бінарний файл, ви можете розпакувати архів і запустити його безпосередньо з термінала.
Потім створіть файл `traefik.toml` з таким вмістом:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Це вказує Traefik слухати порт 9999 і використовувати інший файл `routes.toml`.
/// tip | Порада
Ми використовуємо порт 9999 замість стандартного HTTP-порту 80, щоб вам не довелося запускати його з адмін-повноваженнями (`sudo`).
///
Тепер створіть інший файл `routes.toml`:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Цей файл налаштовує Traefik на використання префікса шляху `/api/v1`.
Потім Traefik переспрямовуватиме свої запити до вашого Uvicorn, який працює на `http://127.0.0.1:8000`.
Тепер запустіть Traefik:
А тепер запустіть ваш застосунок, використовуючи опцію `--root-path`:
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
### Перевірте відповіді { #check-the-responses }
Тепер, якщо ви перейдете за URL із портом Uvicorn: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), ви побачите звичайну відповідь:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | Порада
Зверніть увагу, що хоча ви звертаєтеся за адресою `http://127.0.0.1:8000/app`, відображається `root_path` `/api/v1`, взятий із опції `--root-path`.
///
А тепер відкрийте URL із портом Traefik, включно з префіксом шляху: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app).
Ми отримуємо ту саму відповідь:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
але цього разу за URL із префіксом шляху, який надає представник: `/api/v1`.
Звісно, ідея полягає в тому, що всі мають доступатися до застосунку через представника, тож варіант із префіксом шляху `/api/v1` є «правильним».
А варіант без префікса шляху (`http://127.0.0.1:8000/app`), який надає безпосередньо Uvicorn, має бути виключно для того, щоб _представник_ (Traefik) звертався до нього.
Це демонструє, як Представник (Traefik) використовує префікс шляху і як сервер (Uvicorn) використовує `root_path` з опції `--root-path`.
### Перевірте інтерфейс документації { #check-the-docs-ui }
А тепер до цікавої частини. ✨
«Офіційний» спосіб доступу до застосунку - через представника з префіксом шляху, який ми визначили. Тож, як і очікується, якщо ви спробуєте інтерфейс документації, який обслуговує безпосередньо Uvicorn без префікса шляху в URL, це не запрацює, оскільки він очікує доступу через представника.
Ви можете перевірити це на [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs):
Але якщо ми звернемося до інтерфейсу документації за «офіційним» URL, використовуючи представника з портом `9999`, за адресою `/api/v1/docs`, усе працює коректно! 🎉
Ви можете перевірити це на [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs):
Саме так, як ми хотіли. ✔️
Це тому, що FastAPI використовує `root_path`, щоб створити типовий `server` в OpenAPI з URL, наданою `root_path`.
## Додаткові сервери { #additional-servers }
/// warning | Попередження
Це більш просунутий випадок використання. Можете пропустити його.
///
За замовчуванням **FastAPI** створить `server` у схемі OpenAPI з URL для `root_path`.
Але ви також можете надати інші альтернативні `servers`, наприклад, якщо хочете, щоб той самий інтерфейс документації взаємодіяв і зі стейджингом, і з продакшном.
Якщо ви передасте власний список `servers`, і є `root_path` (тому що ваш API знаходиться за представником), **FastAPI** вставить «server» з цим `root_path` на початок списку.
Наприклад:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Буде згенерована схема OpenAPI на кшталт:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Ще дещо тут
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Ще дещо тут
}
}
```
/// tip | Порада
Зверніть увагу на автоматично згенерований сервер із значенням `url` `/api/v1`, взятим із `root_path`.
///
В інтерфейсі документації за адресою [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) це виглядатиме так:
/// tip | Порада
Інтерфейс документації взаємодіятиме з сервером, який ви оберете.
///
/// note | Технічні деталі
Властивість `servers` у специфікації OpenAPI є необовʼязковою.
Якщо ви не вкажете параметр `servers`, і `root_path` дорівнює `/`, властивість `servers` у згенерованій схемі OpenAPI буде повністю пропущено за замовчуванням, що еквівалентно одному серверу зі значенням `url` рівним `/`.
///
### Вимкнути автоматичний сервер із `root_path` { #disable-automatic-server-from-root-path }
Якщо ви не хочете, щоб **FastAPI** додавав автоматичний сервер, використовуючи `root_path`, скористайтеся параметром `root_path_in_servers=False`:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
і тоді він не буде включений у схему OpenAPI.
## Монтування підзастосунку { #mounting-a-sub-application }
Якщо вам потрібно змонтувати підзастосунок (як описано в [Підзастосунки - монтування](sub-applications.md)), одночасно використовуючи представника з `root_path`, ви можете робити це звичайним чином, як і очікуєте.
FastAPI внутрішньо розумно використовуватиме `root_path`, тож усе просто працюватиме. ✨
================================================
FILE: docs/uk/docs/advanced/custom-response.md
================================================
# Користувацька відповідь - HTML, стрім, файл, інше { #custom-response-html-stream-file-others }
Типово **FastAPI** повертатиме JSON-відповіді.
Ви можете переписати це, повернувши `Response` безпосередньо, як показано в [Повернути відповідь безпосередньо](response-directly.md).
Але якщо ви повертаєте `Response` безпосередньо (або будь-який його підклас, як-от `JSONResponse`), дані не будуть автоматично конвертовані (навіть якщо ви оголосите `response_model`), і документація не буде автоматично згенерована (наприклад, з включенням конкретного «медіа-типу» в HTTP-заголовку `Content-Type` як частини згенерованого OpenAPI).
Ви також можете оголосити `Response`, який слід використовувати (наприклад, будь-який підклас `Response`), у декораторі операції шляху через параметр `response_class`.
Вміст, який ви повертаєте з вашої функції операції шляху, буде поміщений усередину цього `Response`.
/// note | Примітка
Якщо ви використовуєте клас відповіді без медіа-типу, FastAPI очікуватиме, що у вашої відповіді не буде вмісту, тож формат відповіді не буде задокументовано в згенерованій документації OpenAPI.
///
## JSON-відповіді { #json-responses }
Типово FastAPI повертає JSON-відповіді.
Якщо ви оголосите [Модель відповіді](../tutorial/response-model.md), FastAPI використає її, щоб серіалізувати дані в JSON за допомогою Pydantic.
Якщо ви не оголосите модель відповіді, FastAPI використає `jsonable_encoder`, пояснений у [Сумісний кодувальник JSON](../tutorial/encoder.md), і помістить результат у `JSONResponse`.
Якщо ви оголосите `response_class` з JSON медіа-типом (`application/json`), як у випадку з `JSONResponse`, дані, що повертаються, будуть автоматично перетворені (і відфільтровані) згідно з будь-якою Pydantic `response_model`, яку ви оголосили в декораторі операції шляху. Але дані не будуть серіалізовані в JSON-байти за допомогою Pydantic, натомість вони будуть перетворені з `jsonable_encoder`, а потім передані класу `JSONResponse`, який і серіалізує їх у байти, використовуючи стандартну JSON-бібліотеку в Python.
### Продуктивність JSON { #json-performance }
Коротко: якщо вам потрібна максимальна продуктивність, використовуйте [Модель відповіді](../tutorial/response-model.md) і не оголошуйте `response_class` у декораторі операції шляху.
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
## HTML-відповідь { #html-response }
Щоб повернути відповідь із HTML безпосередньо з **FastAPI**, використовуйте `HTMLResponse`.
- Імпортуйте `HTMLResponse`.
- Передайте `HTMLResponse` як параметр `response_class` вашого декоратора операції шляху.
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
/// info | Інформація
Параметр `response_class` також визначатиме «медіа-тип» відповіді.
У цьому випадку HTTP-заголовок `Content-Type` буде встановлено в `text/html`.
І це буде задокументовано відповідно в OpenAPI.
///
### Повернути `Response` { #return-a-response }
Як показано в [Повернути відповідь безпосередньо](response-directly.md), ви також можете переписати відповідь безпосередньо у вашій операції шляху, просто повернувши її.
Той самий приклад вище, що повертає `HTMLResponse`, може виглядати так:
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
/// warning | Попередження
`Response`, повернений безпосередньо вашою функцією операції шляху, не буде задокументовано в OpenAPI (наприклад, `Content-Type` не буде задокументовано) і не буде видно в автоматичній інтерактивній документації.
///
/// info | Інформація
Звісно, фактичні заголовок `Content-Type`, код статусу тощо прийдуть з об'єкта `Response`, який ви повернули.
///
### Задокументуйте в OpenAPI і перепишіть `Response` { #document-in-openapi-and-override-response }
Якщо ви хочете переписати відповідь усередині функції, але водночас задокументувати «медіа-тип» в OpenAPI, ви можете використати параметр `response_class` І повернути об'єкт `Response`.
Тоді `response_class` буде використано лише для документування операції шляху в OpenAPI, а ваша `Response` буде використана як є.
#### Повернути `HTMLResponse` безпосередньо { #return-an-htmlresponse-directly }
Наприклад, це може бути щось на кшталт:
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
У цьому прикладі функція `generate_html_response()` уже генерує та повертає `Response` замість повернення HTML як `str`.
Повертаючи результат виклику `generate_html_response()`, ви вже повертаєте `Response`, яка перепише типову поведінку **FastAPI**.
Але оскільки ви також передали `HTMLResponse` у `response_class`, **FastAPI** знатиме, як задокументувати це в OpenAPI та інтерактивній документації як HTML з `text/html`:
## Доступні відповіді { #available-responses }
Ось деякі з доступних відповідей.
Майте на увазі, що ви можете використовувати `Response`, щоб повертати що завгодно інше, або навіть створити власний підклас.
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import HTMLResponse`.
**FastAPI** надає ті ж `starlette.responses` як `fastapi.responses` лише для вашої зручності як розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette.
///
### `Response` { #response }
Головний клас `Response`, від якого успадковуються всі інші відповіді.
Ви можете повертати його безпосередньо.
Він приймає такі параметри:
- `content` - `str` або `bytes`.
- `status_code` - `int` - код статусу HTTP.
- `headers` - `dict` строк.
- `media_type` - `str`, що задає медіа-тип, напр. `"text/html"`.
FastAPI (насправді Starlette) автоматично додасть заголовок Content-Length. Також буде додано заголовок Content-Type на основі `media_type` з додаванням набору символів для текстових типів.
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Приймає текст або байти та повертає HTML-відповідь, як описано вище.
### `PlainTextResponse` { #plaintextresponse }
Приймає текст або байти та повертає відповідь звичайним текстом.
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
Приймає дані та повертає відповідь, закодовану як `application/json`.
Це типова відповідь, яку використовує **FastAPI**, як зазначено вище.
/// note | Технічні деталі
Але якщо ви оголосите модель відповіді або тип, його буде використано безпосередньо для серіалізації даних у JSON, і відповідь з коректним медіа-типом для JSON буде повернута безпосередньо, без використання класу `JSONResponse`.
Це ідеальний спосіб отримати найкращу продуктивність.
///
### `RedirectResponse` { #redirectresponse }
Повертає HTTP-перенаправлення. Типово використовує код статусу 307 (Temporary Redirect).
Ви можете повернути `RedirectResponse` безпосередньо:
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
---
Або ви можете використати його в параметрі `response_class`:
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
У такому разі ви можете повертати URL безпосередньо з вашої функції операції шляху.
У цьому випадку `status_code` буде типовим для `RedirectResponse`, тобто `307`.
---
Ви також можете використати параметр `status_code` разом із параметром `response_class`:
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Приймає async-генератор або звичайний генератор/ітератор (функцію з `yield`) і потоково передає тіло відповіді.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *}
/// note | Технічні деталі
Завдання `async` може бути скасовано лише тоді, коли воно досягає `await`. Якщо немає `await`, генератор (функція з `yield`) не може бути коректно скасований і може продовжувати працювати навіть після запиту на скасування.
Оскільки цьому невеликому прикладу не потрібні жодні оператори `await`, ми додаємо `await anyio.sleep(0)`, щоб надати циклу подій шанс обробити скасування.
Це ще важливіше для великих або нескінченних потоків.
///
/// tip | Порада
Замість того щоб повертати `StreamingResponse` безпосередньо, імовірно, краще дотримуватися стилю в [Потокова передача даних](./stream-data.md), це значно зручніше та обробляє скасування «за лаштунками» для вас.
Якщо ви транслюєте JSON Lines, дотримуйтесь навчального посібника [Потоки JSON Lines](../tutorial/stream-json-lines.md).
///
### `FileResponse` { #fileresponse }
Асинхронно транслює файл як відповідь.
Приймає інший набір аргументів для створення екземпляра, ніж інші типи відповідей:
- `path` - шлях до файлу для трансляції.
- `headers` - будь-які користувацькі заголовки як словник.
- `media_type` - строка, що задає медіа-тип. Якщо не встановлено, медіа-тип буде виведено з імені файлу або шляху.
- `filename` - якщо встановлено, буде включено до `Content-Disposition` відповіді.
Відповіді з файлами включатимуть відповідні заголовки `Content-Length`, `Last-Modified` і `ETag`.
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
Ви також можете використати параметр `response_class`:
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
У цьому випадку ви можете повертати шлях до файлу безпосередньо з вашої функції операції шляху.
## Власний клас відповіді { #custom-response-class }
Ви можете створити власний клас відповіді, успадкувавши його від `Response`, і використовувати його.
Наприклад, скажімо, ви хочете використовувати [`orjson`](https://github.com/ijl/orjson) з деякими налаштуваннями.
Припустімо, ви хочете, щоб повертався відформатований із відступами JSON, тож ви хочете використати опцію orjson `orjson.OPT_INDENT_2`.
Ви можете створити `CustomORJSONResponse`. Головне, що потрібно зробити, це створити метод `Response.render(content)`, який повертає вміст як `bytes`:
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
Тепер замість повернення:
```json
{"message": "Hello World"}
```
...ця відповідь повертатиме:
```json
{
"message": "Hello World"
}
```
Звісно, ви, ймовірно, знайдете значно кращі способи скористатися цим, ніж просто форматування JSON. 😉
### `orjson` або Модель відповіді { #orjson-or-response-model }
Якщо ви шукаєте продуктивність, імовірно, краще використати [Модель відповіді](../tutorial/response-model.md), ніж відповідь `orjson`.
З моделлю відповіді FastAPI використає Pydantic, щоб серіалізувати дані в JSON без проміжних кроків, як-от перетворення за допомогою `jsonable_encoder`, що відбувалося б в іншому випадку.
І «під капотом» Pydantic використовує ті самі внутрішні механізми Rust, що й `orjson`, для серіалізації в JSON, тож ви вже отримаєте найкращу продуктивність із моделлю відповіді.
## Типова відповідь за замовчуванням { #default-response-class }
Створюючи екземпляр класу **FastAPI** або `APIRouter`, ви можете вказати, який клас відповіді використовувати за замовчуванням.
Параметр, що це визначає, - `default_response_class`.
У прикладі нижче **FastAPI** використовуватиме `HTMLResponse` за замовчуванням в усіх операціях шляху, замість JSON.
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
/// tip | Порада
Ви все одно можете переписати `response_class` в операціях шляху, як і раніше.
///
## Додаткова документація { #additional-documentation }
Ви також можете оголосити медіа-тип і багато інших деталей в OpenAPI, використовуючи `responses`: [Додаткові відповіді в OpenAPI](additional-responses.md).
================================================
FILE: docs/uk/docs/advanced/dataclasses.md
================================================
# Використання dataclasses { #using-dataclasses }
FastAPI побудовано поверх **Pydantic**, і я показував вам, як використовувати моделі Pydantic для оголошення запитів і відповідей.
Але FastAPI також підтримує використання [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) таким самим чином:
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Це підтримується завдяки **Pydantic**, адже він має [внутрішню підтримку `dataclasses`](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel).
Тож навіть із наведеним вище кодом, який явно не використовує Pydantic, FastAPI використовує Pydantic, щоб перетворити стандартні dataclasses у власний варіант dataclasses Pydantic.
І, звісно, підтримується те саме:
* валідація даних
* серіалізація даних
* документація даних тощо
Це працює так само, як із моделями Pydantic. Насправді під капотом це також досягається за допомогою Pydantic.
/// info
Майте на увазі, що dataclasses не можуть робити все те, що можуть моделі Pydantic.
Тож вам усе ще може знадобитися використовувати моделі Pydantic.
Але якщо у вас вже є чимало dataclasses, це зручний трюк, щоб задіяти їх для веб-API на FastAPI. 🤓
///
## Dataclasses у `response_model` { #dataclasses-in-response-model }
Ви також можете використовувати `dataclasses` у параметрі `response_model`:
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Dataclass буде автоматично перетворено на dataclass Pydantic.
Таким чином його схема з'явиться в інтерфейсі користувача документації API:
## Dataclasses у вкладених структурах даних { #dataclasses-in-nested-data-structures }
Можна поєднувати `dataclasses` з іншими анотаціями типів, щоб створювати вкладені структури даних.
У деяких випадках вам усе ж доведеться використовувати варіант `dataclasses` від Pydantic. Наприклад, якщо виникають помилки з автоматично згенерованою документацією API.
У такому разі ви можете просто замінити стандартні `dataclasses` на `pydantic.dataclasses`, що є взаємозамінником:
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Ми все ще імпортуємо `field` зі стандартних `dataclasses`.
2. `pydantic.dataclasses` - це взаємозамінник для `dataclasses`.
3. Dataclass `Author` містить список dataclass `Item`.
4. Dataclass `Author` використовується як параметр `response_model`.
5. Ви можете використовувати інші стандартні анотації типів із dataclasses як тіло запиту.
У цьому випадку це список dataclass `Item`.
6. Тут ми повертаємо словник, що містить `items`, який є списком dataclass.
FastAPI усе ще здатний серіалізувати дані до JSON.
7. Тут у `response_model` використано анотацію типу список dataclass `Author`.
Знову ж, ви можете поєднувати `dataclasses` зі стандартними анотаціями типів.
8. Зверніть увагу, що ця *функція операції шляху* використовує звичайний `def` замість `async def`.
Як завжди, у FastAPI ви можете поєднувати `def` і `async def` за потреби.
Якщо вам потрібне коротке нагадування, коли що використовувати, перегляньте розділ _«Поспішаєте?»_ у документації про [`async` та `await`](../async.md#in-a-hurry).
9. Ця *функція операції шляху* не повертає dataclasses (хоча могла б), а список словників із внутрішніми даними.
FastAPI використає параметр `response_model` (що включає dataclasses), щоб перетворити відповідь.
Ви можете поєднувати `dataclasses` з іншими анотаціями типів у багатьох поєднаннях, щоб формувати складні структури даних.
Перегляньте підказки щодо анотацій у коді вище, щоб побачити більше деталей.
## Дізнатися більше { #learn-more }
Можна поєднувати `dataclasses` з іншими моделями Pydantic, наслідувати їх, включати у власні моделі тощо.
Щоб дізнатися більше, перегляньте [документацію Pydantic про dataclasses](https://docs.pydantic.dev/latest/concepts/dataclasses/).
## Версія { #version }
Доступно починаючи з версії FastAPI `0.67.0`. 🔖
================================================
FILE: docs/uk/docs/advanced/events.md
================================================
# Події тривалості життя { #lifespan-events }
Ви можете визначити логіку (код), яку слід виконати перед тим, як застосунок запуститься. Це означає, що цей код буде виконано один раз, перед тим як застосунок почне отримувати запити.
Так само ви можете визначити логіку (код), яку слід виконати під час вимкнення застосунку. У цьому випадку код буде виконано один раз, після обробки можливо багатьох запитів.
Оскільки цей код виконується перед тим, як застосунок почне приймати запити, і одразу після того, як він завершить їх обробку, він охоплює всю тривалість життя застосунку (слово «lifespan» буде важливим за мить 😉).
Це дуже корисно для налаштування ресурсів, які потрібні для всього застосунку, які спільні між запитами, та/або які потрібно потім прибрати. Наприклад, пул з’єднань з базою даних або завантаження спільної моделі машинного навчання.
## Випадок використання { #use-case }
Почнемо з прикладу випадку використання, а потім подивимось, як це вирішити.
Уявімо, що у вас є моделі машинного навчання, якими ви хочете обробляти запити. 🤖
Ті самі моделі спільні між запитами, тобто це не окрема модель на запит чи на користувача.
Уявімо, що завантаження моделі може займати чимало часу, бо треба читати багато даних з диска. Тож ви не хочете робити це для кожного запиту.
Ви могли б завантажити її на верхньому рівні модуля/файлу, але це означало б, що модель завантажиться навіть якщо ви просто запускаєте простий автоматизований тест - тоді тест буде повільним, бо йому доведеться чекати завантаження моделі перед виконанням незалежної частини коду.
Ось це ми й вирішимо: завантажимо модель перед обробкою запитів, але лише безпосередньо перед тим, як застосунок почне отримувати запити, а не під час завантаження коду.
## Тривалість життя { #lifespan }
Ви можете визначити цю логіку запуску і вимкнення за допомогою параметра `lifespan` застосунку `FastAPI` та «менеджера контексту» (зараз покажу, що це).
Почнемо з прикладу, а потім розберемо детально.
Ми створюємо асинхронну функцію `lifespan()` з `yield` так:
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Тут ми імітуємо дорогу операцію запуску із завантаженням моделі, поміщаючи (фальшиву) функцію моделі у словник з моделями машинного навчання перед `yield`. Цей код буде виконано перед тим, як застосунок почне приймати запити, під час запуску.
А одразу після `yield` ми розвантажуємо модель. Цей код буде виконано після того, як застосунок завершить обробку запитів, безпосередньо перед вимкненням. Це, наприклад, може звільнити ресурси на кшталт пам’яті або GPU.
/// tip | Порада
Подія `shutdown` відбувається, коли ви зупиняєте застосунок.
Можливо, вам треба запустити нову версію, або ви просто втомилися її запускати. 🤷
///
### Функція тривалості життя { #lifespan-function }
Перше, на що слід звернути увагу: ми визначаємо асинхронну функцію з `yield`. Це дуже схоже на залежності з `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
Перша частина функції до `yield` буде виконана перед запуском застосунку.
А частина після `yield` буде виконана після завершення роботи застосунку.
### Асинхронний менеджер контексту { #async-context-manager }
Якщо придивитися, функція задекорована за допомогою `@asynccontextmanager`.
Це перетворює функцію на так званий «асинхронний менеджер контексту».
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Менеджер контексту в Python - це те, що можна використовувати в операторі `with`, наприклад, `open()` можна використовувати як менеджер контексту:
```Python
with open("file.txt") as file:
file.read()
```
У новіших версіях Python також є асинхронний менеджер контексту. Його використовують з `async with`:
```Python
async with lifespan(app):
await do_stuff()
```
Коли ви створюєте менеджер контексту або асинхронний менеджер контексту, як вище, перед входом у блок `with` буде виконано код перед `yield`, а після виходу з блоку `with` буде виконано код після `yield`.
У нашому прикладі коду вище ми не використовуємо його напряму, а передаємо його до FastAPI, щоб він його використав.
Параметр `lifespan` застосунку `FastAPI` приймає асинхронний менеджер контексту, тож ми можемо передати йому наш новий асинхронний менеджер контексту `lifespan`.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Альтернативні події (застаріло) { #alternative-events-deprecated }
/// warning | Попередження
Рекомендований спосіб обробляти запуск і вимкнення - використовувати параметр `lifespan` застосунку `FastAPI`, як описано вище. Якщо ви надаєте параметр `lifespan`, обробники подій `startup` і `shutdown` більше не будуть викликані. Або все через `lifespan`, або все через події - не обидва одночасно.
Можете, ймовірно, пропустити цю частину.
///
Є альтернативний спосіб визначити логіку, яку слід виконати під час запуску і під час вимкнення.
Ви можете визначити обробники подій (функції), які потрібно виконати перед запуском застосунку або коли застосунок вимикається.
Ці функції можна оголошувати як з `async def`, так і звичайним `def`.
### Подія `startup` { #startup-event }
Щоб додати функцію, яку слід виконати перед запуском застосунку, оголосіть її з подією `"startup"`:
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
У цьому випадку функція-обробник події `startup` ініціалізує «базу даних» предметів (це лише `dict`) деякими значеннями.
Ви можете додати більше ніж один обробник події.
І ваш застосунок не почне приймати запити, доки всі обробники події `startup` не завершаться.
### Подія `shutdown` { #shutdown-event }
Щоб додати функцію, яку слід виконати, коли застосунок вимикається, оголосіть її з подією `"shutdown"`:
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Тут функція-обробник події `shutdown` запише текстовий рядок `"Application shutdown"` у файл `log.txt`.
/// info | Інформація
У функції `open()` параметр `mode="a"` означає «append», тож рядок буде додано після всього, що є у файлі, без перезапису попереднього вмісту.
///
/// tip | Порада
Зауважте, що в цьому випадку ми використовуємо стандартну Python-функцію `open()`, яка працює з файлом.
Тобто вона включає I/O (input/output), де потрібно «чекати», поки дані буде записано на диск.
Але `open()` не використовує `async` і `await`.
Тому ми оголошуємо функцію-обробник події зі стандартним `def`, а не `async def`.
///
### Разом `startup` і `shutdown` { #startup-and-shutdown-together }
Велика ймовірність, що логіка для вашого запуску і вимкнення пов’язана: ви можете хотіти щось запустити, а потім завершити, отримати ресурс, а потім звільнити його тощо.
Робити це в окремих функціях, які не діляться логікою чи змінними, складніше - доведеться зберігати значення у глобальних змінних або вдаватися до подібних трюків.
Тому зараз рекомендується натомість використовувати `lifespan`, як пояснено вище.
## Технічні деталі { #technical-details }
Невелика технічна деталь для допитливих нердів. 🤓
Під капотом, у технічній специфікації ASGI, це частина [Протоколу тривалості життя](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), і там визначені події `startup` і `shutdown`.
/// info | Інформація
Ви можете прочитати більше про обробники `lifespan` у [документації Starlette про Lifespan](https://www.starlette.dev/lifespan/).
Зокрема, як працювати зі станом тривалості життя, який можна використовувати в інших ділянках вашого коду.
///
## Підзастосунки { #sub-applications }
🚨 Майте на увазі, що ці події тривалості життя (startup і shutdown) виконуються лише для головного застосунку, а не для [Підзастосунки - монтування](sub-applications.md).
================================================
FILE: docs/uk/docs/advanced/generate-clients.md
================================================
# Генерація SDK { #generating-sdks }
Оскільки **FastAPI** базується на специфікації **OpenAPI**, його API можна описати у стандартному форматі, який розуміють багато інструментів.
Це спрощує створення актуальної **документації**, клієнтських бібліотек (**SDKs**) багатьма мовами, а також **тестування** чи **автоматизованих робочих процесів**, що залишаються синхронізованими з вашим кодом.
У цьому посібнику ви дізнаєтеся, як згенерувати **TypeScript SDK** для вашого бекенда на FastAPI.
## Генератори SDK з відкритим кодом { #open-source-sdk-generators }
Універсальним варіантом є [OpenAPI Generator](https://openapi-generator.tech/), який підтримує **багато мов програмування** та може генерувати SDK з вашої специфікації OpenAPI.
Для **клієнтів TypeScript** [Hey API](https://heyapi.dev/) — спеціалізоване рішення, що надає оптимізований досвід для екосистеми TypeScript.
Більше генераторів SDK ви можете знайти на [OpenAPI.Tools](https://openapi.tools/#sdk).
/// tip | Порада
FastAPI автоматично генерує специфікації **OpenAPI 3.1**, тож будь-який інструмент, який ви використовуєте, має підтримувати цю версію.
///
## Генератори SDK від спонсорів FastAPI { #sdk-generators-from-fastapi-sponsors }
У цьому розділі представлено рішення від компаній, що спонсорують FastAPI: вони мають **венчурну підтримку** та **корпоративну підтримку**. Ці продукти надають **додаткові можливості** та **інтеграції** поверх високоякісно згенерованих SDK.
Завдяки ✨ [**спонсорству FastAPI**](../help-fastapi.md#sponsor-the-author) ✨ ці компанії допомагають підтримувати фреймворк та його **екосистему** здоровими та **сталими**.
Їхня підтримка також демонструє сильну відданість **спільноті** FastAPI (вам), показуючи, що їм важливо не лише надавати **відмінний сервіс**, а й підтримувати **міцний і процвітаючий фреймворк**, FastAPI. 🙇
Наприклад, ви можете спробувати:
* [Speakeasy](https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship)
* [Stainless](https://www.stainless.com/?utm_source=fastapi&utm_medium=referral)
* [liblab](https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi)
Деякі з цих рішень також можуть бути з відкритим кодом або мати безкоштовні тарифи, тож ви можете спробувати їх без фінансових зобов'язань. Інші комерційні генератори SDK також доступні й їх можна знайти онлайн. 🤓
## Створити TypeScript SDK { #create-a-typescript-sdk }
Почнімо з простого застосунку FastAPI:
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
Зверніть увагу, що *операції шляху* визначають моделі, які вони використовують для корисного навантаження запиту та корисного навантаження відповіді, використовуючи моделі `Item` і `ResponseMessage`.
### Документація API { #api-docs }
Якщо ви перейдете до `/docs`, ви побачите **схеми** даних, які надсилаються в запитах і приймаються у відповідях:
Ви бачите ці схеми, оскільки їх було оголошено як моделі в застосунку.
Ця інформація доступна у **схемі OpenAPI** застосунку, а потім показується в документації API.
Та сама інформація з моделей, яку включено до OpenAPI, може бути використана для **генерації клієнтського коду**.
### Hey API { #hey-api }
Коли у нас є застосунок FastAPI з моделями, ми можемо використати Hey API для генерації клієнта TypeScript. Найшвидший спосіб зробити це — через npx.
```sh
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
```
Це згенерує TypeScript SDK у `./src/client`.
Ви можете дізнатися, як [встановити `@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/get-started), і почитати про [згенерований результат](https://heyapi.dev/openapi-ts/output) на їхньому сайті.
### Використання SDK { #using-the-sdk }
Тепер ви можете імпортувати та використовувати клієнтський код. Це може виглядати так; зверніть увагу, що ви отримуєте «автодоповнення» для методів:
Ви також отримаєте автодоповнення для корисного навантаження, яке надсилаєте:
/// tip | Порада
Зверніть увагу на автодоповнення для `name` і `price`, які були визначені в застосунку FastAPI, у моделі `Item`.
///
Ви бачитимете вбудовані помилки для даних, які надсилаєте:
Об'єкт відповіді також матиме автодоповнення:
## Застосунок FastAPI з мітками { #fastapi-app-with-tags }
У багатьох випадках ваш застосунок FastAPI буде більшим, і ви, ймовірно, використовуватимете мітки, щоб розділяти різні групи *операцій шляху*.
Наприклад, у вас може бути секція для **items** і окрема секція для **users**, і їх можна розділити мітками:
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
### Згенерувати TypeScript-клієнт із мітками { #generate-a-typescript-client-with-tags }
Якщо ви згенеруєте клієнт для застосунку FastAPI, що використовує мітки, зазвичай клієнтський код також буде розділено за цими мітками.
Таким чином, ви матимете правильно впорядковані та згруповані частини клієнтського коду:
У цьому випадку у вас є:
* `ItemsService`
* `UsersService`
### Назви методів клієнта { #client-method-names }
Зараз згенеровані назви методів на кшталт `createItemItemsPost` виглядають не дуже охайно:
```TypeScript
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
```
...це тому, що генератор клієнта використовує внутрішній OpenAPI **operation ID** для кожної *операції шляху*.
OpenAPI вимагає, щоб кожен operation ID був унікальним для всіх *операцій шляху*, тому FastAPI використовує **назву функції**, **шлях** і **HTTP-метод/операцію** для генерації цього operation ID, адже так воно може гарантувати унікальність operation ID.
Але далі я покажу, як це покращити. 🤓
## Користувацькі operation ID та кращі назви методів { #custom-operation-ids-and-better-method-names }
Ви можете **змінити** спосіб **генерації** цих operation ID, щоб зробити їх простішими та мати **простіші назви методів** у клієнтах.
У цьому випадку вам потрібно буде іншим способом гарантувати, що кожен operation ID є **унікальним**.
Наприклад, ви можете переконатися, що кожна *операція шляху* має мітку, а потім генерувати operation ID на основі **мітки** та **назви** *операції шляху* (назви функції).
### Користувацька функція генерування унікального ID { #custom-generate-unique-id-function }
FastAPI використовує **унікальний ID** для кожної *операції шляху*, який застосовується для **operation ID**, а також для назв будь-яких потрібних користувацьких моделей для запитів чи відповідей.
Ви можете налаштувати цю функцію. Вона приймає `APIRoute` і повертає строку.
Наприклад, тут використовується перша мітка (у вас, ймовірно, буде лише одна мітка) і назва *операції шляху* (назва функції).
Потім ви можете передати цю користувацьку функцію до **FastAPI** як параметр `generate_unique_id_function`:
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
### Згенерувати TypeScript-клієнт з користувацькими operation ID { #generate-a-typescript-client-with-custom-operation-ids }
Тепер, якщо ви згенеруєте клієнт знову, ви побачите покращені назви методів:
Як бачите, тепер у назвах методів є мітка, а потім назва функції; вони більше не містять інформації з URL-шляху та HTTP-операції.
### Попередня обробка специфікації OpenAPI для генератора клієнта { #preprocess-the-openapi-specification-for-the-client-generator }
У згенерованому коді все ще є певна **дубльована інформація**.
Ми вже знаємо, що цей метод стосується **items**, адже це слово є в `ItemsService` (взято з мітки), але все ще маємо назву мітки як префікс у назві методу. 😕
Ми, ймовірно, все одно захочемо зберегти це загалом для OpenAPI, адже так гарантується унікальність operation ID.
Але для згенерованого клієнта ми можемо **змінити** operation ID в OpenAPI безпосередньо перед генерацією клієнтів, просто щоб зробити назви методів приємнішими та **чистішими**.
Ми можемо завантажити JSON OpenAPI у файл `openapi.json`, а потім **прибрати цей префікс із міткою** за допомогою такого скрипту:
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
//// tab | Node.js
```Javascript
{!> ../../docs_src/generate_clients/tutorial004.js!}
```
////
Після цього operation ID буде перейменовано з чогось на кшталт `items-get_items` просто на `get_items`, тож генератор клієнта зможе створити простіші назви методів.
### Згенерувати TypeScript-клієнт із попередньо обробленим OpenAPI { #generate-a-typescript-client-with-the-preprocessed-openapi }
Оскільки кінцевий результат тепер у файлі `openapi.json`, вам потрібно оновити шлях до вхідних даних:
```sh
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
```
Після генерації нового клієнта ви отримаєте **чисті назви методів**, із усім **автодоповненням**, **вбудованими помилками** тощо:
## Переваги { #benefits }
Використовуючи автоматично згенеровані клієнти, ви отримаєте **автодоповнення** для:
* Методів.
* Корисного навантаження запиту в тілі, параметрах запиту тощо.
* Корисного навантаження відповіді.
Також ви матимете **вбудовані помилки** для всього.
І щоразу, коли ви оновлюєте код бекенда та **перегенеровуєте** фронтенд, у ньому з'являтимуться нові *операції шляху* як методи, старі буде видалено, а будь-які інші зміни відобразяться у згенерованому коді. 🤓
Це також означає, що якщо щось змінилося, це буде **відображено** в клієнтському коді автоматично. А якщо ви **зіберете** клієнт, буде повідомлено про помилку, якщо є будь-яка **невідповідність** у використаних даних.
Таким чином, ви **виявлятимете багато помилок** дуже рано в циклі розробки, замість того, щоб чекати, поки помилки проявляться у ваших кінцевих користувачів у продакшені, і лише потім намагатися з'ясувати, у чому проблема. ✨
================================================
FILE: docs/uk/docs/advanced/index.md
================================================
# Просунутий посібник користувача { #advanced-user-guide }
## Додаткові можливості { #additional-features }
Основний [Навчальний посібник - Посібник користувача](../tutorial/index.md) має бути достатнім, щоб провести вас через усі основні можливості **FastAPI**.
У наступних розділах ви побачите інші опції, конфігурації та додаткові можливості.
/// tip
Наступні розділи **не обов'язково «просунуті»**.
І можливо, що рішення для вашого випадку використання може бути в одному з них.
///
## Спершу прочитайте навчальний посібник { #read-the-tutorial-first }
Ви все ще можете використовувати більшість можливостей **FastAPI**, маючи знання з основного [Навчального посібника - Посібника користувача](../tutorial/index.md).
А в наступних розділах передбачається, що ви вже його прочитали і знайомі з основними ідеями.
================================================
FILE: docs/uk/docs/advanced/json-base64-bytes.md
================================================
# JSON з байтами як Base64 { #json-with-bytes-as-base64 }
Якщо ваш застосунок має отримувати і надсилати дані JSON, але потрібно включати туди двійкові дані, ви можете кодувати їх як base64.
## Base64 проти файлів { #base64-vs-files }
Насамперед розгляньте, чи можете ви використати [Файли запиту](../tutorial/request-files.md) для завантаження двійкових даних і [Користувацька відповідь - FileResponse](./custom-response.md#fileresponse--fileresponse-) для надсилання двійкових даних замість кодування їх у JSON.
JSON може містити лише строки, закодовані в UTF-8, тому він не може містити «сирі» байти.
Base64 може кодувати двійкові дані у строках, але для цього потрібно більше символів, ніж у початкових двійкових даних, тож зазвичай це менш ефективно, ніж звичайні файли.
Використовуйте base64 лише якщо справді потрібно включати двійкові дані в JSON і ви не можете використати файли для цього.
## Pydantic `bytes` { #pydantic-bytes }
Ви можете оголосити модель Pydantic з полями `bytes`, а потім використати `val_json_bytes` у конфігурації моделі, щоб вказати їй використовувати base64 для перевірки вхідних даних JSON; як частина цієї перевірки, вона декодує строку base64 у байти.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
Якщо ви перевірите `/docs`, там буде показано, що поле `data` очікує байти, закодовані в base64:
Ви можете надіслати запит так:
```json
{
"description": "Some data",
"data": "aGVsbG8="
}
```
/// tip | Порада
`aGVsbG8=` - це кодування base64 для `hello`.
///
Після цього Pydantic декодує строку base64 і надасть вам початкові байти в полі моделі `data`.
Ви отримаєте відповідь приблизно таку:
```json
{
"description": "Some data",
"content": "hello"
}
```
## Pydantic `bytes` для вихідних даних { #pydantic-bytes-for-output-data }
Ви також можете використовувати поля `bytes` з `ser_json_bytes` у конфігурації моделі для вихідних даних, і Pydantic серіалізує байти як base64 під час формування відповіді JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
## Pydantic `bytes` для вхідних і вихідних даних { #pydantic-bytes-for-input-and-output-data }
І, звісно, ви можете використовувати ту саму модель, налаштовану на base64, щоб обробляти і вхідні дані (перевіряти) з `val_json_bytes`, і вихідні дані (серіалізувати) з `ser_json_bytes` під час отримання та надсилання даних JSON.
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
================================================
FILE: docs/uk/docs/advanced/middleware.md
================================================
# Просунуте проміжне програмне забезпечення { #advanced-middleware }
У головному навчальному посібнику ви читали, як додати [Користувацьке проміжне ПЗ](../tutorial/middleware.md) до вашого застосунку.
Також ви читали, як обробляти [CORS за допомогою `CORSMiddleware`](../tutorial/cors.md).
У цьому розділі розглянемо, як використовувати інше проміжне ПЗ.
## Додавання middleware ASGI { #adding-asgi-middlewares }
Оскільки **FastAPI** базується на Starlette і реалізує специфікацію ASGI, ви можете використовувати будь-яке проміжне ПЗ ASGI.
Middleware не обов'язково має бути створене саме для FastAPI або Starlette, головне - щоб воно відповідало специфікації ASGI.
Загалом, middleware ASGI — це класи, які очікують отримати застосунок ASGI як перший аргумент.
Тож у документації до сторонніх middleware ASGI вам, імовірно, порадять зробити приблизно так:
```Python
from unicorn import UnicornMiddleware
app = SomeASGIApp()
new_app = UnicornMiddleware(app, some_config="rainbow")
```
Але FastAPI (точніше Starlette) надає простіший спосіб, який гарантує, що внутрішнє middleware обробляє помилки сервера, а користувацькі обробники винятків працюють коректно.
Для цього використовуйте `app.add_middleware()` (як у прикладі для CORS).
```Python
from fastapi import FastAPI
from unicorn import UnicornMiddleware
app = FastAPI()
app.add_middleware(UnicornMiddleware, some_config="rainbow")
```
`app.add_middleware()` приймає клас middleware як перший аргумент і будь-які додаткові аргументи, що будуть передані цьому middleware.
## Вбудоване middleware { #integrated-middlewares }
**FastAPI** містить кілька middleware для поширених випадків використання, далі розглянемо, як їх використовувати.
/// note | Технічні деталі
У наступних прикладах ви також можете використовувати `from starlette.middleware.something import SomethingMiddleware`.
**FastAPI** надає кілька middleware у `fastapi.middleware` виключно для зручності розробника. Але більшість доступних middleware походять безпосередньо зі Starlette.
///
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
Примушує, щоб усі вхідні запити були або `https`, або `wss`.
Будь-який вхідний запит до `http` або `ws` буде перенаправлено на захищену схему.
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Примушує, щоб усі вхідні запити мали коректно встановлений заголовок `Host`, щоб захиститися від атак HTTP Host Header.
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
Підтримуються такі аргументи:
- `allowed_hosts` - Список доменних імен, які слід дозволити як імена хостів. Підтримуються домени з «дикою картою», такі як `*.example.com`, для зіставлення піддоменів. Щоб дозволити будь-яке ім'я хоста, або використовуйте `allowed_hosts=["*"]`, або не додавайте це middleware.
- `www_redirect` - Якщо встановлено True, запити до не-www версій дозволених хостів буде перенаправлено до їхніх www-варіантів. Типово `True`.
Якщо вхідний запит не проходить перевірку, буде надіслано відповідь `400`.
## `GZipMiddleware` { #gzipmiddleware }
Обробляє відповіді GZip для будь-якого запиту, що містить `"gzip"` у заголовку `Accept-Encoding`.
Middleware обробляє як стандартні, так і потокові відповіді.
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
Підтримуються такі аргументи:
- `minimum_size` - Не GZip-увати відповіді, менші за цей мінімальний розмір у байтах. Типово `500`.
- `compresslevel` - Використовується під час стиснення GZip. Це ціле число в діапазоні від 1 до 9. Типово `9`. Менше значення дає швидше стиснення, але більший розмір файлів; більше значення дає повільніше стиснення, але менший розмір файлів.
## Інше middleware { #other-middlewares }
Є багато іншого проміжного ПЗ ASGI.
Наприклад:
- [`ProxyHeadersMiddleware` з Uvicorn](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py)
- [MessagePack](https://github.com/florimondmanca/msgpack-asgi)
Щоб переглянути інші доступні middleware, ознайомтеся з [документацією Starlette щодо middleware](https://www.starlette.dev/middleware/) та [списком ASGI Awesome](https://github.com/florimondmanca/awesome-asgi).
================================================
FILE: docs/uk/docs/advanced/openapi-callbacks.md
================================================
# Зворотні виклики OpenAPI { #openapi-callbacks }
Ви можете створити API з операцією шляху, яка ініціюватиме запит до зовнішнього API, створеного кимось іншим (ймовірно тим самим розробником, який буде використовувати ваш API).
Процес, що відбувається, коли ваш застосунок API викликає зовнішній API, називається «зворотний виклик». Тому що програмне забезпечення, написане зовнішнім розробником, надсилає запит до вашого API, а потім ваш API виконує зворотний виклик, надсилаючи запит до зовнішнього API (його, ймовірно, також створив той самий розробник).
У такому випадку вам може знадобитися задокументувати, яким має бути той зовнішній API: які операції шляху він має мати, яке тіло очікувати, яку відповідь повертати тощо.
## Застосунок зі зворотними викликами { #an-app-with-callbacks }
Розгляньмо це на прикладі.
Уявімо, що ви розробляєте застосунок для створення рахунків.
Ці рахунки матимуть `id`, `title` (необов'язково), `customer` і `total`.
Користувач вашого API (зовнішній розробник) створить рахунок у вашому API за допомогою POST-запиту.
Потім ваш API буде (уявімо):
- Надсилати рахунок деякому клієнту зовнішнього розробника.
- Отримувати оплату.
- Надсилати сповіщення назад користувачу API (зовнішньому розробнику).
- Це буде зроблено шляхом надсилання POST-запиту (з вашого API) до деякого зовнішнього API, наданого тим зовнішнім розробником (це і є «зворотний виклик»).
## Звичайний застосунок FastAPI { #the-normal-fastapi-app }
Спочатку подивімося, як виглядав би звичайний застосунок API до додавання зворотного виклику.
Він матиме операцію шляху, яка отримуватиме тіло `Invoice`, і параметр запиту `callback_url`, що міститиме URL для зворотного виклику.
Ця частина цілком звична, більшість коду вам, ймовірно, уже знайома:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
/// tip | Порада
Параметр запиту `callback_url` використовує тип Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/).
///
Єдина нова річ - це `callbacks=invoices_callback_router.routes` як аргумент декоратора операції шляху. Далі розглянемо, що це таке.
## Документування зворотного виклику { #documenting-the-callback }
Фактичний код зворотного виклику сильно залежатиме від вашого застосунку API.
І, ймовірно, сильно відрізнятиметься від застосунку до застосунку.
Це можуть бути лише один-два рядки коду, наприклад:
```Python
callback_url = "https://example.com/api/v1/invoices/events/"
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
```
Але, можливо, найважливіша частина зворотного виклику - переконатися, що користувач вашого API (зовнішній розробник) правильно реалізує зовнішній API відповідно до даних, які ваш API надсилатиме в тілі запиту зворотного виклику тощо.
Тому далі ми додамо код, щоб задокументувати, яким має бути цей зовнішній API, щоб приймати зворотний виклик від вашого API.
Ця документація з'явиться в Swagger UI за адресою `/docs` у вашому API і дасть змогу зовнішнім розробникам зрозуміти, як створити зовнішній API.
У цьому прикладі сам зворотний виклик не реалізовано (це може бути лише один рядок коду), лише частину з документацією.
/// tip | Порада
Фактичний зворотний виклик - це просто HTTP-запит.
Реалізуючи зворотний виклик самостійно, ви можете скористатися, наприклад, [HTTPX](https://www.python-httpx.org) або [Requests](https://requests.readthedocs.io/).
///
## Напишіть код документації для зворотного виклику { #write-the-callback-documentation-code }
Цей код не виконуватиметься у вашому застосунку, він потрібен лише, щоб задокументувати, яким має бути зовнішній API.
Але ви вже знаєте, як легко створювати автоматичну документацію для API за допомогою FastAPI.
Тож ми скористаємося цими знаннями, щоб задокументувати, яким має бути зовнішній API... створивши операції шляху, які має реалізувати зовнішній API (ті, які викликатиме ваш API).
/// tip | Порада
Пишучи код для документування зворотного виклику, корисно уявити, що ви - той *зовнішній розробник*. І що ви зараз реалізуєте *зовнішній API*, а не *ваш API*.
Тимчасово прийнявши цю точку зору ( *зовнішнього розробника* ), вам буде очевидніше, куди помістити параметри, яку Pydantic-модель використати для тіла, для відповіді тощо для того *зовнішнього API*.
///
### Створіть callback `APIRouter` { #create-a-callback-apirouter }
Спочатку створіть новий `APIRouter`, який міститиме один або кілька зворотних викликів.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
### Створіть операцію шляху зворотного виклику { #create-the-callback-path-operation }
Щоб створити операцію шляху зворотного виклику, використайте той самий `APIRouter`, який ви створили вище.
Вона має виглядати як звичайна операція шляху FastAPI:
- Ймовірно має містити оголошення тіла, яке вона приймає, напр. `body: InvoiceEvent`.
- І також може містити оголошення відповіді, яку вона повертає, напр. `response_model=InvoiceEventReceived`.
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
Є 2 основні відмінності від звичайної операції шляху:
- Їй не потрібен реальний код, адже ваш застосунок ніколи не викликатиме цей код. Вона використовується лише для документування зовнішнього API. Тому функція може просто містити `pass`.
- Шлях може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (див. нижче), де можна використовувати змінні з параметрами та частини оригінального запиту, надісланого до вашого API.
### Вираз шляху зворотного виклику { #the-callback-path-expression }
Шлях зворотного виклику може містити [вираз OpenAPI 3](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression), який включає частини оригінального запиту, надісланого до вашого API.
У цьому випадку це строка:
```Python
"{$callback_url}/invoices/{$request.body.id}"
```
Отже, якщо користувач вашого API (зовнішній розробник) надішле запит до *вашого API* на:
```
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
```
з JSON-тілом:
```JSON
{
"id": "2expen51ve",
"customer": "Mr. Richie Rich",
"total": "9999"
}
```
тоді *ваш API* опрацює рахунок і згодом надішле запит зворотного виклику на `callback_url` ( *зовнішній API* ):
```
https://www.external.org/events/invoices/2expen51ve
```
з JSON-тілом на кшталт:
```JSON
{
"description": "Payment celebration",
"paid": true
}
```
і очікуватиме відповідь від того *зовнішнього API* з JSON-тілом на кшталт:
```JSON
{
"ok": true
}
```
/// tip | Порада
Зверніть увагу, що використаний URL зворотного виклику містить URL, отриманий як параметр запиту в `callback_url` (`https://www.external.org/events`), а також `id` рахунку зсередини JSON-тіла (`2expen51ve`).
///
### Додайте маршрутизатор зворотного виклику { #add-the-callback-router }
На цьому етапі ви маєте потрібні операції шляху зворотного виклику (ті, які має реалізувати *зовнішній розробник* у *зовнішньому API*) у створеному вище маршрутизаторі зворотного виклику.
Тепер використайте параметр `callbacks` у декораторі операції шляху вашого API, щоб передати атрибут `.routes` (це насправді просто `list` маршрутів/операцій шляху) з цього маршрутизатора зворотного виклику:
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
/// tip | Порада
Зверніть увагу, що ви передаєте не сам маршрутизатор (`invoices_callback_router`) у `callback=`, а атрибут `.routes`, тобто `invoices_callback_router.routes`.
///
### Перевірте документацію { #check-the-docs }
Тепер ви можете запустити застосунок і перейти за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите вашу документацію з розділом «Callbacks» для вашої операції шляху, який показує, як має виглядати зовнішній API:
================================================
FILE: docs/uk/docs/advanced/openapi-webhooks.md
================================================
# Вебхуки OpenAPI { #openapi-webhooks }
Бувають випадки, коли ви хочете повідомити **користувачів** вашого API, що ваш застосунок може викликати *їхній* застосунок (надсилаючи запит) із деякими даними, зазвичай щоб **сповістити** про певний тип **події**.
Це означає, що замість звичного процесу, коли ваші користувачі надсилають запити до вашого API, саме **ваш API** (або ваш застосунок) може **надсилати запити до їхньої системи** (до їх API, їх застосунку).
Зазвичай це називають **вебхуком**.
## Кроки вебхуків { #webhooks-steps }
Зазвичай процес такий: ви визначаєте у своєму коді, яке повідомлення надсилатимете, тобто **тіло запиту**.
Ви також якимось чином визначаєте моменти, коли ваш застосунок надсилатиме ці запити або події.
А **ваші користувачі** якимось чином (наприклад, у веб-дашборді) визначають **URL**, куди ваш застосунок має надсилати ці запити.
Уся **логіка** щодо реєстрації URL для вебхуків і код для фактичного надсилання цих запитів - на ваш розсуд. Ви пишете це у **власному коді** так, як вважаєте за потрібне.
## Документування вебхуків у **FastAPI** та OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
У **FastAPI**, використовуючи OpenAPI, ви можете визначити назви цих вебхуків, типи HTTP-операцій, які ваш застосунок може надсилати (наприклад, `POST`, `PUT` тощо), і **тіла** запитів, які ваш застосунок надсилатиме.
Це значно спростить для ваших користувачів **реалізацію їхніх API** для отримання ваших запитів **вебхуків**; вони навіть зможуть згенерувати частину власного коду API автоматично.
/// info | Інформація
Вебхуки доступні в OpenAPI 3.1.0 і вище, підтримуються FastAPI `0.99.0` і вище.
///
## Застосунок із вебхуками { #an-app-with-webhooks }
Коли ви створюєте застосунок **FastAPI**, є атрибут `webhooks`, який можна використати для визначення *вебхуків* так само, як ви визначаєте *операції шляху*, наприклад за допомогою `@app.webhooks.post()`.
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *}
Визначені вами вебхуки потраплять до **схеми OpenAPI** та автоматичного **інтерфейсу документації**.
/// info | Інформація
Об'єкт `app.webhooks` насправді є просто `APIRouter` - тим самим типом, який ви використовуєте, структуризуючи застосунок у кількох файлах.
///
Зверніть увагу, що з вебхуками ви фактично не оголошуєте *шлях* (на кшталт `/items/`), текст, який ви передаєте там, - це лише **ідентифікатор** вебхука (назва події). Наприклад, у `@app.webhooks.post("new-subscription")` назва вебхука - `new-subscription`.
Це тому, що очікується, що **ваші користувачі** іншим способом (наприклад, у веб-дашборді) визначать фактичний **URL-шлях**, де вони хочуть отримувати запит вебхука.
### Перевірте документацію { #check-the-docs }
Тепер ви можете запустити свій застосунок і перейти за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите у своїй документації звичайні *операції шляху*, а також деякі **вебхуки**:
================================================
FILE: docs/uk/docs/advanced/path-operation-advanced-configuration.md
================================================
# Додаткова конфігурація операцій шляху { #path-operation-advanced-configuration }
## OpenAPI operationId { #openapi-operationid }
/// warning | Попередження
Якщо ви не «експерт» з OpenAPI, імовірно, вам це не потрібно.
///
Ви можете встановити OpenAPI `operationId`, який буде використано у вашій *операції шляху*, за допомогою параметра `operation_id`.
Потрібно переконатися, що він унікальний для кожної операції.
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
### Використання назви *функції операції шляху* як operationId { #using-the-path-operation-function-name-as-the-operationid }
Якщо ви хочете використовувати назви функцій ваших API як `operationId`, ви можете пройтися по всіх них і переписати `operation_id` кожної *операції шляху*, використовуючи їхній `APIRoute.name`.
Зробіть це після додавання всіх *операцій шляху*.
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
/// tip | Порада
Якщо ви вручну викликаєте `app.openapi()`, оновіть усі `operationId` до цього.
///
/// warning | Попередження
Якщо ви робите це, переконайтеся, що кожна з ваших *функцій операцій шляху* має унікальну назву.
Навіть якщо вони в різних модулях (файлах Python).
///
## Виключення з OpenAPI { #exclude-from-openapi }
Щоб виключити *операцію шляху* зі згенерованої Схеми OpenAPI (а отже, і з автоматичних систем документації), використайте параметр `include_in_schema` і встановіть його в `False`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
## Розширений опис із docstring { #advanced-description-from-docstring }
Ви можете обмежити кількість рядків із docstring *функції операції шляху*, що використовуються для OpenAPI.
Додавання `\f` (екранованого символу «form feed») змусить **FastAPI** обрізати вивід для OpenAPI в цій точці.
Це не з’явиться в документації, але інші інструменти (такі як Sphinx) зможуть використати решту.
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
## Додаткові відповіді { #additional-responses }
Ймовірно, ви вже бачили, як оголошувати `response_model` і `status_code` для *операції шляху*.
Це визначає метадані про основну відповідь *операції шляху*.
Також можна оголосити додаткові відповіді з їхніми моделями, кодами статусу тощо.
У документації є цілий розділ про це, ви можете прочитати його тут: [Додаткові відповіді в OpenAPI](additional-responses.md).
## Додатково в OpenAPI { #openapi-extra }
Коли ви оголошуєте *операцію шляху* у своєму застосунку, **FastAPI** автоматично генерує відповідні метадані про цю *операцію шляху* для включення в Схему OpenAPI.
/// note | Технічні деталі
У специфікації OpenAPI це називається [Об'єкт Operation](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object).
///
Він містить усю інформацію про *операцію шляху* і використовується для побудови автоматичної документації.
Він включає `tags`, `parameters`, `requestBody`, `responses` тощо.
Цю OpenAPI-схему, специфічну для *операції шляху*, зазвичай генерує **FastAPI** автоматично, але ви також можете її розширити.
/// tip | Порада
Це низькорівнева точка розширення.
Якщо вам потрібно лише оголосити додаткові відповіді, зручніше зробити це через [Додаткові відповіді в OpenAPI](additional-responses.md).
///
Ви можете розширити OpenAPI-схему для *операції шляху*, використовуючи параметр `openapi_extra`.
### Розширення OpenAPI { #openapi-extensions }
`openapi_extra` може бути корисним, наприклад, для оголошення [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
Якщо ви відкриєте автоматичну документацію API, ваше розширення з’явиться внизу конкретної *операції шляху*.
І якщо ви відкриєте згенерований OpenAPI (за адресою `/openapi.json` у вашому API), ви також побачите своє розширення як частину конкретної *операції шляху*:
```JSON hl_lines="22"
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
```
### Власна схема OpenAPI для *операції шляху* { #custom-openapi-path-operation-schema }
Словник у `openapi_extra` буде глибоко об’єднано з автоматично згенерованою OpenAPI-схемою для *операції шляху*.
Тож ви можете додати додаткові дані до автоматично згенерованої схеми.
Наприклад, ви можете вирішити читати та перевіряти запит власним кодом, не використовуючи автоматичні можливості FastAPI з Pydantic, але все ж захотіти визначити запит у Схемі OpenAPI.
Ви можете зробити це за допомогою `openapi_extra`:
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
У цьому прикладі ми не оголошували жодної моделі Pydantic. Насправді тіло запиту навіть не розібрано як JSON, воно читається безпосередньо як `bytes`, а функція `magic_data_reader()` відповідатиме за його розбір певним чином.
Водночас ми можемо оголосити очікувану схему для тіла запиту.
### Власний тип вмісту OpenAPI { #custom-openapi-content-type }
Використовуючи той самий прийом, ви можете застосувати модель Pydantic, щоб визначити Схему JSON, яка потім включається в користувацький розділ OpenAPI-схеми для *операції шляху*.
І ви можете зробити це, навіть якщо тип даних у запиті - не JSON.
Наприклад, у цьому застосунку ми не використовуємо вбудовану функціональність FastAPI для отримання Схеми JSON з моделей Pydantic і не використовуємо автоматичну валідацію для JSON. Насправді ми оголошуємо тип вмісту запиту як YAML, а не JSON:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
Попри те, що ми не використовуємо типову вбудовану функціональність, ми все одно використовуємо модель Pydantic, щоб вручну згенерувати Схему JSON для даних, які хочемо отримати у форматі YAML.
Потім ми працюємо із запитом безпосередньо і отримуємо тіло як `bytes`. Це означає, що FastAPI навіть не намагатиметься розібрати корисне навантаження запиту як JSON.
Далі у нашому коді ми безпосередньо розбираємо цей YAML-вміст і знову використовуємо ту саму модель Pydantic, щоб перевірити YAML-вміст:
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
/// tip | Порада
Тут ми перевикористовуємо ту саму модель Pydantic.
Але так само ми могли б перевіряти дані іншим способом.
///
================================================
FILE: docs/uk/docs/advanced/response-change-status-code.md
================================================
# Відповідь - зміна коду статусу { #response-change-status-code }
Ймовірно, ви вже читали, що можна встановити типовий [код статусу відповіді](../tutorial/response-status-code.md).
Але інколи потрібно повернути інший код статусу, ніж типовий.
## Випадок використання { #use-case }
Наприклад, уявімо, що ви хочете за замовчуванням повертати код статусу HTTP «OK» `200`.
Але якщо даних не існувало, ви хочете створити їх і повернути код статусу HTTP «CREATED» `201`.
Водночас ви все одно хочете мати змогу фільтрувати та перетворювати повернені дані за допомогою `response_model`.
Для таких випадків ви можете використати параметр `Response`.
## Використовуйте параметр `Response` { #use-a-response-parameter }
Ви можете оголосити параметр типу `Response` у своїй функції операції шляху (так само, як для кукі та заголовків).
Потім ви можете встановити `status_code` у цьому *тимчасовому* об'єкті відповіді.
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
Після цього ви можете повернути будь-який потрібний об'єкт, як зазвичай (`dict`, модель бази даних тощо).
І якщо ви оголосили `response_model`, він усе одно буде використаний для фільтрації та перетворення поверненого об'єкта.
**FastAPI** використає цей *тимчасовий* об'єкт відповіді, щоб отримати код статусу (а також кукі та заголовки), і помістить їх у фінальну відповідь, що містить повернуте вами значення, відфільтроване за допомогою `response_model`.
Ви також можете оголосити параметр `Response` у залежностях і встановлювати там код статусу. Але майте на увазі: останнє встановлене значення матиме пріоритет.
================================================
FILE: docs/uk/docs/advanced/response-cookies.md
================================================
# Кукі у відповіді { #response-cookies }
## Використовуйте параметр `Response` { #use-a-response-parameter }
Ви можете оголосити параметр типу `Response` у вашій *функції операції шляху*.
Потім ви можете встановити кукі в цьому *тимчасовому* об'єкті відповіді.
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
Після цього ви можете повернути будь-який потрібний об'єкт, як зазвичай (наприклад, `dict`, модель бази даних тощо).
І якщо ви оголосили `response_model`, він усе одно буде використаний для фільтрації та перетворення об'єкта, який ви повернули.
**FastAPI** використає цю *тимчасову* відповідь, щоб витягнути кукі (а також заголовки та код статусу) і помістить їх у фінальну відповідь, що містить значення, яке ви повернули, відфільтроване будь-якою `response_model`.
Ви також можете оголосити параметр `Response` у залежностях і встановлювати в них кукі (і заголовки).
## Повертайте `Response` безпосередньо { #return-a-response-directly }
Ви також можете створювати кукі, повертаючи `Response` безпосередньо у вашому коді.
Для цього ви можете створити відповідь, як описано в [Повернути відповідь безпосередньо](response-directly.md).
Потім встановіть у ньому кукі і поверніть його:
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
/// tip
Майте на увазі, що якщо ви повертаєте відповідь безпосередньо замість використання параметра `Response`, FastAPI поверне її напряму.
Тому вам потрібно переконатися, що ваші дані мають коректний тип. Наприклад, сумісні з JSON, якщо ви повертаєте `JSONResponse`.
А також що ви не надсилаєте дані, які мали б бути відфільтровані за допомогою `response_model`.
///
### Більше інформації { #more-info }
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import Response` або `from starlette.responses import JSONResponse`.
**FastAPI** надає ті самі `starlette.responses` як `fastapi.responses` лише для зручності для вас, розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette.
І оскільки `Response` часто використовується для встановлення заголовків і кукі, **FastAPI** також надає його як `fastapi.Response`.
///
Щоб побачити всі доступні параметри та опції, перегляньте [документацію в Starlette](https://www.starlette.dev/responses/#set-cookie).
================================================
FILE: docs/uk/docs/advanced/response-directly.md
================================================
# Повернення Response безпосередньо { #return-a-response-directly }
Коли ви створюєте операцію шляху **FastAPI**, зазвичай ви можете повертати з неї будь-які дані: `dict`, `list`, модель Pydantic, модель бази даних тощо.
Якщо ви оголосите [Модель відповіді](../tutorial/response-model.md), **FastAPI** використає її, щоб серіалізувати дані у JSON за допомогою Pydantic.
Якщо ви не оголошуєте модель відповіді, **FastAPI** використає `jsonable_encoder`, описаний у [Сумісному з JSON кодері](../tutorial/encoder.md), і помістить результат у `JSONResponse`.
Ви також можете створити `JSONResponse` безпосередньо і повернути його.
/// tip | Порада
Зазвичай ви отримаєте значно кращу продуктивність, використовуючи [Модель відповіді](../tutorial/response-model.md), ніж повертаючи `JSONResponse` безпосередньо, адже так дані серіалізуються Pydantic на Rust.
///
## Повернення `Response` { #return-a-response }
Ви можете повертати `Response` або будь-який його підклас.
/// info | Інформація
`JSONResponse` сам є підкласом `Response`.
///
І коли ви повертаєте `Response`, **FastAPI** передасть його безпосередньо.
Він не виконуватиме жодних перетворень даних за допомогою моделей Pydantic, не перетворюватиме вміст на будь-який тип тощо.
Це дає вам багато гнучкості. Ви можете повертати будь-які типи даних, переписувати будь-які оголошення або перевірки даних тощо.
Це також покладає на вас багато відповідальності. Ви маєте переконатися, що дані, які ви повертаєте, коректні, у правильному форматі, можуть бути серіалізовані тощо.
## Використання `jsonable_encoder` у `Response` { #using-the-jsonable-encoder-in-a-response }
Оскільки **FastAPI** не вносить змін у `Response`, який ви повертаєте, вам потрібно впевнитися, що його вміст готовий.
Наприклад, ви не можете помістити модель Pydantic у `JSONResponse`, не перетворивши її спочатку на `dict` з усіма типами даних (як-от `datetime`, `UUID` тощо), перетвореними на типи, сумісні з JSON.
Для таких випадків ви можете використати `jsonable_encoder`, щоб перетворити ваші дані перед тим, як передати їх у відповідь:
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import JSONResponse`.
**FastAPI** надає ті самі `starlette.responses` як `fastapi.responses` просто як зручність для вас, розробника. Але більшість доступних відповідей походять безпосередньо зі Starlette.
///
## Повернення власного `Response` { #returning-a-custom-response }
Наведений вище приклад показує всі необхідні частини, але він ще не дуже корисний, адже ви могли просто повернути `item` безпосередньо, і **FastAPI** помістив би його у `JSONResponse`, перетворивши на `dict` тощо. Усе це відбувається за замовчуванням.
Тепер подивімося, як це використати, щоб повернути власну відповідь.
Припустімо, ви хочете повернути відповідь [XML](https://en.wikipedia.org/wiki/XML).
Ви можете помістити свій вміст XML у строку, помістити це в `Response` і повернути:
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
## Як працює модель відповіді { #how-a-response-model-works }
Коли ви оголошуєте [Модель відповіді - Тип, що повертається](../tutorial/response-model.md) в операції шляху, **FastAPI** використає її, щоб серіалізувати дані у JSON за допомогою Pydantic.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
Оскільки це відбувається на боці Rust, продуктивність буде значно кращою, ніж якби це робилося звичайним Python і класом `JSONResponse`.
Коли використовується `response_model` або тип, що повертається, **FastAPI** не використовуватиме `jsonable_encoder` для перетворення даних (що було б повільніше) і не використовуватиме клас `JSONResponse`.
Натомість воно бере байти JSON, згенеровані Pydantic за допомогою моделі відповіді (або типу, що повертається), і повертає `Response` з відповідним медіа-типом для JSON (`application/json`) безпосередньо.
## Примітки { #notes }
Коли ви повертаєте `Response` безпосередньо, його дані не перевіряються, не перетворюються (серіалізуються) і не документуються автоматично.
Але ви все ще можете задокументувати це, як описано в [Додаткових відповідях в OpenAPI](additional-responses.md).
У подальших розділах ви побачите, як використовувати/оголошувати ці власні `Response`, водночас зберігаючи автоматичне перетворення даних, документацію тощо.
================================================
FILE: docs/uk/docs/advanced/response-headers.md
================================================
# Заголовки відповіді { #response-headers }
## Використовуйте параметр `Response` { #use-a-response-parameter }
Ви можете оголосити параметр типу `Response` у вашій функції операції шляху (так само, як і для кукі).
Потім ви можете встановлювати заголовки в цьому *тимчасовому* обʼєкті відповіді.
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
Далі ви можете повернути будь-який потрібний обʼєкт, як зазвичай (наприклад, `dict`, модель бази даних тощо).
Якщо ви оголосили `response_model`, його все одно буде використано для фільтрації та перетворення поверненого обʼєкта.
**FastAPI** використає цей *тимчасовий* обʼєкт відповіді, щоб витягти заголовки (а також кукі та код статусу) і помістить їх у кінцеву відповідь, яка міститиме повернуте вами значення, відфільтроване будь-яким `response_model`.
Також ви можете оголосити параметр `Response` у залежностях і встановлювати в них заголовки (та кукі).
## Поверніть `Response` безпосередньо { #return-a-response-directly }
Ви також можете додавати заголовки, коли повертаєте `Response` безпосередньо.
Створіть відповідь, як описано в [Повернення Response безпосередньо](response-directly.md), і передайте заголовки як додатковий параметр:
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import Response` або `from starlette.responses import JSONResponse`.
**FastAPI** надає ті самі `starlette.responses` як `fastapi.responses` просто для зручності для вас, розробника. Але більшість доступних типів відповідей походять безпосередньо зі Starlette.
Оскільки `Response` часто використовують для встановлення заголовків і кукі, **FastAPI** також надає його як `fastapi.Response`.
///
## Власні заголовки { #custom-headers }
Майте на увазі, що власні пропрієтарні заголовки можна додавати [за допомогою префікса `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Але якщо у вас є власні заголовки, які клієнт у браузері має бачити, вам потрібно додати їх у вашу конфігурацію CORS (докладніше в [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md)), використовуючи параметр `expose_headers`, задокументований у [документації Starlette щодо CORS](https://www.starlette.dev/middleware/#corsmiddleware).
================================================
FILE: docs/uk/docs/advanced/security/http-basic-auth.md
================================================
# HTTP Basic Auth { #http-basic-auth }
Для найпростіших випадків ви можете використовувати HTTP Basic Auth.
У HTTP Basic Auth застосунок очікує заголовок, що містить ім'я користувача та пароль.
Якщо він його не отримує, повертається помилка HTTP 401 «Unauthorized».
І повертається заголовок `WWW-Authenticate` зі значенням `Basic` та необов'язковим параметром `realm`.
Це каже браузеру показати вбудовану підсказку для введення імені користувача та пароля.
Потім, коли ви введете це ім'я користувача та пароль, браузер автоматично надішле їх у заголовку.
## Простий HTTP Basic Auth { #simple-http-basic-auth }
- Імпортуйте `HTTPBasic` і `HTTPBasicCredentials`.
- Створіть «`security` scheme» за допомогою `HTTPBasic`.
- Використайте цей `security` як залежність у вашій операції шляху.
- Він повертає об'єкт типу `HTTPBasicCredentials`:
- Він містить надіслані `username` і `password`.
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
Коли ви спробуєте відкрити URL вперше (або натиснете кнопку «Execute» у документації), браузер попросить вас ввести ім'я користувача та пароль:
## Перевірте ім'я користувача { #check-the-username }
Ось більш повний приклад.
Використайте залежність, щоб перевірити, чи правильні ім'я користувача та пароль.
Для цього використайте стандартний модуль Python [`secrets`](https://docs.python.org/3/library/secrets.html), щоб перевірити ім'я користувача та пароль.
`secrets.compare_digest()` повинен отримувати `bytes` або `str`, що містить лише ASCII-символи (англійські), це означає, що він не працюватиме з символами на кшталт `á`, як у `Sebastián`.
Щоб це обійти, ми спочатку перетворюємо `username` і `password` у `bytes`, кодувавши їх у UTF-8.
Потім ми можемо використати `secrets.compare_digest()`, щоб упевнитися, що `credentials.username` дорівнює `"stanleyjobson"`, а `credentials.password` дорівнює `"swordfish"`.
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
Це було б подібно до:
```Python
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
# Поверніть якусь помилку
...
```
Але використовуючи `secrets.compare_digest()`, це буде захищено від типу атак, що називаються «атаки за часом» (timing attacks).
### Атаки за часом { #timing-attacks }
Що таке «атака за часом»?
Уявімо, що зловмисники намагаються вгадати ім'я користувача та пароль.
Вони надсилають запит з ім'ям користувача `johndoe` та паролем `love123`.
Тоді Python-код у вашому застосунку буде еквівалентний чомусь на кшталт:
```Python
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
```
Але в той момент, коли Python порівнює першу `j` у `johndoe` з першою `s` у `stanleyjobson`, він поверне `False`, тому що вже знає, що ці дві строки не однакові, «немає сенсу витрачати обчислення на порівняння решти літер». І ваш застосунок скаже «Невірні ім'я користувача або пароль».
А потім зловмисники спробують з ім'ям користувача `stanleyjobsox` і паролем `love123`.
І ваш код застосунку зробить щось на кшталт:
```Python
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
```
Python доведеться порівняти весь `stanleyjobso` у обох `stanleyjobsox` і `stanleyjobson`, перш ніж зрозуміти, що строки різні. Тому відповідь «Невірні ім'я користувача або пароль» займе на кілька мікросекунд довше.
#### Час відповіді допомагає зловмисникам { #the-time-to-answer-helps-the-attackers }
У цей момент, помітивши, що сервер витратив на кілька мікросекунд більше, щоб надіслати відповідь «Невірні ім'я користувача або пароль», зловмисники знатимуть, що вони щось вгадали, деякі початкові літери правильні.
І тоді вони можуть спробувати знову, знаючи, що це, ймовірно, щось більш схоже на `stanleyjobsox`, ніж на `johndoe`.
#### «Професійна» атака { #a-professional-attack }
Звісно, зловмисники не робитимуть усе це вручну, вони напишуть програму, можливо з тисячами або мільйонами перевірок за секунду. І вони отримуватимуть лише по одній правильній літері за раз.
Але так за кілька хвилин або годин зловмисники вгадають правильні ім'я користувача та пароль, «за допомогою» нашого застосунку, просто використовуючи час, потрібний для відповіді.
#### Виправте за допомогою `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
Але в нашому коді ми насправді використовуємо `secrets.compare_digest()`.
Коротко, він витрачає однаковий час на порівняння `stanleyjobsox` зі `stanleyjobson`, як і на порівняння `johndoe` зі `stanleyjobson`. І так само для пароля.
Таким чином, використовуючи `secrets.compare_digest()` у коді вашого застосунку, він буде захищений від усього цього класу атак безпеки.
### Поверніть помилку { #return-the-error }
Після виявлення, що облікові дані неправильні, поверніть `HTTPException` з кодом статусу 401 (такий самий повертається, коли облікові дані не надані) і додайте заголовок `WWW-Authenticate`, щоб браузер знову показав підсказку входу:
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
================================================
FILE: docs/uk/docs/advanced/security/index.md
================================================
# Просунута безпека { #advanced-security }
## Додаткові можливості { #additional-features }
Є кілька додаткових можливостей для роботи з безпекою, окрім тих, що розглянуті в [Навчальний посібник - Посібник користувача: Безпека](../../tutorial/security/index.md).
/// tip | Порада
Наступні розділи **не обов'язково «просунуті»**.
І можливо, що для вашого випадку використання рішення є в одному з них.
///
## Спершу прочитайте навчальний посібник { #read-the-tutorial-first }
У наступних розділах передбачається, що ви вже прочитали основний [Навчальний посібник - Посібник користувача: Безпека](../../tutorial/security/index.md).
Усі вони базуються на тих самих концепціях, але надають деякі додаткові можливості.
================================================
FILE: docs/uk/docs/advanced/security/oauth2-scopes.md
================================================
# OAuth2 scopes { #oauth2-scopes }
Ви можете використовувати OAuth2 scopes безпосередньо з **FastAPI**, вони інтегровані для безшовної роботи.
Це дозволить мати більш детальну систему дозволів, відповідно до стандарту OAuth2, інтегровану у ваш застосунок OpenAPI (і документацію API).
OAuth2 зі scopes - це механізм, який використовують багато великих провайдерів автентифікації, як-от Facebook, Google, GitHub, Microsoft, X (Twitter) тощо. Вони застосовують його, щоб надавати конкретні дозволи користувачам і застосункам.
Кожного разу, коли ви «log in with» Facebook, Google, GitHub, Microsoft, X (Twitter), цей застосунок використовує OAuth2 зі scopes.
У цьому розділі ви побачите, як керувати автентифікацією та авторизацією за допомогою того ж OAuth2 зі scopes у вашому застосунку **FastAPI**.
/// warning | Попередження
Це більш-менш просунутий розділ. Якщо ви тільки починаєте, можете пропустити його.
Вам не обов’язково потрібні OAuth2 scopes, ви можете керувати автентифікацією та авторизацією будь-яким зручним способом.
Але OAuth2 зі scopes можна гарно інтегрувати у ваш API (з OpenAPI) і документацію API.
Водночас ви все одно примушуєте виконувати ці scopes або будь-які інші вимоги безпеки/авторизації так, як потрібно, у своєму коді.
У багатьох випадках OAuth2 зі scopes - це надмірність.
Але якщо ви знаєте, що це потрібно, або просто цікаво, читайте далі.
///
## OAuth2 scopes та OpenAPI { #oauth2-scopes-and-openapi }
Специфікація OAuth2 визначає «scopes» як список строк, розділених пробілами.
Вміст кожної з цих строк може мати будь-який формат, але не повинен містити пробілів.
Ці scopes представляють «дозволи».
В OpenAPI (наприклад, у документації API) ви можете визначати «схеми безпеки».
Коли одна з цих схем безпеки використовує OAuth2, ви також можете оголошувати та використовувати scopes.
Кожен «scope» - це просто строка (без пробілів).
Зазвичай їх використовують для оголошення конкретних дозволів безпеки, наприклад:
- `users:read` або `users:write` - поширені приклади.
- `instagram_basic` використовується Facebook / Instagram.
- `https://www.googleapis.com/auth/drive` використовується Google.
/// info | Інформація
В OAuth2 «scope» - це просто строка, що декларує конкретний потрібний дозвіл.
Не має значення, чи містить вона інші символи на кшталт `:` або чи це URL.
Ці деталі специфічні для реалізації.
Для OAuth2 це просто строки.
///
## Загальний огляд { #global-view }
Спочатку швидко подивімося на частини, що відрізняються від прикладів у головному **Навчальному посібнику - Керівництві користувача** для [OAuth2 з паролем (і хешуванням), Bearer з JWT-токенами](../../tutorial/security/oauth2-jwt.md). Тепер із використанням OAuth2 scopes:
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
Тепер розгляньмо ці зміни крок за кроком.
## Схема безпеки OAuth2 { #oauth2-security-scheme }
Перша зміна - тепер ми оголошуємо схему безпеки OAuth2 з двома доступними scopes: `me` і `items`.
Параметр `scopes` приймає `dict`, де кожен scope - це ключ, а опис - значення:
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
Оскільки тепер ми оголошуємо ці scopes, вони з’являться в документації API, коли ви увійдете/авторизуєтеся.
І ви зможете обрати, які scopes надати доступ: `me` і `items`.
Це той самий механізм, який використовується, коли ви надаєте дозволи під час входу через Facebook, Google, GitHub тощо:
## JWT токен зі scopes { #jwt-token-with-scopes }
Тепер змініть операцію шляху токена, щоб повертати запитані scopes.
Ми все ще використовуємо той самий `OAuth2PasswordRequestForm`. Він містить властивість `scopes` зі `list` з `str`, по одному scope, отриманому в запиті.
І ми повертаємо scopes як частину JWT токена.
/// danger | Обережно
Для простоти тут ми просто додаємо отримані scopes безпосередньо до токена.
Але у вашому застосунку, з міркувань безпеки, переконайтеся, що ви додаєте лише ті scopes, які користувач дійсно може мати, або ті, що ви попередньо визначили.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
## Оголосіть scopes в операціях шляху та залежностях { #declare-scopes-in-path-operations-and-dependencies }
Тепер ми оголошуємо, що операція шляху для `/users/me/items/` вимагає scope `items`.
Для цього імпортуємо і використовуємо `Security` з `fastapi`.
Ви можете використовувати `Security` для оголошення залежностей (так само як `Depends`), але `Security` також приймає параметр `scopes` зі списком scopes (строк).
У цьому випадку ми передаємо функцію-залежність `get_current_active_user` до `Security` (так само, як зробили б із `Depends`).
А також передаємо `list` scopes, у цьому випадку лише один scope: `items` (їх могло б бути більше).
І функція-залежність `get_current_active_user` також може оголошувати підзалежності не лише з `Depends`, а й з `Security`. Оголошуючи свою підзалежність (`get_current_user`) і додаткові вимоги до scopes.
У цьому випадку вона вимагає scope `me` (вона могла б вимагати більш ніж один scope).
/// note | Примітка
Вам не обов’язково додавати різні scopes у різних місцях.
Ми робимо це тут, щоб показати, як **FastAPI** обробляє scopes, оголошені на різних рівнях.
///
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
/// info | Технічні деталі
`Security` насправді є підкласом `Depends`, і має лише один додатковий параметр, який ми побачимо пізніше.
Але використовуючи `Security` замість `Depends`, **FastAPI** знатиме, що можна оголошувати scopes безпеки, використовувати їх внутрішньо та документувати API через OpenAPI.
Коли ви імпортуєте `Query`, `Path`, `Depends`, `Security` та інші з `fastapi`, це насправді функції, що повертають спеціальні класи.
///
## Використовуйте `SecurityScopes` { #use-securityscopes }
Тепер оновіть залежність `get_current_user`.
Вона використовується наведеними вище залежностями.
Тут ми використовуємо ту саму схему OAuth2, створену раніше, оголошуючи її як залежність: `oauth2_scheme`.
Оскільки ця функція-залежність не має власних вимог до scopes, ми можемо використовувати `Depends` з `oauth2_scheme`, немає потреби застосовувати `Security`, коли не потрібно вказувати scopes безпеки.
Ми також оголошуємо спеціальний параметр типу `SecurityScopes`, імпортований з `fastapi.security`.
Клас `SecurityScopes` подібний до `Request` (у `Request` ми напряму отримували об’єкт запиту).
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
## Використовуйте scopes { #use-the-scopes }
Параметр `security_scopes` матиме тип `SecurityScopes`.
Він матиме властивість `scopes` зі списком, що містить усі scopes, потрібні самій функції та всім залежним, які використовують її як підзалежність. Тобто всім «залежним»... це може звучати заплутано, нижче пояснено ще раз.
Об’єкт `security_scopes` (класу `SecurityScopes`) також надає атрибут `scope_str` з одним рядком, що містить ці scopes, розділені пробілами (ми його використаємо).
Ми створюємо `HTTPException`, який зможемо повторно використати (`raise`) у кількох місцях.
У цьому винятку ми включаємо потрібні scopes (якщо є) як строку, розділену пробілами (використовуючи `scope_str`). Ми поміщаємо цю строку зі scopes в заголовок `WWW-Authenticate` (це частина специфікації).
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
## Перевірте `username` і структуру даних { #verify-the-username-and-data-shape }
Ми перевіряємо, що отримали `username`, і видобуваємо scopes.
Потім валідовуємо ці дані за допомогою Pydantic-моделі (перехоплюючи виняток `ValidationError`), і якщо виникає помилка читання JWT токена або валідації даних Pydantic, підіймаємо раніше створений `HTTPException`.
Для цього ми оновлюємо Pydantic-модель `TokenData` новою властивістю `scopes`.
Валідувавши дані через Pydantic, ми гарантуємо, що, наприклад, маємо саме `list` із `str` для scopes і `str` для `username`.
Замість, наприклад, `dict` або чогось іншого, що може зламати застосунок далі, створивши ризик безпеки.
Ми також перевіряємо, що існує користувач із цим username, інакше підіймаємо той самий виняток.
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
## Перевірте `scopes` { #verify-the-scopes }
Тепер перевіряємо, що всі потрібні scopes - для цієї залежності та всіх залежних (включно з операціями шляху) - містяться в scopes, наданих у отриманому токені, інакше підіймаємо `HTTPException`.
Для цього використовуємо `security_scopes.scopes`, що містить `list` із усіма цими scopes як `str`.
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
## Дерево залежностей і scopes { #dependency-tree-and-scopes }
Ще раз розгляньмо дерево залежностей і scopes.
Оскільки залежність `get_current_active_user` має підзалежність `get_current_user`, scope «me», оголошений у `get_current_active_user`, буде включений до списку потрібних scopes у `security_scopes.scopes`, переданого до `get_current_user`.
Сама операція шляху також оголошує scope «items», отже він також буде у списку `security_scopes.scopes`, переданому до `get_current_user`.
Ось як виглядає ієрархія залежностей і scopes:
- Операція шляху `read_own_items` має:
- Потрібні scopes `["items"]` із залежністю:
- `get_current_active_user`:
- Функція-залежність `get_current_active_user` має:
- Потрібні scopes `["me"]` із залежністю:
- `get_current_user`:
- Функція-залежність `get_current_user` має:
- Власних scopes не потребує.
- Залежність, що використовує `oauth2_scheme`.
- Параметр `security_scopes` типу `SecurityScopes`:
- Цей параметр `security_scopes` має властивість `scopes` із `list`, що містить усі наведені вище scopes, отже:
- `security_scopes.scopes` міститиме `["me", "items"]` для операції шляху `read_own_items`.
- `security_scopes.scopes` міститиме `["me"]` для операції шляху `read_users_me`, адже він оголошений у залежності `get_current_active_user`.
- `security_scopes.scopes` міститиме `[]` (нічого) для операції шляху `read_system_status`, бо вона не оголошує жодного `Security` зі `scopes`, і її залежність `get_current_user` також не оголошує жодних `scopes`.
/// tip | Порада
Важливе і «магічне» тут у тому, що `get_current_user` матиме різні списки `scopes` для перевірки для кожної операції шляху.
Усе залежить від `scopes`, оголошених у кожній операції шляху та кожній залежності в дереві залежностей для конкретної операції шляху.
///
## Більше деталей про `SecurityScopes` { #more-details-about-securityscopes }
Ви можете використовувати `SecurityScopes` у будь-якому місці й у кількох місцях, він не обов’язково має бути в «кореневій» залежності.
Він завжди міститиме scopes безпеки, оголошені в поточних залежностях `Security` і всіх залежних для **цієї конкретної** операції шляху і **цього конкретного** дерева залежностей.
Оскільки `SecurityScopes` міститиме всі scopes, оголошені залежними, ви можете використовувати його, щоб перевірити, що токен має потрібні scopes, у центральній функції-залежності, а потім оголошувати різні вимоги до scopes у різних операціях шляху.
Вони перевірятимуться незалежно для кожної операції шляху.
## Перевірте { #check-it }
Якщо ви відкриєте документацію API, ви зможете автентифікуватися і вказати, які scopes хочете авторизувати.
Якщо ви не оберете жодного scope, ви будете «автентифіковані», але при спробі доступу до `/users/me/` або `/users/me/items/` отримаєте помилку про недостатні дозволи. Ви все ще матимете доступ до `/status/`.
Якщо оберете scope `me`, але не scope `items`, ви зможете отримати доступ до `/users/me/`, але не до `/users/me/items/`.
Так станеться зі стороннім застосунком, який спробує звернутися до однієї з цих операцій шляху з токеном, наданим користувачем, залежно від того, скільки дозволів користувач надав застосунку.
## Про сторонні інтеграції { #about-third-party-integrations }
У цьому прикладі ми використовуємо «потік паролю» OAuth2.
Це доречно, коли ми входимо у власний застосунок, ймовірно, з власним фронтендом.
Адже ми можемо довіряти йому отримання `username` і `password`, бо ми його контролюємо.
Але якщо ви створюєте OAuth2-застосунок, до якого підключатимуться інші (тобто якщо ви створюєте провайдера автентифікації на кшталт Facebook, Google, GitHub тощо), слід використовувати один з інших потоків.
Найпоширеніший - неявний потік (implicit flow).
Найбезпечніший - потік коду (code flow), але його складніше реалізувати, оскільки він потребує більше кроків. Через складність багато провайдерів у підсумку радять неявний потік.
/// note | Примітка
Часто кожен провайдер автентифікації називає свої потоки по-різному, роблячи це частиною свого бренду.
Але зрештою вони реалізують той самий стандарт OAuth2.
///
**FastAPI** містить утиліти для всіх цих потоків автентифікації OAuth2 у `fastapi.security.oauth2`.
## `Security` у параметрі декоратора `dependencies` { #security-in-decorator-dependencies }
Так само як ви можете визначити `list` із `Depends` у параметрі `dependencies` декоратора (як пояснено в [Залежності в декораторах операцій шляху](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md)), ви також можете використовувати там `Security` зі `scopes`.
================================================
FILE: docs/uk/docs/advanced/settings.md
================================================
# Налаштування та змінні оточення { #settings-and-environment-variables }
У багатьох випадках вашому застосунку можуть знадобитися зовнішні налаштування або конфігурації, наприклад секретні ключі, облікові дані бази даних, облікові дані для email-сервісів тощо.
Більшість із цих налаштувань змінні (можуть змінюватися), як-от URL-адреси баз даних. І багато з них можуть бути чутливими, як-от секрети.
З цієї причини поширено надавати їх у змінних оточення, які зчитуються застосунком.
/// tip | Порада
Щоб зрозуміти змінні оточення, ви можете прочитати [Змінні оточення](../environment-variables.md).
///
## Типи та перевірка { #types-and-validation }
Ці змінні оточення можуть містити лише текстові строки, оскільки вони зовнішні до Python і мають бути сумісні з іншими програмами та рештою системи (і навіть з різними операційними системами, як-от Linux, Windows, macOS).
Це означає, що будь-яке значення, прочитане в Python зі змінної оточення, буде `str`, і будь-яке перетворення в інший тип або будь-яка перевірка мають виконуватися в коді.
## Pydantic `Settings` { #pydantic-settings }
На щастя, Pydantic надає чудовий інструмент для обробки цих налаштувань із змінних оточення - [Pydantic: Settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/).
### Встановіть `pydantic-settings` { #install-pydantic-settings }
Спершу переконайтеся, що ви створили [віртуальне оточення](../virtual-environments.md), активували його, а потім встановили пакет `pydantic-settings`:
### Створіть об'єкт `Settings` { #create-the-settings-object }
Імпортуйте `BaseSettings` із Pydantic і створіть підклас, дуже подібно до моделі Pydantic.
Так само, як і з моделями Pydantic, ви оголошуєте атрибути класу з анотаціями типів і, за потреби, значеннями за замовчуванням.
Ви можете використовувати всі ті самі можливості перевірки та інструменти, що й для моделей Pydantic, як-от різні типи даних і додаткові перевірки з `Field()`.
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
/// tip | Порада
Якщо вам потрібно щось швидко скопіювати й вставити, не використовуйте цей приклад, скористайтеся останнім нижче.
///
Потім, коли ви створите екземпляр цього класу `Settings` (у цьому випадку в об'єкті `settings`), Pydantic зчитуватиме змінні оточення без урахування регістру, тож верхньорегістрова змінна `APP_NAME` все одно буде прочитана для атрибута `app_name`.
Далі він перетворить і перевірить дані. Тож коли ви використовуватимете об'єкт `settings`, у вас будуть дані тих типів, які ви оголосили (наприклад, `items_per_user` буде `int`).
### Використовуйте `settings` { #use-the-settings }
Потім ви можете використати новий об'єкт `settings` у вашому застосунку:
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
### Запустіть сервер { #run-the-server }
Далі ви б запустили сервер, передаючи конфігурації як змінні оточення, наприклад, ви можете встановити `ADMIN_EMAIL` і `APP_NAME` так:
```console
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
/// tip | Порада
Щоб встановити кілька змінних оточення для однієї команди, просто розділіть їх пробілами і розмістіть усі перед командою.
///
Після цього налаштування `admin_email` буде встановлено в `"deadpool@example.com"`.
`app_name` буде `"ChimichangApp"`.
А `items_per_user` збереже своє значення за замовчуванням `50`.
## Налаштування в іншому модулі { #settings-in-another-module }
Ви можете розмістити ці налаштування в іншому модулі, як ви бачили в [Більші застосунки - кілька файлів](../tutorial/bigger-applications.md).
Наприклад, у вас може бути файл `config.py` з:
{* ../../docs_src/settings/app01_py310/config.py *}
А потім використати його у файлі `main.py`:
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
/// tip | Порада
Вам також знадобиться файл `__init__.py`, як ви бачили в [Більші застосунки - кілька файлів](../tutorial/bigger-applications.md).
///
## Налаштування як залежність { #settings-in-a-dependency }
Іноді може бути корисно надавати налаштування через залежність, замість того, щоб мати глобальний об'єкт `settings`, який використовується всюди.
Це може бути особливо корисно під час тестування, оскільки дуже легко переписати залежність власними налаштуваннями.
### Файл конфігурації { #the-config-file }
Продовжуючи попередній приклад, ваш файл `config.py` може виглядати так:
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
Зверніть увагу, що тепер ми не створюємо екземпляр за замовчуванням `settings = Settings()`.
### Основний файл застосунку { #the-main-app-file }
Тепер ми створюємо залежність, яка повертає новий `config.Settings()`.
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
/// tip | Порада
Ми обговоримо `@lru_cache` трохи згодом.
Поки що можете вважати, що `get_settings()` - це звичайна функція.
///
А далі ми можемо вимагати її у *функції операції шляху* як залежність і використовувати будь-де, де це потрібно.
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
### Налаштування і тестування { #settings-and-testing }
Потім буде дуже просто надати інший об'єкт налаштувань під час тестування, створивши переписування залежності для `get_settings`:
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
У переписуванні залежності ми встановлюємо нове значення `admin_email` під час створення нового об'єкта `Settings`, а потім повертаємо цей новий об'єкт.
Після цього ми можемо перевірити, що саме він використовується.
## Читання файлу `.env` { #reading-a-env-file }
Якщо у вас багато налаштувань, які можуть часто змінюватися, можливо в різних оточеннях, може бути корисно розмістити їх у файлі, а потім зчитувати їх із нього так, ніби це змінні оточення.
Ця практика достатньо поширена, тож має назву - ці змінні оточення зазвичай розміщуються у файлі `.env`, а сам файл називається «dotenv».
/// tip | Порада
Файл, що починається з крапки (`.`), є прихованим у системах, подібних до Unix, як-от Linux і macOS.
Але файл dotenv не обов'язково має мати саме таку назву.
///
Pydantic має підтримку читання з таких типів файлів за допомогою зовнішньої бібліотеки. Ви можете дізнатися більше тут: [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support).
/// tip | Порада
Щоб це працювало, потрібно виконати `pip install python-dotenv`.
///
### Файл `.env` { #the-env-file }
У вас може бути файл `.env` із вмістом:
```bash
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
```
### Зчитування налаштувань із `.env` { #read-settings-from-env }
Потім оновіть ваш `config.py` так:
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
/// tip | Порада
Атрибут `model_config` використовується лише для конфігурації Pydantic. Докладніше: [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/).
///
Тут ми визначаємо конфіг `env_file` усередині вашого класу Pydantic `Settings` і задаємо значення - ім'я файла з dotenv, який ми хочемо використати.
### Створення `Settings` лише один раз за допомогою `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
Читання файла з диска зазвичай є дорогою (повільною) операцією, тож, імовірно, ви захочете робити це лише один раз і потім перевикористовувати той самий об'єкт налаштувань замість зчитування для кожного запиту.
Але щоразу, коли ми робимо:
```Python
Settings()
```
буде створено новий об'єкт `Settings`, і під час створення він знову зчитуватиме файл `.env`.
Якби функція залежності виглядала так:
```Python
def get_settings():
return Settings()
```
ми створювали б цей об'єкт для кожного запиту і читали б файл `.env` для кожного запиту. ⚠️
Але оскільки ми використовуємо декоратор `@lru_cache` зверху, об'єкт `Settings` буде створено лише один раз, під час першого виклику. ✔️
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
Потім для будь-якого подальшого виклику `get_settings()` у залежностях для наступних запитів, замість виконання внутрішнього коду `get_settings()` і створення нового об'єкта `Settings`, він повертатиме той самий об'єкт, що був повернутий під час першого виклику, знову і знову.
#### Технічні деталі `lru_cache` { #lru-cache-technical-details }
`@lru_cache` модифікує функцію, яку він декорує, так, щоб вона повертала те саме значення, що й уперше, замість повторного обчислення, виконуючи код функції щоразу.
Тобто функція під ним буде виконана один раз для кожної комбінації аргументів. А потім значення, повернені кожною з цих комбінацій аргументів, використовуватимуться знову і знову щоразу, коли функцію викликають із точно такою ж комбінацією аргументів.
Наприклад, якщо у вас є функція:
```Python
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
ваша програма може виконуватись так:
```mermaid
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
```
У випадку з нашою залежністю `get_settings()` функція взагалі не приймає жодних аргументів, тож вона завжди повертає те саме значення.
Таким чином, вона поводиться майже так само, якби це була просто глобальна змінна. Але оскільки використовується функція залежності, ми можемо легко переписати її для тестування.
`@lru_cache` є частиною `functools`, що входить до стандартної бібліотеки Python, більше про це можна прочитати в [документації Python для `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache).
## Підсумок { #recap }
Ви можете використовувати Pydantic Settings для обробки налаштувань або конфігурацій вашого застосунку, з усією потужністю моделей Pydantic.
- Використовуючи залежність, ви можете спростити тестування.
- Ви можете використовувати з ним файли `.env`.
- Використання `@lru_cache` дає змогу уникнути повторного читання файла dotenv для кожного запиту, водночас дозволяючи переписувати його під час тестування.
================================================
FILE: docs/uk/docs/advanced/stream-data.md
================================================
# Потокова передача даних { #stream-data }
Якщо ви хочете передавати потоком дані, які можна структурувати як JSON, див. [Потокова передача JSON Lines](../tutorial/stream-json-lines.md).
Але якщо ви хочете передавати потоком чисті бінарні дані або строки, ось як це зробити.
/// info | Інформація
Додано у FastAPI 0.134.0.
///
## Варіанти використання { #use-cases }
Це можна використовувати, якщо ви хочете передавати потоком чисті строки, наприклад безпосередньо з виводу сервісу AI LLM.
Також це можна використати для потокової передачі великих бінарних файлів, коли ви надсилаєте кожний фрагмент даних під час читання, без потреби завантажувати все в пам'ять одразу.
Так само можна стрімити відео чи аудіо; їх навіть можна генерувати під час обробки та надсилання.
## `StreamingResponse` з `yield` { #a-streamingresponse-with-yield }
Якщо ви оголосите `response_class=StreamingResponse` у вашій функції операції шляху, ви можете використовувати `yield`, щоб послідовно надсилати кожний фрагмент даних.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *}
FastAPI передаватиме кожний фрагмент даних до `StreamingResponse` як є; він не намагатиметься перетворити його на JSON чи щось подібне.
### Не-async функції операції шляху { #non-async-path-operation-functions }
Можна також використовувати звичайні функції `def` (без `async`) і так само застосовувати `yield`.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *}
### Без анотації { #no-annotation }
Для потокової передачі бінарних даних немає потреби оголошувати анотацію типу, що повертається.
Оскільки FastAPI не намагатиметься перетворювати дані на JSON за допомогою Pydantic чи серіалізувати їх іншим чином, у цьому випадку анотація типу потрібна лише для вашого редактора та інструментів; FastAPI її не використовуватиме.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *}
Це також означає, що з `StreamingResponse` у вас є свобода і відповідальність формувати та кодувати байти даних саме так, як їх потрібно надіслати, незалежно від анотацій типів. 🤓
### Потік байтів { #stream-bytes }
Один з основних сценаріїв - передавати потоком `bytes` замість строк; це, звісно, підтримується.
{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *}
## Користувацький `PNGStreamingResponse` { #a-custom-pngstreamingresponse }
У наведених вище прикладах байти даних передавалися потоком, але у відповіді не було заголовка `Content-Type`, тому клієнт не знав, який тип даних він отримує.
Можна створити власний підклас `StreamingResponse`, який встановлює заголовок `Content-Type` відповідно до типу даних, що ви стрімите.
Наприклад, можна створити `PNGStreamingResponse`, який встановлює заголовок `Content-Type` у `image/png` за допомогою атрибута `media_type`:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *}
Потім ви можете використати цей новий клас у `response_class=PNGStreamingResponse` у вашій функції операції шляху:
{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *}
### Симулювати файл { #simulate-a-file }
У цьому прикладі ми імітуємо файл за допомогою `io.BytesIO` - це об'єкт на кшталт файлу, який існує лише в пам'яті, але надає той самий інтерфейс.
Наприклад, ми можемо ітеруватися по ньому, щоб зчитати вміст, так само як і з файлом.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *}
/// note | Технічні деталі
Інші дві змінні, `image_base64` та `binary_image`, - це зображення, закодоване в Base64, яке потім перетворюється на байти, щоб передати його в `io.BytesIO`.
Лише для того, щоб усе містилося в одному файлі для цього прикладу, і ви могли скопіювати його та запустити як є. 🥚
///
Використовуючи блок `with`, ми гарантуємо, що об'єкт, подібний до файлу, буде закрито після завершення генераторної функції (функції з `yield`). Тобто після завершення надсилання відповіді.
У цьому конкретному прикладі це не так важливо, адже це фальшивий файл у пам'яті (з `io.BytesIO`), але для справжнього файлу важливо переконатися, що файл закрито після завершення роботи з ним.
### Файли та async { #files-and-async }
У більшості випадків об'єкти, подібні до файлів, за замовчуванням несумісні з `async` та `await`.
Наприклад, у них немає `await file.read()` або `async for chunk in file`.
І часто їх читання є блокувальною операцією (що може блокувати цикл подій), адже дані зчитуються з диска або мережі.
/// info | Інформація
Наведений вище приклад - виняток, адже об'єкт `io.BytesIO` вже в пам'яті, тож читання нічого не блокує.
Але в багатьох випадках читання файлу або схожого на файл об'єкта блокує виконання.
///
Щоб уникнути блокування циклу подій, просто оголосіть функцію операції шляху зі звичайним `def` замість `async def`. Тоді FastAPI виконуватиме її в працівнику пулу потоків, щоб не блокувати головний цикл.
{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *}
/// tip | Порада
Якщо вам потрібно викликати блокувальний код усередині async-функції або async-функцію зсередини блокувальної функції, ви можете скористатися [Asyncer](https://asyncer.tiangolo.com) - спорідненою бібліотекою до FastAPI.
///
### `yield from` { #yield-from }
Коли ви ітеруєтеся по чомусь, наприклад по об'єкту, подібному до файлу, і робите `yield` для кожного елемента, можна також використати `yield from`, щоб віддавати кожен елемент напряму і пропустити цикл `for`.
Це не специфічно для FastAPI, це просто Python, але корисний трюк. 😎
{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *}
================================================
FILE: docs/uk/docs/advanced/strict-content-type.md
================================================
# Сувора перевірка Content-Type { #strict-content-type-checking }
За замовчуванням **FastAPI** використовує сувору перевірку заголовка `Content-Type` для тіл запитів JSON, це означає, що запити JSON мають включати дійсний заголовок `Content-Type` (наприклад, `application/json`), щоб тіло було розібране як JSON.
## Ризик CSRF { #csrf-risk }
Ця поведінка за замовчуванням забезпечує захист від класу атак **Cross-Site Request Forgery (CSRF)** у дуже конкретному сценарії.
Ці атаки використовують той факт, що браузери дозволяють скриптам надсилати запити без виконання перевірки CORS preflight, коли вони:
* не мають заголовка `Content-Type` (наприклад, використовуючи `fetch()` з тілом типу `Blob`)
* і не надсилають жодних облікових даних автентифікації.
Такий тип атаки головним чином актуальний, коли:
* застосунок працює локально (наприклад, на `localhost`) або у внутрішній мережі
* і в застосунку немає жодної автентифікації, очікується, що будь-який запит з тієї ж мережі є надійним.
## Приклад атаки { #example-attack }
Уявіть, що ви створюєте спосіб запускати локального AI-агента.
Він надає API за адресою
```
http://localhost:8000/v1/agents/multivac
```
Є також фронтенд за адресою
```
http://localhost:8000
```
/// tip | Порада
Зауважте, що обидва мають один і той самий хост.
///
Використовуючи фронтенд, ви можете змушувати AI-агента виконувати дії від вашого імені.
Оскільки він працює локально, а не у відкритому інтернеті, ви вирішуєте не налаштовувати жодної автентифікації, просто покладаючись на доступ до локальної мережі.
Один із ваших користувачів може встановити його і запустити локально.
Потім він може відкрити шкідливий вебсайт, напр. щось на кшталт
```
https://evilhackers.example.com
```
І цей шкідливий вебсайт надсилає запити, використовуючи `fetch()` з тілом типу `Blob`, до локального API за адресою
```
http://localhost:8000/v1/agents/multivac
```
Хоча хости шкідливого вебсайту та локального застосунку різні, браузер не запустить CORS preflight-запит, тому що:
* Застосунок працює без будь-якої автентифікації, немає потреби надсилати облікові дані.
* Браузер вважає, що він не надсилає JSON (через відсутній заголовок `Content-Type`).
Тоді шкідливий вебсайт може змусити локального AI-агента надсилати злі повідомлення колишньому босу користувача... або щось гірше. 😅
## Відкритий інтернет { #open-internet }
Якщо ваш застосунок у відкритому інтернеті, ви не стали б «довіряти мережі» і дозволяти кому завгодно надсилати привілейовані запити без автентифікації.
Зловмисники можуть просто запустити скрипт, щоб надсилати запити до вашого API, без будь-якої участі браузера, тож ви, ймовірно, вже захищаєте будь-які привілейовані ендпоїнти.
У такому разі ця атака/ризик до вас не застосовується.
Цей ризик і атака головним чином актуальні, коли застосунок працює в локальній мережі і це єдиний передбачуваний захист.
## Дозволення запитів без Content-Type { #allowing-requests-without-content-type }
Якщо вам потрібно підтримувати клієнтів, які не надсилають заголовок `Content-Type`, ви можете вимкнути сувору перевірку, встановивши `strict_content_type=False`:
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
З цим налаштуванням запити без заголовка `Content-Type` матимуть тіло, розібране як JSON, що відповідає поведінці старіших версій FastAPI.
/// info | Інформація
Цю поведінку і конфігурацію додано у FastAPI 0.132.0.
///
================================================
FILE: docs/uk/docs/advanced/sub-applications.md
================================================
# Підзастосунки - монтування { #sub-applications-mounts }
Якщо вам потрібно мати два незалежні застосунки FastAPI з власними незалежними OpenAPI та власними інтерфейсами документації, ви можете мати головний застосунок і «змонтувати» один або кілька підзастосунків.
## Монтування застосунку **FastAPI** { #mounting-a-fastapi-application }
«Монтування» означає додавання повністю «незалежного» застосунку на конкретний шлях, який далі обробляє все під цим шляхом за допомогою _операцій шляху_, оголошених у цьому підзастосунку.
### Застосунок верхнього рівня { #top-level-application }
Спочатку створіть головний, верхньорівневий застосунок **FastAPI** та його _операції шляху_:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
### Підзастосунок { #sub-application }
Потім створіть свій підзастосунок та його _операції шляху_.
Цей підзастосунок - просто ще один стандартний застосунок FastAPI, але саме його буде «змонтовано»:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
### Змонтуйте підзастосунок { #mount-the-sub-application }
У вашому застосунку верхнього рівня, `app`, змонтуйте підзастосунок `subapi`.
У цьому випадку його буде змонтовано за шляхом `/subapi`:
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
### Перевірте автоматичну документацію API { #check-the-automatic-api-docs }
Тепер запустіть команду `fastapi`:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
І відкрийте документацію за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите автоматичну документацію API для головного застосунку, що містить лише його власні _операції шляху_:
А потім відкрийте документацію для підзастосунку за адресою [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs).
Ви побачите автоматичну документацію API для підзастосунку, що містить лише його власні _операції шляху_, усі з правильним префіксом підшляху `/subapi`:
Якщо ви спробуєте взаємодіяти з будь-яким із двох інтерфейсів користувача, вони працюватимуть коректно, адже браузер зможе спілкуватися з кожним конкретним застосунком або підзастосунком.
### Технічні деталі: `root_path` { #technical-details-root-path }
Коли ви монтуєте підзастосунок, як описано вище, FastAPI подбає про передачу шляху монтування для підзастосунку, використовуючи механізм зі специфікації ASGI під назвою `root_path`.
Таким чином підзастосунок знатиме, що слід використовувати цей префікс шляху для інтерфейсу документації.
Підзастосунок також може мати власні змонтовані підзастосунки, і все працюватиме коректно, оскільки FastAPI автоматично обробляє всі ці `root_path`.
Ви дізнаєтеся більше про `root_path` і як використовувати його явно в розділі [За представником](behind-a-proxy.md).
================================================
FILE: docs/uk/docs/advanced/templates.md
================================================
# Шаблони { #templates }
Ви можете використовувати будь-який рушій шаблонів з **FastAPI**.
Поширений вибір - Jinja2, той самий, що використовується у Flask та інших інструментах.
Є утиліти для простої конфігурації, які ви можете використовувати безпосередньо у вашому застосунку **FastAPI** (надає Starlette).
## Встановіть залежності { #install-dependencies }
Переконайтеся, що ви створили [віртуальне оточення](../virtual-environments.md), активували його та встановили `jinja2`:
/// info | Інформація
Існують інші формати та інструменти для визначення і встановлення залежностей пакетів.
///
### Створіть код **FastAPI** { #create-the-fastapi-code }
* Створіть директорію `app` і перейдіть у неї.
* Створіть порожній файл `__init__.py`.
* Створіть файл `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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
### Dockerfile { #dockerfile }
Тепер у тій самій директорії проєкту створіть файл `Dockerfile` з вмістом:
```{ .dockerfile .annotate }
# (1)!
FROM python:3.14
# (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 ["fastapi", "run", "app/main.py", "--port", "80"]
```
1. Почніть з офіційного базового образу Python.
2. Встановіть поточну робочу директорію в `/code`.
Саме сюди ми помістимо файл `requirements.txt` і директорію `app`.
3. Скопіюйте файл з вимогами в директорію `/code`.
Спочатку скопіюйте лише файл з вимогами, а не решту коду.
Оскільки цей файл змінюється нечасто, Docker виявить це і використає кеш для цього кроку, що також увімкне кеш і для наступного кроку.
4. Встановіть залежності пакетів із файлу вимог.
Опція `--no-cache-dir` каже `pip` не зберігати завантажені пакети локально, адже це потрібно лише тоді, якщо `pip` буде запущено знову для встановлення тих самих пакетів, але в роботі з контейнерами це не так.
/// note | Примітка
`--no-cache-dir` стосується лише `pip`, це не має відношення до Docker чи контейнерів.
///
Опція `--upgrade` каже `pip` оновити пакети, якщо вони вже встановлені.
Оскільки попередній крок копіювання файлу може бути виявлений кешем Docker, цей крок також використовуватиме кеш Docker, коли це можливо.
Використання кешу на цьому кроці збереже вам багато часу під час повторних збірок образу в розробці, замість того щоб завжди завантажувати і встановлювати всі залежності.
5. Скопіюйте директорію `./app` у директорію `/code`.
Оскільки тут увесь код, який змінюється найчастіше, кеш Docker не буде легко використаний для цього або будь-яких наступних кроків.
Тому важливо розмістити це ближче до кінця `Dockerfile`, щоб оптимізувати час збірки образу контейнера.
6. Встановіть команду для використання `fastapi run`, яка всередині використовує Uvicorn.
`CMD` приймає список строк, кожна з яких - це те, що ви б набирали в командному рядку, розділене пробілами.
Ця команда буде виконана з поточної робочої директорії, тієї самої `/code`, яку ви вказали вище через `WORKDIR /code`.
/// tip | Порада
Перегляньте, що робить кожен рядок, клацнувши на кожну номерну позначку в коді. 👆
///
/// warning | Попередження
Обов’язково завжди використовуйте exec form інструкції `CMD`, як пояснено нижче.
///
#### Використовуйте `CMD` - exec form { #use-cmd-exec-form }
Інструкцію Docker [`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) можна записати у двох формах:
✅ Exec form:
```Dockerfile
# ✅ Робіть так
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
```
⛔️ Shell form:
```Dockerfile
# ⛔️ Не робіть так
CMD fastapi run app/main.py --port 80
```
Обов’язково завжди використовуйте exec form, щоб FastAPI міг коректно завершувати роботу та щоб були викликані [події тривалості життя](../advanced/events.md).
Докладніше про це можна прочитати в [документації Docker про shell та exec form](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form).
Це може бути особливо помітно при використанні `docker compose`. Див. розділ FAQ Docker Compose для технічних деталей: [Чому мої сервіси потребують 10 секунд, щоб пересотворитися або зупинитися?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop).
#### Структура директорій { #directory-structure }
Зараз у вас має бути така структура директорій:
```
.
├── app
│ ├── __init__.py
│ └── main.py
├── Dockerfile
└── requirements.txt
```
#### За представником з термінацією TLS { #behind-a-tls-termination-proxy }
Якщо ви запускаєте контейнер за представником з термінацією TLS (балансувальником навантаження), наприклад Nginx або Traefik, додайте опцію `--proxy-headers`. Це скаже Uvicorn (через CLI FastAPI) довіряти заголовкам, що надсилаються цим представником, які вказують, що застосунок працює за HTTPS тощо.
```Dockerfile
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
```
#### Кеш Docker { #docker-cache }
У цьому `Dockerfile` є важливий трюк: спочатку ми копіюємо лише файл із залежностями, а не решту коду. Ось чому.
```Dockerfile
COPY ./requirements.txt /code/requirements.txt
```
Docker та інші інструменти збирають ці образи контейнерів інкрементально, додаючи один шар поверх іншого, починаючи з верхньої частини `Dockerfile` і додаючи будь-які файли, створені кожною інструкцією в `Dockerfile`.
Docker та подібні інструменти також використовують внутрішній кеш під час збірки образу. Якщо файл не змінювався з моменту останньої збірки, тоді він повторно використає той самий шар, створений востаннє, замість копіювання файлу знову та створення нового шару з нуля.
Просте уникнення копіювання файлів не обов’язково суттєво покращує ситуацію, але оскільки для цього кроку використано кеш, він може використати кеш і для наступного кроку. Наприклад, він може використати кеш для інструкції, яка встановлює залежності:
```Dockerfile
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
```
Файл із вимогами до пакетів змінюватиметься нечасто. Отже, копіюючи лише цей файл, Docker зможе використати кеш для цього кроку.
А потім Docker зможе використати кеш і для наступного кроку, який завантажує та встановлює ці залежності. І саме тут ми заощаджуємо багато часу. ✨ ...і уникаємо нудного очікування. 😪😆
Завантаження і встановлення залежностей пакетів може займати хвилини, але використання кешу займе максимум секунди.
І оскільки ви збиратимете образ контейнера знову і знову під час розробки, щоб перевіряти, що зміни у вашому коді працюють, це заощадить багато накопиченого часу.
Потім, ближче до кінця `Dockerfile`, ми копіюємо весь код. Оскільки це те, що змінюється найчастіше, ми розміщуємо це ближче до кінця, адже майже завжди все після цього кроку не зможе використати кеш.
```Dockerfile
COPY ./app /code/app
```
### Зберіть Docker-образ { #build-the-docker-image }
Тепер, коли всі файли на місці, зберімо образ контейнера.
* Перейдіть у директорію проєкту (де ваш `Dockerfile`, який містить директорію `app`).
* Зберіть ваш образ FastAPI:
/// tip | Порада
Зверніть увагу на `.` в кінці. Це еквівалент `./`. Воно каже Docker, яку директорію використовувати для збірки образу контейнера.
У цьому випадку - це поточна директорія (`.`).
///
### Запустіть Docker-контейнер { #start-the-docker-container }
* Запустіть контейнер на основі вашого образу:
## Перевірте { #check-it }
Ви маєте змогу перевірити це за URL вашого Docker-контейнера, наприклад: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) або [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (або еквівалент, використовуючи ваш Docker-хост).
Ви побачите щось таке:
```JSON
{"item_id": 5, "q": "somequery"}
```
## Інтерактивна документація API { #interactive-api-docs }
Тепер ви можете перейти на [http://192.168.99.100/docs](http://192.168.99.100/docs) або [http://127.0.0.1/docs](http://127.0.0.1/docs) (або еквівалент, використовуючи ваш Docker-хост).
Ви побачите автоматичну інтерактивну документацію API (надається [Swagger UI](https://github.com/swagger-api/swagger-ui)):

## Альтернативна документація API { #alternative-api-docs }
Також ви можете перейти на [http://192.168.99.100/redoc](http://192.168.99.100/redoc) або [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (або еквівалент, використовуючи ваш Docker-хост).
Ви побачите альтернативну автоматичну документацію (надається [ReDoc](https://github.com/Rebilly/ReDoc)):

## Збірка Docker-образу з FastAPI в одному файлі { #build-a-docker-image-with-a-single-file-fastapi }
Якщо ваш FastAPI - це один файл, наприклад `main.py` без директорії `./app`, структура файлів може виглядати так:
```
.
├── Dockerfile
├── main.py
└── requirements.txt
```
Тоді вам потрібно лише змінити відповідні шляхи для копіювання файлу всередині `Dockerfile`:
```{ .dockerfile .annotate hl_lines="10 13" }
FROM python:3.14
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 ["fastapi", "run", "main.py", "--port", "80"]
```
1. Скопіюйте файл `main.py` безпосередньо у директорію `/code` (без будь-якої директорії `./app`).
2. Використовуйте `fastapi run`, щоб обслуговувати ваш застосунок з одного файлу `main.py`.
Коли ви передаєте файл у `fastapi run`, воно автоматично визначить, що це один файл, а не частина пакета, і знатиме, як імпортувати його та запустити ваш застосунок FastAPI. 😎
## Концепції розгортання { #deployment-concepts }
Поговорімо знову про деякі з тих самих [Концепцій розгортання](concepts.md) у термінах контейнерів.
Контейнери - це переважно інструмент для спрощення процесу збирання та розгортання застосунку, але вони не нав’язують конкретний підхід до обробки цих концепцій розгортання, і існує кілька можливих стратегій.
Гарна новина полягає в тому, що для кожної стратегії є спосіб покрити всі концепції розгортання. 🎉
Розгляньмо ці концепції розгортання в контексті контейнерів:
* HTTPS
* Автозапуск
* Перезапуски
* Реплікація (кількість запущених процесів)
* Пам’ять
* Попередні кроки перед запуском
## HTTPS { #https }
Якщо зосередитись лише на образі контейнера для застосунку FastAPI (а згодом на запущеному контейнері), HTTPS зазвичай обробляється зовнішнім іншим інструментом.
Це може бути інший контейнер, наприклад з [Traefik](https://traefik.io/), що обробляє HTTPS і автоматичне отримання сертифікатів.
/// tip | Порада
Traefik має інтеграції з Docker, Kubernetes та іншими, тож налаштувати і сконфігурувати HTTPS для ваших контейнерів з ним дуже просто.
///
Альтернативно, HTTPS може оброблятись хмарним провайдером як один з їхніх сервісів (при цьому застосунок усе ще працює в контейнері).
## Автозапуск і перезапуски { #running-on-startup-and-restarts }
Зазвичай інший інструмент відповідає за запуск і виконання вашого контейнера.
Це може бути безпосередньо Docker, Docker Compose, Kubernetes, хмарний сервіс тощо.
У більшості (або всіх) випадків є проста опція, щоб увімкнути запуск контейнера при старті системи та перезапуски у разі збоїв. Наприклад, у Docker це опція командного рядка `--restart`.
Без використання контейнерів змусити застосунки запускатися при старті системи та з перезапусками може бути клопітно і складно. Але під час роботи з контейнерами у більшості випадків ця функціональність вбудована за замовчуванням. ✨
## Реплікація - кількість процесів { #replication-number-of-processes }
Якщо у вас є кластер машин із Kubernetes, Docker Swarm Mode, Nomad або іншою подібною складною системою для керування розподіленими контейнерами на кількох машинах, тоді ви, ймовірно, захочете обробляти реплікацію на рівні кластера замість використання менеджера процесів (як-от Uvicorn з працівниками) у кожному контейнері.
Одна з таких розподілених систем керування контейнерами, як-от Kubernetes, зазвичай має інтегровані способи обробляти реплікацію контейнерів, підтримуючи водночас балансування навантаження для вхідних запитів. Усе це - на рівні кластера.
У таких випадках ви, ймовірно, захочете зібрати Docker-образ з нуля, як [пояснено вище](#dockerfile), встановивши ваші залежності і запустивши один процес Uvicorn замість використання кількох працівників Uvicorn.
### Балансувальник навантаження { #load-balancer }
При використанні контейнерів зазвичай є якийсь компонент, що слухає на головному порту. Це може бути інший контейнер, який також є представником з термінацією TLS для обробки HTTPS, або подібний інструмент.
Оскільки цей компонент приймає навантаження запитів і розподіляє його між працівниками (сподіваємось) збалансовано, його також часто називають балансувальником навантаження.
/// tip | Порада
Той самий компонент представника з термінацією TLS, що використовується для HTTPS, швидше за все, також буде балансувальником навантаження.
///
І під час роботи з контейнерами та сама система, яку ви використовуєте для їх запуску і керування ними, вже матиме внутрішні інструменти для передавання мережевої комунікації (наприклад, HTTP-запитів) від цього балансувальника навантаження (який також може бути представником з термінацією TLS) до контейнерів із вашим застосунком.
### Один балансувальник навантаження - кілька контейнерів-працівників { #one-load-balancer-multiple-worker-containers }
Під час роботи з Kubernetes або подібними розподіленими системами керування контейнерами використання їхніх внутрішніх мережевих механізмів дозволяє єдиному балансувальнику навантаження, що слухає на головному порту, передавати комунікацію (запити) до кількох контейнерів, у яких запущено ваш застосунок.
Кожен з цих контейнерів із вашим застосунком зазвичай має лише один процес (наприклад, процес Uvicorn, що запускає ваш застосунок FastAPI). Усі вони будуть ідентичними контейнерами, які запускають те саме, але кожен зі своїм процесом, пам’яттю тощо. Таким чином ви використаєте переваги паралелізму на різних ядрах процесора або навіть на різних машинах.
А розподілена система контейнерів із балансувальником навантаження розподілятиме запити між кожним із контейнерів із вашим застосунком по черзі. Тож кожен запит може оброблятися одним із кількох реплікованих контейнерів, що запускають ваш застосунок.
І зазвичай цей балансувальник навантаження зможе обробляти запити, які йдуть до інших застосунків у вашому кластері (наприклад, до іншого домену або під іншим префіксом шляху URL), і передаватиме комунікацію до відповідних контейнерів для того іншого застосунку, що працює у вашому кластері.
### Один процес на контейнер { #one-process-per-container }
У такому сценарії ви, ймовірно, захочете мати один (Uvicorn) процес на контейнер, адже ви вже обробляєте реплікацію на рівні кластера.
Тобто в цьому випадку ви не захочете мати кількох працівників у контейнері, наприклад через опцію командного рядка `--workers`. Ви захочете мати лише один процес Uvicorn на контейнер (але, ймовірно, кілька контейнерів).
Наявність іншого менеджера процесів всередині контейнера (як це було б із кількома працівниками) лише додасть зайвої складності, яку, найімовірніше, ви вже вирішуєте на рівні кластера.
### Контейнери з кількома процесами та особливі випадки { #containers-with-multiple-processes-and-special-cases }
Звісно, є особливі випадки, коли ви можете захотіти мати контейнер із кількома процесами-працівниками Uvicorn всередині.
У таких випадках ви можете використати опцію командного рядка `--workers`, щоб задати кількість працівників, яких потрібно запустити:
```{ .dockerfile .annotate }
FROM python:3.14
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
```
1. Тут ми використовуємо опцію командного рядка `--workers`, щоб встановити кількість працівників 4.
Ось кілька прикладів, коли це може мати сенс:
#### Простий застосунок { #a-simple-app }
Ви можете захотіти менеджер процесів у контейнері, якщо ваш застосунок достатньо простий, щоб запускати його на одному сервері, а не на кластері.
#### Docker Compose { #docker-compose }
Ви можете розгортати на одному сервері (не в кластері) за допомогою Docker Compose, тож у вас не буде простого способу керувати реплікацією контейнерів (у Docker Compose), зберігаючи спільну мережу та балансування навантаження.
Тоді ви можете захотіти мати один контейнер із менеджером процесів, що запускає кілька процесів-працівників всередині.
---
Головна думка: це не правила, викарбувані в камені, яких потрібно сліпо дотримуватися. Ви можете використати ці ідеї, щоб оцінити власний кейс і вирішити, який підхід найкращий для вашої системи, розглядаючи, як керувати такими концепціями:
* Безпека - HTTPS
* Автозапуск
* Перезапуски
* Реплікація (кількість запущених процесів)
* Пам’ять
* Попередні кроки перед запуском
## Пам’ять { #memory }
Якщо ви запускаєте один процес на контейнер, ви матимете більш-менш чітко визначений, стабільний і обмежений обсяг пам’яті, що споживається кожним із цих контейнерів (їх може бути більше одного, якщо вони репліковані).
Потім ви можете встановити ті самі ліміти та вимоги до пам’яті у ваших конфігураціях для системи керування контейнерами (наприклад, у Kubernetes). Таким чином вона зможе реплікувати контейнери на доступних машинах, враховуючи обсяг пам’яті, потрібний їм, і обсяг доступної пам’яті на машинах у кластері.
Якщо ваш застосунок простий, імовірно, це не буде проблемою, і вам може не знадобитися задавати жорсткі ліміти пам’яті. Але якщо ви використовуєте багато пам’яті (наприклад, із моделями машинного навчання), вам слід перевірити, скільки пам’яті ви споживаєте, і відкоригувати кількість контейнерів, що запускаються на кожній машині (і, можливо, додати більше машин у ваш кластер).
Якщо ви запускаєте кілька процесів на контейнер, вам потрібно переконатися, що кількість запущених процесів не споживає більше пам’яті, ніж доступно.
## Попередні кроки перед запуском і контейнери { #previous-steps-before-starting-and-containers }
Якщо ви використовуєте контейнери (наприклад, Docker, Kubernetes), то є два основні підходи.
### Кілька контейнерів { #multiple-containers }
Якщо у вас кілька контейнерів, імовірно кожен запускає один процес (наприклад, у кластері Kubernetes), тоді ви, ймовірно, захочете мати окремий контейнер, який виконає попередні кроки в одному контейнері, запустивши один процес, перед запуском реплікованих контейнерів-працівників.
/// info | Інформація
Якщо ви використовуєте Kubernetes, це, ймовірно, буде [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/).
///
Якщо у вашому випадку немає проблеми запускати ці попередні кроки кілька разів паралельно (наприклад, якщо ви не виконуєте міграції бази даних, а лише перевіряєте, чи база вже готова), тоді ви також можете просто помістити їх у кожен контейнер безпосередньо перед запуском головного процесу.
### Один контейнер { #single-container }
Якщо у вас просте налаштування з одним контейнером, який потім запускає кілька процесів-працівників (або теж лише один процес), тоді ви можете виконати ці попередні кроки в тому ж контейнері безпосередньо перед запуском процесу із застосунком.
### Базовий образ Docker { #base-docker-image }
Колись існував офіційний образ Docker для FastAPI: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker). Але зараз він застарілий. ⛔️
Ймовірно, вам не слід використовувати цей базовий образ Docker (або будь-який інший подібний).
Якщо ви використовуєте Kubernetes (або інші) і вже налаштовуєте реплікацію на рівні кластера з кількома контейнерами. У таких випадках краще зібрати образ з нуля, як описано вище: [Зібрати Docker-образ для FastAPI](#build-a-docker-image-for-fastapi).
А якщо вам потрібно мати кілька працівників, ви можете просто використати опцію командного рядка `--workers`.
/// note | Технічні деталі
Цей образ Docker було створено тоді, коли Uvicorn не підтримував керування та перезапуск «мертвих» працівників, тому потрібно було використовувати Gunicorn з Uvicorn, що додавало чимало складності лише для того, щоб Gunicorn керував та перезапускав процеси-працівники Uvicorn.
Але тепер, коли Uvicorn (і команда `fastapi`) підтримують використання `--workers`, немає причин використовувати базовий образ Docker замість того, щоб зібрати власний (це приблизно та сама кількість коду 😅).
///
## Розгорнути образ контейнера { #deploy-the-container-image }
Після отримання образу контейнера (Docker) є кілька способів його розгорнути.
Наприклад:
* З Docker Compose на одному сервері
* З кластером Kubernetes
* З кластером Docker Swarm Mode
* З іншим інструментом, як-от Nomad
* З хмарним сервісом, який бере ваш образ контейнера і розгортає його
## Образ Docker з `uv` { #docker-image-with-uv }
Якщо ви використовуєте [uv](https://github.com/astral-sh/uv) для встановлення та керування вашим проєктом, ви можете скористатися їхнім [посібником Docker для uv](https://docs.astral.sh/uv/guides/integration/docker/).
## Підсумок { #recap }
Використовуючи системи контейнерів (наприклад, з Docker і Kubernetes), досить просто обробляти всі концепції розгортання:
* HTTPS
* Автозапуск
* Перезапуски
* Реплікація (кількість запущених процесів)
* Пам’ять
* Попередні кроки перед запуском
У більшості випадків ви, ймовірно, не захочете використовувати будь-який базовий образ, а натомість зібрати образ контейнера з нуля на основі офіційного образу Python для Docker.
Дотримуючись порядку інструкцій у `Dockerfile` і використовуючи кеш Docker, ви можете мінімізувати час збірки, щоб максимізувати свою продуктивність (і уникнути нудьги). 😎
================================================
FILE: docs/uk/docs/deployment/fastapicloud.md
================================================
# FastAPI Cloud { #fastapi-cloud }
Ви можете розгорнути свій застосунок FastAPI на [FastAPI Cloud](https://fastapicloud.com) **однією командою**, приєднуйтесь до списку очікування, якщо ще ні. 🚀
## Вхід { #login }
Переконайтеся, що у вас вже є обліковий запис **FastAPI Cloud** (ми запросили вас зі списку очікування 😉).
Потім увійдіть:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## Розгортання { #deploy }
Тепер розгорніть свій застосунок **однією командою**:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Ось і все! Тепер ви можете отримати доступ до свого застосунку за цим URL. ✨
## Про FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** створено тим самим автором і командою, що стоїть за **FastAPI**.
Він спрощує процес **створення**, **розгортання** та **доступу** до API з мінімальними зусиллями.
Він переносить той самий **досвід розробника**, що і під час створення застосунків з FastAPI, на їх **розгортання** у хмарі. 🎉
Він також подбає про більшість речей, які вам потрібні під час розгортання застосунку, як-от:
- HTTPS
- реплікація з автомасштабуванням на основі запитів
- тощо
FastAPI Cloud - основний спонсор і джерело фінансування для відкритих проєктів *«FastAPI та друзі»*. ✨
## Розгортання в інших хмарних провайдерів { #deploy-to-other-cloud-providers }
FastAPI є відкритим кодом і базується на стандартах. Ви можете розгортати застосунки FastAPI в будь-якого хмарного провайдера за вашим вибором.
Дотримуйтесь інструкцій вашого хмарного провайдера, щоб розгортати застосунки FastAPI у них. 🤓
## Розгортання на вашому сервері { #deploy-your-own-server }
Пізніше в цьому розділі **Розгортання** я також навчу вас усім деталям, щоб ви розуміли, що відбувається, що потрібно зробити і як розгортати застосунки FastAPI самостійно, зокрема на власних серверах. 🤓
================================================
FILE: docs/uk/docs/deployment/https.md
================================================
# Про HTTPS { #about-https }
Легко припустити, що HTTPS - це щось, що просто «увімкнено» або ні.
Але все значно складніше.
/// tip | Порада
Якщо ви поспішаєте або це неважливо для вас, переходьте до наступних розділів для покрокових інструкцій із налаштування всього різними техніками.
///
Щоб **вивчити основи HTTPS** з точки зору споживача, перегляньте [https://howhttps.works/](https://howhttps.works/).
Тепер, з **точки зору розробника**, ось кілька речей, які варто пам'ятати, розмірковуючи про HTTPS:
* Для HTTPS **сервер** має **мати «сертифікати»**, видані **третьою стороною**.
* Насправді ці сертифікати **«отримуються»** у третьої сторони, а не **«генеруються»**.
* Сертифікати мають **строк дії**.
* Їхній строк дії **спливає**.
* І тоді їх потрібно **поновити**, **знову отримавши** у третьої сторони.
* Шифрування з'єднання відбувається на **рівні TCP**.
* Це один шар **нижче від HTTP**.
* Тож **обробка сертифіката та шифрування** виконується **до HTTP**.
* **TCP не знає про «домени»**. Лише про IP-адреси.
* Інформація про **конкретний домен**, який запитується, міститься в **даних HTTP**.
* **Сертифікати HTTPS** «засвідчують» **певний домен**, але протокол і шифрування працюють на рівні TCP, **до того як відомо**, з яким доменом маємо справу.
* **Типово**, це означало б, що на одну IP-адресу можна мати **лише один сертифікат HTTPS**.
* Неважливо, наскільки великий ваш сервер або наскільки малий кожен застосунок на ньому.
* Однак для цього є **рішення**.
* Є **розширення** протоколу **TLS** (який обробляє шифрування на рівні TCP, до HTTP), що називається **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)**.
* Це розширення SNI дозволяє одному серверу (з **однією IP-адресою**) мати **кілька сертифікатів HTTPS** і обслуговувати **кілька доменів/застосунків через HTTPS**.
* Щоб це працювало, один-єдиний компонент (програма), що працює на сервері та слухає **публічну IP-адресу**, має мати **всі сертифікати HTTPS** на сервері.
* **Після** отримання захищеного з'єднання протокол обміну **залишається HTTP**.
* Вміст **зашифровано**, хоча він надсилається з використанням **протоколу HTTP**.
Поширена практика мати **одну програму/HTTP-сервер**, що працює на сервері (машині, хості тощо) і **керує всіма частинами HTTPS**: приймає **зашифровані HTTPS-запити**, надсилає **розшифровані HTTP-запити** до фактичного HTTP-застосунку, що працює на тому ж сервері (у нашому випадку застосунок **FastAPI**), отримує **HTTP-відповідь** від застосунку, **шифрує її** за допомогою відповідного **сертифіката HTTPS** і надсилає її назад клієнту через **HTTPS**. Такий сервер часто називають **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)**.
Деякі варіанти, які ви можете використати як TLS Termination Proxy:
* Traefik (також може виконувати поновлення сертифікатів)
* Caddy (також може виконувати поновлення сертифікатів)
* Nginx
* HAProxy
## Let's Encrypt { #lets-encrypt }
До Let's Encrypt ці **сертифікати HTTPS** продавалися довіреними третіми сторонами.
Процес отримання одного з таких сертифікатів був громіздким, вимагав чимало паперової роботи, а самі сертифікати були доволі дорогими.
Але потім з'явився проєкт **[Let's Encrypt](https://letsencrypt.org/)**.
Це проєкт Linux Foundation. Він надає **сертифікати HTTPS безкоштовно**, в автоматизований спосіб. Ці сертифікати використовують усі стандартні криптографічні механізми безпеки і є короткостроковими (близько 3 місяців), тож **безпека насправді краща** завдяки зменшеній тривалості життя.
Домени безпечно перевіряються, а сертифікати генеруються автоматично. Це також дозволяє автоматизувати поновлення цих сертифікатів.
Ідея полягає в автоматизації отримання та поновлення цих сертифікатів, щоб ви могли мати **безпечний HTTPS, безкоштовно і назавжди**.
## HTTPS для розробників { #https-for-developers }
Ось приклад того, як може виглядати HTTPS API, крок за кроком, зосереджуючись переважно на ідеях, важливих для розробників.
### Доменне ім'я { #domain-name }
Ймовірно, все почнеться з того, що ви **придбаєте** якесь **доменне ім'я**. Потім ви налаштуєте його на сервері DNS (можливо, у вашого ж хмарного провайдера).
Ви, скоріш за все, отримаєте хмарний сервер (віртуальну машину) або щось подібне, і він матиме фіксовану **публічну IP-адресу**.
На сервері(ах) DNS ви налаштуєте запис («`A record`»), щоб спрямувати **ваш домен** на публічну **IP-адресу вашого сервера**.
Ймовірно, ви зробите це лише один раз, уперше, коли все налаштовуватимете.
/// tip | Порада
Частина про доменне ім'я значно раніше за HTTPS, але оскільки все залежить від домену та IP-адреси, варто згадати це тут.
///
### DNS { #dns }
Тепер зосередьмося на всіх власне частинах HTTPS.
Спочатку браузер звернеться до **DNS-серверів**, щоб дізнатися, яка **IP-адреса для домену**, у цьому випадку `someapp.example.com`.
Сервери DNS повідомлять браузеру використати конкретну **IP-адресу**. Це буде публічна IP-адреса, яку використовує ваш сервер і яку ви налаштували на серверах DNS.
### Початок TLS рукостискання { #tls-handshake-start }
Потім браузер зв'яжеться з цією IP-адресою на **порту 443** (порт HTTPS).
Перша частина комунікації - це просто встановлення з'єднання між клієнтом і сервером та узгодження криптографічних ключів тощо.
Ця взаємодія між клієнтом і сервером для встановлення з'єднання TLS називається **TLS рукостисканням**.
### TLS із розширенням SNI { #tls-with-sni-extension }
**Лише один процес** на сервері може слухати конкретний **порт** на конкретній **IP-адресі**. Інші процеси можуть слухати інші порти на тій самій IP-адресі, але лише один для кожної комбінації IP-адреси та порту.
TLS (HTTPS) за замовчуванням використовує конкретний порт `443`. Отже, це порт, який нам потрібен.
Оскільки лише один процес може слухати цей порт, процесом, що робитиме це, буде **TLS Termination Proxy**.
TLS Termination Proxy матиме доступ до одного або кількох **сертифікатів TLS** (сертифікатів HTTPS).
Використовуючи **розширення SNI**, обговорене вище, TLS Termination Proxy перевірить, який із наявних сертифікатів TLS (HTTPS) слід використати для цього з'єднання, обравши той, що відповідає домену, очікуваному клієнтом.
У цьому випадку він використає сертифікат для `someapp.example.com`.
Клієнт уже **довіряє** сутності, яка видала цей сертифікат TLS (у цьому випадку Let's Encrypt, але про це згодом), тож він може **перевірити**, що сертифікат дійсний.
Потім, використовуючи сертифікат, клієнт і TLS Termination Proxy **вирішать, як шифрувати** решту **TCP-комунікації**. На цьому частина **TLS рукостискання** завершується.
Після цього клієнт і сервер мають **зашифроване TCP-з'єднання** - саме це надає TLS. І тоді вони можуть використати це з'єднання, щоб почати власне **HTTP-комунікацію**.
І це і є **HTTPS**: це звичайний **HTTP** усередині **захищеного TLS-з'єднання** замість чистого (незашифрованого) TCP-з'єднання.
/// tip | Порада
Зверніть увагу, що шифрування комунікації відбувається на **рівні TCP**, а не на рівні HTTP.
///
### HTTPS-запит { #https-request }
Тепер, коли клієнт і сервер (конкретно браузер і TLS Termination Proxy) мають **зашифроване TCP-з'єднання**, вони можуть почати **HTTP-комунікацію**.
Отже, клієнт надсилає **HTTPS-запит**. Це просто HTTP-запит через зашифроване TLS-з'єднання.
### Розшифрування запиту { #decrypt-the-request }
TLS Termination Proxy використає узгоджене шифрування, щоб **розшифрувати запит**, і передасть **звичайний (розшифрований) HTTP-запит** процесу, що запускає застосунок (наприклад, процесу з Uvicorn, який запускає застосунок FastAPI).
### HTTP-відповідь { #http-response }
Застосунок обробить запит і надішле **звичайну (незашифровану) HTTP-відповідь** TLS Termination Proxy.
### HTTPS-відповідь { #https-response }
Потім TLS Termination Proxy **зашифрує відповідь**, використовуючи попередньо узгоджену криптографію (що почалася із сертифіката для `someapp.example.com`), і надішле її назад у браузер.
Далі браузер перевірить, що відповідь дійсна й зашифрована правильним криптографічним ключем тощо. Потім він **розшифрує відповідь** і обробить її.
Клієнт (браузер) знатиме, що відповідь надходить від правильного сервера, тому що використовується узгоджена раніше криптографія з використанням **сертифіката HTTPS**.
### Кілька застосунків { #multiple-applications }
На тому самому сервері (або серверах) може бути **кілька застосунків**, наприклад інші програми API або база даних.
Лише один процес може обробляти конкретну IP-адресу і порт (TLS Termination Proxy у нашому прикладі), але інші застосунки/процеси також можуть працювати на сервері(ах), доки вони не намагаються використати ту саму **комбінацію публічної IP-адреси й порту**.
Таким чином, TLS Termination Proxy може обробляти HTTPS і сертифікати для **кількох доменів**, для кількох застосунків, а потім передавати запити до відповідного застосунку в кожному випадку.
### Поновлення сертифіката { #certificate-renewal }
У певний момент у майбутньому строк дії кожного сертифіката **спливе** (приблизно через 3 місяці після його отримання).
Потім інша програма (в деяких випадках це інша програма, а в деяких - той самий TLS Termination Proxy) зв'яжеться з Let's Encrypt і поновить сертифікат(и).
**Сертифікати TLS** пов'язані **з доменним іменем**, а не з IP-адресою.
Тому, щоб поновити сертифікати, програма поновлення має **довести** авторитету (Let's Encrypt), що вона справді **«володіє» і контролює цей домен**.
Щоб зробити це й задовольнити різні потреби застосунків, є кілька способів. Деякі популярні:
* **Змінити деякі записи DNS**.
* Для цього програма поновлення має підтримувати API провайдера DNS, тож залежно від того, якого провайдера DNS ви використовуєте, це може бути або не бути варіантом.
* **Запуститися як сервер** (принаймні під час процесу отримання сертифіката) на публічній IP-адресі, пов'язаній із доменом.
* Як ми казали вище, лише один процес може слухати конкретну IP-адресу та порт.
* Це одна з причин, чому дуже зручно, коли той самий TLS Termination Proxy також займається процесом поновлення сертифікатів.
* Інакше вам, можливо, доведеться на мить зупинити TLS Termination Proxy, запустити програму поновлення, щоб отримати сертифікати, потім налаштувати їх у TLS Termination Proxy і перезапустити TLS Termination Proxy. Це неідеально, оскільки ваші застосунки будуть недоступні під час вимкнення TLS Termination Proxy.
Увесь цей процес поновлення, паралельно з обслуговуванням застосунку, - одна з головних причин, чому ви можете захотіти мати **окрему систему для обробки HTTPS** за допомогою TLS Termination Proxy замість того, щоб просто використовувати сертифікати TLS безпосередньо з сервером застосунку (наприклад, Uvicorn).
## Направлені заголовки проксі { #proxy-forwarded-headers }
Коли ви використовуєте проксі для обробки HTTPS, ваш **сервер застосунку** (наприклад, Uvicorn через FastAPI CLI) нічого не знає про процес HTTPS, він спілкується звичайним HTTP із **TLS Termination Proxy**.
Цей **проксі** зазвичай динамічно встановлює деякі HTTP-заголовки перед передачею запиту **серверу застосунку**, щоб дати йому знати, що запит **направляється** проксі.
/// note | Технічні деталі
Заголовки проксі:
* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For)
* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto)
* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host)
///
Втім, оскільки **сервер застосунку** не знає, що він стоїть за довіреним **проксі**, за замовчуванням він не довірятиме цим заголовкам.
Але ви можете налаштувати **сервер застосунку**, щоб довіряти направленим заголовкам, надісланим **проксі**. Якщо ви використовуєте FastAPI CLI, ви можете скористатися курсивною *опцією CLI* `--forwarded-allow-ips`, щоб повідомити, з яких IP-адрес слід довіряти цим направленим заголовкам.
Наприклад, якщо **сервер застосунку** отримує комунікацію лише від довіреного **проксі**, ви можете встановити `--forwarded-allow-ips="*"`, щоб довіряти всім вхідним IP-адресам, оскільки він отримуватиме запити лише з тієї IP-адреси, яку використовує **проксі**.
Так застосунок зможе знати свою публічну URL-адресу, чи використовує він HTTPS, домен тощо.
Це буде корисно, наприклад, для коректної обробки перенаправлень.
/// tip | Порада
Ви можете дізнатися більше про це в документації [За проксі - Увімкнути направлені заголовки проксі](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers)
///
## Підсумок { #recap }
Наявність **HTTPS** дуже важлива і в більшості випадків **критична**. Більшість зусиль, які вам як розробнику доведеться докласти навколо HTTPS, полягають лише в **розумінні цих концепцій** і того, як вони працюють.
Але як тільки ви знаєте базову інформацію про **HTTPS для розробників**, ви можете легко комбінувати й налаштовувати різні інструменти, щоб керувати всім просто.
У деяких наступних розділах я покажу кілька конкретних прикладів налаштування **HTTPS** для застосунків **FastAPI**. 🔒
================================================
FILE: docs/uk/docs/deployment/index.md
================================================
# Розгортання { #deployment }
Розгортання застосунку **FastAPI** відносно просте.
## Що означає розгортання { #what-does-deployment-mean }
Розгорнути застосунок - це виконати необхідні кроки, щоб зробити його доступним для користувачів.
Для **веб API** це зазвичай означає розміщення його на **віддаленій машині** з **серверною програмою**, що забезпечує хорошу продуктивність, стабільність тощо, щоб ваші **користувачі** могли **отримувати доступ** до застосунку ефективно та без перерв чи проблем.
Це відрізняється від етапів **розробки**, коли ви постійно змінюєте код, ламаєте й виправляєте його, зупиняєте та перезапускаєте сервер розробки тощо.
## Стратегії розгортання { #deployment-strategies }
Існує кілька способів зробити це залежно від вашого конкретного випадку використання та інструментів, які ви використовуєте.
Ви можете розгорнути сервер самостійно, використовуючи комбінацію інструментів, можете скористатися **хмарним сервісом**, який виконує частину роботи за вас, або обрати інші варіанти.
Наприклад, ми, команда, що стоїть за FastAPI, створили [**FastAPI Cloud**](https://fastapicloud.com), щоб зробити розгортання застосунків FastAPI у хмарі якомога простішим і з тим самим досвідом розробки, що й під час роботи з FastAPI.
Я покажу вам кілька основних концепцій, про які, ймовірно, варто пам'ятати під час розгортання **FastAPI**-застосунку (хоча більшість із них стосується будь-яких інших типів веб-застосунків).
У наступних розділах ви побачите більше деталей, на які варто зважати, та деякі техніки, як це зробити. ✨
================================================
FILE: docs/uk/docs/deployment/manually.md
================================================
# Запустіть сервер вручну { #run-a-server-manually }
## Використовуйте команду `fastapi run` { #use-the-fastapi-run-command }
Коротко: використовуйте `fastapi run`, щоб запустити ваш застосунок FastAPI:
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
Це спрацює в більшості випадків. 😎
Цю команду можна використати, наприклад, щоб запустити ваш застосунок FastAPI у контейнері, на сервері тощо.
## Сервери ASGI { #asgi-servers }
Розгляньмо деталі.
FastAPI використовує стандарт для побудови Python вебфреймворків і серверів під назвою ASGI. FastAPI - це ASGI вебфреймворк.
Головне, що потрібно, щоб запустити застосунок **FastAPI** (або будь-який інший ASGI-застосунок) на віддаленій серверній машині, - це програма ASGI-сервера на кшталт **Uvicorn**; саме вона постачається за замовчуванням у команді `fastapi`.
Є кілька альтернатив, зокрема:
* [Uvicorn](https://www.uvicorn.dev/): високопродуктивний ASGI-сервер.
* [Hypercorn](https://hypercorn.readthedocs.io/): ASGI-сервер, сумісний з HTTP/2 і Trio, серед інших можливостей.
* [Daphne](https://github.com/django/daphne): ASGI-сервер, створений для Django Channels.
* [Granian](https://github.com/emmett-framework/granian): Rust HTTP-сервер для Python-застосунків.
* [NGINX Unit](https://unit.nginx.org/howto/fastapi/): NGINX Unit - легке й універсальне середовище виконання вебзастосунків.
## Серверна машина і серверна програма { #server-machine-and-server-program }
Є невелика деталь щодо назв, яку варто пам'ятати. 💡
Слово «**сервер**» зазвичай означає і віддалений/хмарний комп'ютер (фізична або віртуальна машина), і програму, що працює на цій машині (наприклад, Uvicorn).
Майте на увазі, що коли ви бачите слово «сервер» загалом, воно може стосуватися будь-якого з цих двох значень.
Коли йдеться про віддалену машину, її часто називають «сервер», а також «машина», «VM» (віртуальна машина), «вузол». Усе це означає різновиди віддаленої машини, зазвичай з Linux, на якій ви запускаєте програми.
## Встановіть серверну програму { #install-the-server-program }
Після встановлення FastAPI ви отримуєте продакшн-сервер Uvicorn і можете запускати його командою `fastapi run`.
Але ви також можете встановити ASGI-сервер вручну.
Переконайтеся, що ви створили [віртуальне оточення](../virtual-environments.md), активували його, після чого можете встановити серверну програму.
Наприклад, щоб установити Uvicorn:
Подібний процес застосовується до будь-якої іншої ASGI-серверної програми.
/// tip | Порада
Додавши `standard`, Uvicorn встановить і використовуватиме деякі рекомендовані додаткові залежності.
Зокрема `uvloop` - високопродуктивну заміну «без змін у коді» для `asyncio`, що суттєво підвищує рівночасність і продуктивність.
Якщо ви встановлюєте FastAPI через `pip install "fastapi[standard]"`, ви вже отримаєте і `uvicorn[standard]`.
///
## Запустіть серверну програму { #run-the-server-program }
Якщо ви встановили ASGI-сервер вручну, зазвичай потрібно передати рядок імпорту в спеціальному форматі, щоб він імпортував ваш застосунок FastAPI:
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
/// note | Примітка
Команда `uvicorn main:app` означає:
* `main`: файл `main.py` (Python «модуль»).
* `app`: об'єкт, створений у `main.py` рядком `app = FastAPI()`.
Це еквівалентно:
```Python
from main import app
```
///
Кожна альтернативна ASGI-серверна програма матиме подібну команду; читайте більше в їхній документації.
/// warning | Попередження
Uvicorn та інші сервери підтримують опцію `--reload`, корисну під час розробки.
Опція `--reload` споживає значно більше ресурсів, є менш стабільною тощо.
Вона дуже допомагає під час **розробки**, але її **не слід** використовувати в **продакшні**.
///
## Концепції розгортання { #deployment-concepts }
Ці приклади запускають серверну програму (наприклад, Uvicorn), піднімаючи один процес, що слухає всі IP (`0.0.0.0`) на визначеному порту (наприклад, `80`).
Це базова ідея. Але, ймовірно, вам знадобиться подбати ще про таке:
* Безпека - HTTPS
* Автозапуск
* Перезапуски
* Реплікація (кількість запущених процесів)
* Пам'ять
* Попередні кроки перед стартом
У наступних розділах я розповім більше про кожну з цих концепцій, як про них думати, і наведу конкретні приклади та стратегії для їх опрацювання. 🚀
================================================
FILE: docs/uk/docs/deployment/server-workers.md
================================================
# Працівники сервера - Uvicorn з працівниками { #server-workers-uvicorn-with-workers }
Повернімося до попередніх концепцій розгортання:
- Безпека - HTTPS
- Запуск під час старту
- Перезапуски
- **Реплікація (кількість процесів, що виконуються)**
- Пам'ять
- Попередні кроки перед запуском
До цього моменту, проходячи всі навчальні посібники в документації, ви, ймовірно, запускали серверну програму, наприклад, використовуючи команду `fastapi`, яка запускає Uvicorn у вигляді одного процесу.
Під час розгортання застосунків ви, найімовірніше, захочете мати реплікацію процесів, щоб використовувати кілька ядер і обробляти більше запитів.
Як ви бачили в попередньому розділі про [Концепції розгортання](concepts.md), існує кілька стратегій, які можна використовувати.
Тут я покажу, як використовувати Uvicorn із процесами-працівниками за допомогою команди `fastapi` або безпосередньо команди `uvicorn`.
/// info | Інформація
Якщо ви використовуєте контейнери, наприклад з Docker або Kubernetes, я розповім про це більше в наступному розділі: [FastAPI у контейнерах - Docker](docker.md).
Зокрема, під час запуску в Kubernetes вам, найімовірніше, не варто використовувати працівників, натомість запускати один процес Uvicorn на контейнер. Але про це я розповім пізніше в тому розділі.
///
## Кілька працівників { #multiple-workers }
Ви можете запустити кілька працівників за допомогою параметра командного рядка `--workers`:
//// tab | `fastapi`
Якщо ви використовуєте команду `fastapi`:
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
Якщо ви віддаєте перевагу використовувати команду `uvicorn` безпосередньо:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
////
Єдина нова опція тут — `--workers`, яка вказує Uvicorn запустити 4 процеси-працівники.
Також ви можете побачити, що виводиться PID кожного процесу: `27365` для батьківського процесу (це менеджер процесів) і по одному для кожного процесу-працівника: `27368`, `27369`, `27370` і `27367`.
## Концепції розгортання { #deployment-concepts }
Тут ви побачили, як використовувати кілька працівників, щоб паралелізувати виконання застосунку, використати кілька ядер процесора та обслуговувати більше запитів.
Із наведеного вище списку концепцій розгортання, використання працівників головним чином допоможе з частиною про реплікацію і трохи з перезапусками, але про інше все ще треба подбати:
- **Безпека - HTTPS**
- **Запуск під час старту**
- ***Перезапуски***
- Реплікація (кількість процесів, що виконуються)
- **Пам'ять**
- **Попередні кроки перед запуском**
## Контейнери і Docker { #containers-and-docker }
У наступному розділі про [FastAPI у контейнерах - Docker](docker.md) я поясню кілька стратегій, які ви можете використати для інших концепцій розгортання.
Я покажу, як побудувати власний образ з нуля для запуску одного процесу Uvicorn. Це простий процес і, ймовірно, саме те, що потрібно при використанні розподіленої системи керування контейнерами, такої як Kubernetes.
## Підсумок { #recap }
Ви можете використовувати кілька процесів-працівників за допомогою параметра CLI `--workers` у командах `fastapi` або `uvicorn`, щоб скористатися перевагами багатоядерних процесорів і запускати кілька процесів паралельно.
Ви можете застосувати ці інструменти та ідеї, якщо налаштовуєте власну систему розгортання і самостійно дбаєте про інші концепції розгортання.
Перегляньте наступний розділ, щоб дізнатися про FastAPI з контейнерами (наприклад Docker і Kubernetes). Ви побачите, що ці інструменти також мають прості способи вирішити інші концепції розгортання. ✨
================================================
FILE: docs/uk/docs/deployment/versions.md
================================================
# Про версії FastAPI { #about-fastapi-versions }
**FastAPI** уже використовується у продакшні в багатьох застосунках і системах. Покриття тестами підтримується на рівні 100%. Але розробка все ще рухається швидко.
Нові можливості додаються часто, помилки регулярно виправляються, а код постійно поліпшується.
Тому поточні версії все ще `0.x.x`, це відображає те, що кожна версія потенційно може містити несумісні зміни. Це відповідає правилам [Семантичного версіонування](https://semver.org/).
Ви можете створювати продакшн-застосунки з **FastAPI** вже зараз (і, ймовірно, робите це вже певний час), просто переконайтеся, що ви використовуєте версію, яка коректно працює з рештою вашого коду.
## Зафіксуйте версію `fastapi` { #pin-your-fastapi-version }
Перше, що слід зробити, - «зафіксувати» версію **FastAPI**, яку ви використовуєте, до конкретної останньої версії, що, як ви знаєте, правильно працює для вашого застосунку.
Наприклад, припустімо, що у вашому застосунку використовується версія `0.112.0`.
Якщо ви використовуєте файл `requirements.txt`, ви можете вказати версію так:
```txt
fastapi[standard]==0.112.0
```
це означатиме, що ви використовуватимете саме версію `0.112.0`.
Або ви також можете зафіксувати так:
```txt
fastapi[standard]>=0.112.0,<0.113.0
```
це означатиме, що ви використовуватимете версії `0.112.0` і вище, але менше `0.113.0`, наприклад, версія `0.112.2` все ще буде прийнятною.
Якщо ви використовуєте інший інструмент для керування встановленнями, як-от `uv`, Poetry, Pipenv чи інші, у всіх них є спосіб визначати конкретні версії для ваших пакетів.
## Доступні версії { #available-versions }
Ви можете переглянути доступні версії (наприклад, щоб перевірити поточну останню) в [Примітках до випусків](../release-notes.md).
## Про версії { #about-versions }
Відповідно до правил Семантичного версіонування, будь-яка версія нижче `1.0.0` потенційно може містити несумісні зміни.
FastAPI також дотримується правила, що будь-яка зміна «PATCH»-версії - це виправлення помилок і зміни без порушень сумісності.
/// tip | Порада
«PATCH» - це останнє число, наприклад, у `0.2.3` PATCH-версія - це `3`.
///
Тож ви можете зафіксувати версію так:
```txt
fastapi>=0.45.0,<0.46.0
```
Несумісні зміни та нові можливості додаються у «MINOR»-версіях.
/// tip | Порада
«MINOR» - це число посередині, наприклад, у `0.2.3` MINOR-версія - це `2`.
///
## Оновлення версій FastAPI { #upgrading-the-fastapi-versions }
Ви повинні додати тести для вашого застосунку.
З **FastAPI** це дуже легко (завдяки Starlette), перегляньте документацію: [Тестування](../tutorial/testing.md)
Після того як у вас є тести, ви можете оновити версію **FastAPI** до новішої і переконатися, що весь ваш код працює правильно, запустивши тести.
Якщо все працює або після внесення необхідних змін і проходження всіх тестів, зафіксуйте вашу версію `fastapi` на цій новій версії.
## Про Starlette { #about-starlette }
Не варто фіксувати версію `starlette`.
Різні версії **FastAPI** використовуватимуть конкретну новішу версію Starlette.
Отже, просто дозвольте **FastAPI** використовувати відповідну версію Starlette.
## Про Pydantic { #about-pydantic }
Pydantic включає тести для **FastAPI** разом із власними тестами, тому нові версії Pydantic (вище `1.0.0`) завжди сумісні з FastAPI.
Ви можете зафіксувати Pydantic на будь-якій версії вище `1.0.0`, яка вам підходить.
Наприклад:
```txt
pydantic>=2.7.0,<3.0.0
```
================================================
FILE: docs/uk/docs/editor-support.md
================================================
# Підтримка редакторів { #editor-support }
Офіційне [FastAPI Extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) покращує ваш робочий процес розробки FastAPI завдяки виявленню й навігації по *операціях шляху*, а також розгортанню у FastAPI Cloud і потоковому передаванню журналів у реальному часі.
Докладніше про розширення дивіться у README в [репозиторії GitHub](https://github.com/fastapi/fastapi-vscode).
## Налаштування та встановлення { #setup-and-installation }
**FastAPI Extension** доступне для [VS Code](https://code.visualstudio.com/) і [Cursor](https://www.cursor.com/). Його можна встановити безпосередньо з панелі Extensions у кожному редакторі, знайшовши «FastAPI» і вибравши розширення від **FastAPI Labs**. Розширення також працює у браузерних редакторах, таких як [vscode.dev](https://vscode.dev) і [github.dev](https://github.dev).
### Виявлення застосунку { #application-discovery }
Типово розширення автоматично виявляє застосунки FastAPI у вашому робочому просторі, скануючи файли, які створюють екземпляр `FastAPI()`. Якщо автовиявлення не працює для структури вашого проєкту, ви можете вказати точку входу через `[tool.fastapi]` у `pyproject.toml` або налаштування VS Code `fastapi.entryPoint`, використовуючи нотацію модуля (наприклад, `myapp.main:app`).
## Можливості { #features }
- **Провідник операцій шляху** - Бічне деревоподібне представлення всіх *операцій шляху* у вашому застосунку. Натисніть, щоб перейти до будь-якого визначення маршруту або маршрутизатора.
- **Пошук маршрутів** - Пошук за шляхом, методом або назвою за допомогою Ctrl + Shift + E (на macOS: Cmd + Shift + E).
- **Навігація CodeLens** - Клікабельні посилання над викликами тестового клієнта (наприклад, `client.get('/items')`), які переходять до відповідної *операції шляху* для швидкої навігації між тестами та реалізацією.
- **Розгортання у FastAPI Cloud** - Розгортання вашого застосунку у [FastAPI Cloud](https://fastapicloud.com/) в один клік.
- **Потокове передавання журналів застосунку** - Потокове передавання журналів у реальному часі з вашого застосунку, розгорнутого у FastAPI Cloud, з фільтруванням за рівнем і пошуком по тексту.
Щоб ознайомитися з можливостями розширення, відкрийте Палітру команд (Ctrl + Shift + P або на macOS: Cmd + Shift + P), виберіть «Welcome: Open walkthrough...», а потім «Get started with FastAPI».
================================================
FILE: docs/uk/docs/environment-variables.md
================================================
# Змінні оточення { #environment-variables }
/// tip | Порада
Якщо ви вже знаєте, що таке «змінні оточення» і як їх використовувати, можете пропустити цей розділ.
///
Змінна оточення (також відома як «env var») - це змінна, що існує поза кодом Python, в операційній системі, і може бути прочитана вашим кодом Python (а також іншими програмами).
Змінні оточення корисні для роботи з налаштуваннями застосунку, як частина встановлення Python тощо.
## Створення і використання змінних оточення { #create-and-use-env-vars }
Ви можете створювати і використовувати змінні оточення в оболонці (терміналі) без участі Python:
//// tab | Linux, macOS, Windows Bash
```console
// Ви можете створити змінну оточення MY_NAME командою
$ export MY_NAME="Wade Wilson"
// Потім можна використати її з іншими програмами, наприклад
$ echo "Hello $MY_NAME"
Hello Wade Wilson
```
////
//// tab | Windows PowerShell
```console
// Створіть змінну оточення MY_NAME
$ $Env:MY_NAME = "Wade Wilson"
// Використайте її з іншими програмами, наприклад
$ echo "Hello $Env:MY_NAME"
Hello Wade Wilson
```
////
## Читання змінних оточення в Python { #read-env-vars-in-python }
Ви також можете створити змінні оточення поза Python, у терміналі (або будь-яким іншим способом), а потім зчитати їх у Python.
Наприклад, у вас може бути файл `main.py` з:
```Python hl_lines="3"
import os
name = os.getenv("MY_NAME", "World")
print(f"Hello {name} from Python")
```
/// tip | Порада
Другий аргумент до [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) - це значення за замовчуванням, яке буде повернено.
Якщо його не вказано, за замовчуванням це `None`. Тут ми надаємо `"World"` як значення за замовчуванням.
///
Потім ви можете запустити цю програму Python:
//// tab | Linux, macOS, Windows Bash
```console
// Тут ми ще не встановлюємо змінну оточення
$ python main.py
// Оскільки ми не встановили змінну оточення, отримуємо значення за замовчуванням
Hello World from Python
// Але якщо спочатку створимо змінну оточення
$ export MY_NAME="Wade Wilson"
// А потім знову викличемо програму
$ python main.py
// Тепер вона може прочитати змінну оточення
Hello Wade Wilson from Python
```
////
//// tab | Windows PowerShell
```console
// Тут ми ще не встановлюємо змінну оточення
$ python main.py
// Оскільки ми не встановили змінну оточення, отримуємо значення за замовчуванням
Hello World from Python
// Але якщо спочатку створимо змінну оточення
$ $Env:MY_NAME = "Wade Wilson"
// А потім знову викличемо програму
$ python main.py
// Тепер вона може прочитати змінну оточення
Hello Wade Wilson from Python
```
////
Оскільки змінні оточення можна встановлювати поза кодом, але читати в коді, і їх не потрібно зберігати (фіксувати у `git`) разом з іншими файлами, їх часто використовують для конфігурацій або налаштувань.
Ви також можете створити змінну оточення лише для конкретного запуску програми, вона буде доступна тільки цій програмі і лише на час її виконання.
Щоб зробити це, створіть її безпосередньо перед командою запуску програми, в тому самому рядку:
```console
// Створіть змінну оточення MY_NAME безпосередньо в цьому виклику програми
$ MY_NAME="Wade Wilson" python main.py
// Тепер вона може прочитати змінну оточення
Hello Wade Wilson from Python
// Після цього змінна оточення більше не існує
$ python main.py
Hello World from Python
```
/// tip | Порада
Ви можете прочитати більше у [The Twelve-Factor App: Config](https://12factor.net/config).
///
## Типи і перевірка { #types-and-validation }
Ці змінні оточення можуть містити лише текстові строки, оскільки вони зовнішні щодо Python і мають бути сумісними з іншими програмами та рештою системи (і навіть з різними операційними системами, як-от Linux, Windows, macOS).
Це означає, що будь-яке значення, прочитане в Python зі змінної оточення, буде `str`, а будь-яке перетворення до іншого типу або будь-яка перевірка має виконуватися в коді.
Ви дізнаєтеся більше про використання змінних оточення для роботи з налаштуваннями застосунку в розділі [Просунутий посібник користувача - Налаштування і змінні оточення](./advanced/settings.md).
## Змінна оточення `PATH` { #path-environment-variable }
Є спеціальна змінна оточення `PATH`, яку використовують операційні системи (Linux, macOS, Windows) для пошуку програм для запуску.
Значення змінної `PATH` - це довга строка, що складається з каталогів, розділених двокрапкою `:` у Linux і macOS та крапкою з комою `;` у Windows.
Наприклад, змінна оточення `PATH` може виглядати так:
//// tab | Linux, macOS
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Це означає, що система має шукати програми в каталогах:
* `/usr/local/bin`
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32
```
Це означає, що система має шукати програми в каталогах:
* `C:\Program Files\Python312\Scripts`
* `C:\Program Files\Python312`
* `C:\Windows\System32`
////
Коли ви вводите команду в терміналі, операційна система шукає програму в кожному з тих каталогів, перелічених у змінній оточення `PATH`.
Наприклад, коли ви вводите `python` у терміналі, операційна система шукає програму з назвою `python` у першому каталозі цього списку.
Якщо знайде, вона використає її. Інакше продовжить пошук в інших каталогах.
### Встановлення Python і оновлення `PATH` { #installing-python-and-updating-the-path }
Під час встановлення Python вас можуть запитати, чи хочете ви оновити змінну оточення `PATH`.
//// tab | Linux, macOS
Припустімо, ви встановлюєте Python і він опиняється в каталозі `/opt/custompython/bin`.
Якщо ви погодитеся оновити змінну оточення `PATH`, інсталятор додасть `/opt/custompython/bin` до змінної `PATH`.
Це може виглядати так:
```plaintext
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin
```
Тепер, коли ви введете `python` у терміналі, система знайде програму Python у `/opt/custompython/bin` (останній каталог) і використає саме її.
////
//// tab | Windows
Припустімо, ви встановлюєте Python і він опиняється в каталозі `C:\opt\custompython\bin`.
Якщо ви погодитеся оновити змінну оточення `PATH`, інсталятор додасть `C:\opt\custompython\bin` до змінної `PATH`.
```plaintext
C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin
```
Тепер, коли ви введете `python` у терміналі, система знайде програму Python у `C:\opt\custompython\bin` (останній каталог) і використає саме її.
////
Отже, якщо ви введете:
```console
$ python
```
//// tab | Linux, macOS
Система знайде програму `python` у `/opt/custompython/bin` і запустить її.
Це приблизно еквівалентно введенню:
```console
$ /opt/custompython/bin/python
```
////
//// tab | Windows
Система знайде програму `python` у `C:\opt\custompython\bin\python` і запустить її.
Це приблизно еквівалентно введенню:
```console
$ C:\opt\custompython\bin\python
```
////
Ця інформація стане у пригоді під час вивчення [Віртуальних середовищ](virtual-environments.md).
## Висновок { #conclusion }
Тепер ви маєте базове розуміння того, що таке змінні оточення і як їх використовувати в Python.
Також можна прочитати більше у [Вікіпедії про змінну оточення](https://en.wikipedia.org/wiki/Environment_variable).
У багатьох випадках не одразу очевидно, як змінні оточення будуть корисними та застосовними. Але вони постійно з’являються в різних сценаріях під час розробки, тож варто про них знати.
Наприклад, вам знадобиться ця інформація в наступному розділі про [Віртуальні середовища](virtual-environments.md).
================================================
FILE: docs/uk/docs/fastapi-cli.md
================================================
# FastAPI CLI { #fastapi-cli }
**FastAPI CLI** — це програма командного рядка, яку ви можете використовувати, щоб обслуговувати ваш застосунок FastAPI, керувати вашим проєктом FastAPI тощо.
Коли ви встановлюєте FastAPI (наприклад, за допомогою `pip install "fastapi[standard]"`), він постачається з програмою командного рядка, яку можна запускати в терміналі.
Щоб запустити ваш застосунок FastAPI для розробки, ви можете використати команду `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
/// tip | Порада
Для продакшну ви б використовували `fastapi run` замість `fastapi dev`. 🚀
///
Внутрішньо **FastAPI CLI** використовує [Uvicorn](https://www.uvicorn.dev), високопродуктивний, готовий до продакшну ASGI сервер. 😎
CLI `fastapi` спробує автоматично визначити застосунок FastAPI для запуску, припускаючи, що це об'єкт з назвою `app` у файлі `main.py` (або кілька інших варіантів).
Але ви можете явно налаштувати застосунок, який слід використовувати.
## Налаштуйте `entrypoint` застосунку в `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Ви можете налаштувати розташування вашого застосунку у файлі `pyproject.toml`, наприклад:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Цей `entrypoint` підкаже команді `fastapi`, що слід імпортувати застосунок так:
```python
from main import app
```
Якщо ваш код має таку структуру:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Тоді ви встановили б `entrypoint` як:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
що еквівалентно:
```python
from backend.main import app
```
### `fastapi dev` зі шляхом { #fastapi-dev-with-path }
Ви також можете передати шлях до файлу команді `fastapi dev`, і вона здогадається, який об'єкт застосунку FastAPI використовувати:
```console
$ fastapi dev main.py
```
Але вам доведеться щоразу пам'ятати, щоб передавати правильний шлях під час виклику команди `fastapi`.
Крім того, інші інструменти можуть не знайти його, наприклад [Розширення VS Code](editor-support.md) або [FastAPI Cloud](https://fastapicloud.com), тому рекомендується використовувати `entrypoint` у `pyproject.toml`.
## `fastapi dev` { #fastapi-dev }
Запуск `fastapi dev` ініціює режим розробки.
За замовчуванням **auto-reload** увімкнено, і сервер автоматично перезавантажується, коли ви вносите зміни у ваш код. Це ресурсоємно та може бути менш стабільним, ніж коли його вимкнено. Вам слід використовувати це лише для розробки. Також він слухає IP-адресу `127.0.0.1`, яка є IP-адресою для того, щоб ваша машина могла взаємодіяти лише сама з собою (`localhost`).
## `fastapi run` { #fastapi-run }
Виконання `fastapi run` за замовчуванням запускає FastAPI у продакшн-режимі.
За замовчуванням **auto-reload** вимкнено. Також він слухає IP-адресу `0.0.0.0`, що означає всі доступні IP-адреси, таким чином він буде публічно доступним для будь-кого, хто може взаємодіяти з машиною. Зазвичай саме так ви запускатимете його в продакшн, наприклад у контейнері.
У більшості випадків ви (і вам слід) матимете «termination proxy», який обробляє HTTPS для вас зверху; це залежатиме від того, як ви розгортаєте ваш застосунок: ваш провайдер може зробити це за вас, або вам може знадобитися налаштувати це самостійно.
/// tip | Порада
Ви можете дізнатися більше про це в [документації з розгортання](deployment/index.md).
///
================================================
FILE: docs/uk/docs/features.md
================================================
# Функціональні можливості { #features }
## Функціональні можливості FastAPI { #fastapi-features }
**FastAPI** надає вам такі можливості:
### На основі відкритих стандартів { #based-on-open-standards }
* [**OpenAPI**](https://github.com/OAI/OpenAPI-Specification) для створення API, включаючи оголошення шляхів операцій, параметрів, тіл запитів, безпеки тощо.
* Автоматична документація моделей даних за допомогою [**JSON Schema**](https://json-schema.org/) (оскільки OpenAPI базується саме на JSON Schema).
* Розроблено на основі цих стандартів після ретельного аналізу, а не як додатковий рівень поверх основної архітектури.
* Це також дає змогу використовувати автоматичну **генерацію клієнтського коду** багатьма мовами.
### Автоматична документація { #automatic-docs }
Інтерактивна документація API та вебінтерфейси для його дослідження. Оскільки фреймворк базується на OpenAPI, є кілька варіантів, 2 з яких включені за замовчуванням.
* [**Swagger UI**](https://github.com/swagger-api/swagger-ui) — з інтерактивним дослідженням, викликом і тестуванням вашого API прямо з браузера.

* Альтернативна документація API за допомогою [**ReDoc**](https://github.com/Rebilly/ReDoc).

### Лише сучасний Python { #just-modern-python }
Усе базується на стандартних оголошеннях **типів Python** (завдяки Pydantic). Жодного нового синтаксису для вивчення. Лише стандартний сучасний Python.
Якщо вам потрібно 2-хвилинне нагадування про те, як використовувати типи Python (навіть якщо ви не використовуєте FastAPI), перегляньте короткий підручник: [Типи Python](python-types.md).
Ви пишете стандартний Python з типами:
```Python
from datetime import date
from pydantic import BaseModel
# Оголосіть змінну як str
# та отримайте підтримку редактора всередині функції
def main(user_id: str):
return user_id
# Модель Pydantic
class User(BaseModel):
id: int
name: str
joined: date
```
Далі це можна використовувати так:
```Python
my_user: User = User(id=3, name="John Doe", joined="2018-07-19")
second_user_data = {
"id": 4,
"name": "Mary",
"joined": "2018-11-30",
}
my_second_user: User = User(**second_user_data)
```
/// info | Інформація
`**second_user_data` означає:
Передати ключі та значення словника `second_user_data` безпосередньо як аргументи у вигляді «ключ-значення», еквівалентно: `User(id=4, name="Mary", joined="2018-11-30")`
///
### Підтримка редакторів { #editor-support }
Увесь фреймворк спроєктовано так, щоб ним було легко та інтуїтивно користуватися; усі рішення тестувалися у кількох редакторах ще до початку розробки, щоб забезпечити найкращий досвід розробки.
З опитувань розробників Python зрозуміло [що однією з найуживаніших функцій є «автодоповнення»](https://www.jetbrains.com/research/python-developers-survey-2017/#tools-and-features).
Увесь фреймворк **FastAPI** побудований так, щоб це забезпечити. Автодоповнення працює всюди.
Вам рідко доведеться повертатися до документації.
Ось як ваш редактор може вам допомогти:
* у [Visual Studio Code](https://code.visualstudio.com/):

* у [PyCharm](https://www.jetbrains.com/pycharm/):

Ви отримаєте автодоповнення в коді, який раніше могли вважати навіть неможливим. Наприклад, для ключа `price` всередині JSON body (який міг бути вкладеним), що надходить із запиту.
Більше не доведеться вводити неправильні назви ключів, постійно повертатися до документації або прокручувати вгору-вниз, щоб знайти, чи ви зрештою використали `username` чи `user_name`.
### Короткий код { #short }
FastAPI має розумні **налаштування за замовчуванням** для всього, з можливістю конфігурації всюди. Усі параметри можна точно налаштувати під ваші потреби та визначити потрібний вам API.
Але за замовчуванням усе **«просто працює»**.
### Валідація { #validation }
* Підтримка валідації для більшості (або всіх?) **типів даних Python**, зокрема:
* JSON-об'єктів (`dict`).
* JSON-масивів (`list`) із визначенням типів елементів.
* Полів-рядків (`str`) із визначенням мінімальної та максимальної довжини.
* Чисел (`int`, `float`) з мінімальними та максимальними значеннями тощо.
* Валідація для більш екзотичних типів, як-от:
* URL.
* Email.
* UUID.
* ...та інші.
Уся валідація виконується через надійний та перевірений **Pydantic**.
### Безпека та автентифікація { #security-and-authentication }
Інтегровані безпека та автентифікація. Без жодних компромісів із базами даних чи моделями даних.
Підтримуються всі схеми безпеки, визначені в OpenAPI, включно з:
* HTTP Basic.
* **OAuth2** (також із підтримкою **JWT tokens**). Перегляньте підручник: [OAuth2 із JWT](tutorial/security/oauth2-jwt.md).
* Ключі API в:
* Заголовках.
* Параметрах запиту.
* Cookies тощо.
А також усі можливості безпеки від Starlette (зокрема **session cookies**).
Усе це зроблено як багаторазові інструменти та компоненти, які легко інтегруються з вашими системами, сховищами даних, реляційними та NoSQL базами даних тощо.
### Впровадження залежностей { #dependency-injection }
FastAPI містить надзвичайно просту у використанні, але надзвичайно потужну систему Впровадження залежностей.
* Навіть залежності можуть мати власні залежності, утворюючи ієрархію або **«граф» залежностей**.
* Усе **автоматично обробляється** фреймворком.
* Усі залежності можуть вимагати дані із запитів і **розширювати обмеження операції шляху** та автоматичну документацію.
* **Автоматична валідація** навіть для *операції шляху*, визначених у залежностях.
* Підтримка складних систем автентифікації користувачів, **підключень до баз даних** тощо.
* **Жодних компромісів** із базами даних, фронтендами тощо. Але проста інтеграція з усіма ними.
### Необмежені «плагіни» { #unlimited-plug-ins }
Інакше кажучи, вони не потрібні — імпортуйте та використовуйте код, який вам потрібен.
Будь-яка інтеграція спроєктована так, щоб її було дуже просто використовувати (із залежностями), тож ви можете створити «плагін» для свого застосунку у 2 рядках коду, використовуючи ту саму структуру та синтаксис, що й для ваших *операцій шляху*.
### Протестовано { #tested }
* 100% покриття тестами.
* 100% анотована типами кодова база.
* Використовується в production-застосунках.
## Можливості Starlette { #starlette-features }
**FastAPI** повністю сумісний із (та побудований на основі) [**Starlette**](https://www.starlette.dev/). Тому будь-який додатковий код Starlette, який ви маєте, також працюватиме.
`FastAPI` фактично є підкласом `Starlette`. Тому, якщо ви вже знайомі зі Starlette або використовуєте його, більшість функціональності працюватиме так само.
З **FastAPI** ви отримуєте всі можливості **Starlette** (адже FastAPI — це просто Starlette на стероїдах):
* Разюча продуктивність. Це [один із найшвидших доступних Python-фреймворків, на рівні з **NodeJS** і **Go**](https://github.com/encode/starlette#performance).
* Підтримка **WebSocket**.
* Фонові задачі у процесі.
* Події запуску та завершення роботи.
* Клієнт для тестування, побудований на HTTPX.
* Підтримка **CORS**, **GZip**, статичних файлів, потокових відповідей.
* Підтримка **сесій** і **cookie**.
* 100% покриття тестами.
* 100% анотована типами кодова база.
## Можливості Pydantic { #pydantic-features }
**FastAPI** повністю сумісний із (та побудований на основі) [**Pydantic**](https://docs.pydantic.dev/). Тому будь-який додатковий код Pydantic, який ви маєте, також працюватиме.
Включно із зовнішніми бібліотеками, які також базуються на Pydantic, як-от ORM-и, ODM-и для баз даних.
Це також означає, що в багатьох випадках ви можете передати той самий об'єкт, який отримуєте із запиту, **безпосередньо в базу даних**, оскільки все автоматично перевіряється.
Те саме застосовується й у зворотному напрямку — у багатьох випадках ви можете просто передати об'єкт, який отримуєте з бази даних, **безпосередньо клієнту**.
З **FastAPI** ви отримуєте всі можливості **Pydantic** (адже FastAPI базується на Pydantic для обробки всіх даних):
* **Ніякої плутанини**:
* Не потрібно вчити нову мікромову для визначення схем.
* Якщо ви знаєте типи Python, ви знаєте, як використовувати Pydantic.
* Легко працює з вашим **IDE/linter/мозком**:
* Оскільки структури даних pydantic є просто екземплярами класів, які ви визначаєте; автодоповнення, лінтинг, mypy і ваша інтуїція повинні добре працювати з вашими перевіреними даними.
* Валідує **складні структури**:
* Використання ієрархічних моделей Pydantic, Python `typing`’s `List` і `Dict` тощо.
* Валідатори дають змогу складні схеми даних чітко й просто визначати, перевіряти й документувати як JSON Schema.
* Ви можете мати глибоко **вкладені JSON** об'єкти, і всі вони будуть валідовані та анотовані.
* **Розширюваність**:
* Pydantic дозволяє визначати користувацькі типи даних або ви можете розширити валідацію методами в моделі, позначеними декоратором validator.
* 100% покриття тестами.
================================================
FILE: docs/uk/docs/help-fastapi.md
================================================
# Допоможіть FastAPI - отримайте допомогу { #help-fastapi-get-help }
Вам подобається **FastAPI**?
Хочете допомогти FastAPI, іншим користувачам та автору?
Або ви хочете отримати допомогу щодо **FastAPI**?
Є дуже прості способи допомогти (деякі потребують лише одного-двох кліків).
І є кілька способів отримати допомогу.
## Підпишіться на розсилку { #subscribe-to-the-newsletter }
Ви можете підписатися на (нечасту) розсилку [**FastAPI and friends**](newsletter.md), щоб бути в курсі:
* Новин про FastAPI та друзів 🚀
* Посібників 📝
* Можливостей ✨
* Несумісних змін 🚨
* Порад і трюків ✅
## Стежте за FastAPI в X (Twitter) { #follow-fastapi-on-x-twitter }
[Стежте за @fastapi в **X (Twitter)**](https://x.com/fastapi), щоб отримувати найсвіжіші новини про **FastAPI**. 🐦
## Додайте зірочку **FastAPI** на GitHub { #star-fastapi-in-github }
Ви можете «поставити зірочку» FastAPI на GitHub (натиснувши кнопку зірочки у верхньому правому куті): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️
Додавши зірочку, іншим користувачам буде легше його знайти і побачити, що він уже був корисним для інших.
## Стежте за випусками в репозиторії GitHub { #watch-the-github-repository-for-releases }
Ви можете «спостерігати» за FastAPI на GitHub (натиснувши кнопку «watch» у верхньому правому куті): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Там ви можете вибрати «Releases only».
Зробивши це, ви отримуватимете сповіщення (на вашу електронну пошту) щоразу, коли вийде новий реліз (нова версія) **FastAPI** з виправленнями помилок і новими можливостями.
## Зв'яжіться з автором { #connect-with-the-author }
Ви можете зв'язатися зі [мною (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), автором.
Ви можете:
* [Стежити за мною на **GitHub**](https://github.com/tiangolo).
* Подивитися інші Open Source-проєкти, які я створив і які можуть вам допомогти.
* Стежити, щоб бачити, коли я створюю новий Open Source-проєкт.
* [Стежити за мною в **X (Twitter)**](https://x.com/tiangolo) або [Mastodon](https://fosstodon.org/@tiangolo).
* Розкажіть мені, як ви використовуєте FastAPI (мені дуже приємно це чути).
* Дізнаватися, коли я роблю оголошення або випускаю нові інструменти.
* Також ви можете [стежити за @fastapi в X (Twitter)](https://x.com/fastapi) (окремий акаунт).
* [Стежити за мною в **LinkedIn**](https://www.linkedin.com/in/tiangolo/).
* Дізнаватися, коли я роблю оголошення або випускаю нові інструменти (хоча X (Twitter) я використовую частіше 🤷♂).
* Читати, що я пишу (або стежити за мною) на [**Dev.to**](https://dev.to/tiangolo) або [**Medium**](https://medium.com/@tiangolo).
* Читати інші ідеї, статті та про інструменти, які я створив.
* Стежити, щоб читати нове, коли я щось публікую.
## Твітніть про **FastAPI** { #tweet-about-fastapi }
[Твітніть про **FastAPI**](https://x.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/fastapi/fastapi) і дайте мені та іншим знати, чому він вам подобається. 🎉
Мені дуже подобається дізнаватися, як використовують **FastAPI**, що вам у ньому сподобалося, у якому проєкті/компанії ви його застосовуєте тощо.
## Голосуйте за FastAPI { #vote-for-fastapi }
* [Проголосуйте за **FastAPI** на Slant](https://www.slant.co/options/34241/~fastapi-review).
* [Проголосуйте за **FastAPI** на AlternativeTo](https://alternativeto.net/software/fastapi/about/).
* [Повідомте, що ви використовуєте **FastAPI**, на StackShare](https://stackshare.io/pypi-fastapi).
## Допомагайте іншим з питаннями на GitHub { #help-others-with-questions-in-github }
Ви можете спробувати допомагати іншим з їхніми питаннями у:
* [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered)
* [GitHub Issues](https://github.com/fastapi/fastapi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+)
У багатьох випадках ви вже можете знати відповідь на ці питання. 🤓
Якщо ви багато допомагаєте людям із їхніми питаннями, ви станете офіційним [Експертом FastAPI](fastapi-people.md#fastapi-experts). 🎉
Пам'ятайте, найважливіше: намагайтеся бути добрими. Люди приходять зі своєю фрустрацією і часто питають не найкращим чином, але постарайтеся бути якомога доброзичливішими. 🤗
Ідея в тому, щоб спільнота **FastAPI** була доброзичливою та гостинною. Водночас не терпіть булінг чи неповажну поведінку щодо інших. Ми маємо піклуватися одне про одного.
---
Ось як допомагати іншим із питаннями (у discussions або issues):
### Зрозумійте питання { #understand-the-question }
* Перевірте, чи розумієте ви **мету** та варіант використання людини, яка питає.
* Потім перевірте, чи **чітке** саме питання (переважна більшість — це питання).
* Часто запит стосується уявного рішення користувача, але може бути **краще**. Якщо ви краще зрозумієте проблему та варіант використання, зможете запропонувати кращу **альтернативу**.
* Якщо ви не розумієте питання, попросіть більше **подробиць**.
### Відтворіть проблему { #reproduce-the-problem }
У більшості випадків і запитань мова йде про **оригінальний код** людини.
Часто вони наводять лише фрагмент коду, але цього недостатньо, щоб **відтворити проблему**.
* Ви можете попросити надати [мінімальний, відтворюваний приклад](https://stackoverflow.com/help/minimal-reproducible-example), який ви зможете **скопіювати-вставити** і запустити локально, щоб побачити ту саму помилку або поведінку, яку бачать вони, або краще зрозуміти їхній варіант використання.
* Якщо ви дуже щедрі, можете спробувати **створити такий приклад** самостійно, лише на основі опису проблеми. Просто майте на увазі, що це може зайняти багато часу, і краще спочатку попросити їх уточнити проблему.
### Запропонуйте рішення { #suggest-solutions }
* Після того як ви змогли зрозуміти питання, дайте можливу **відповідь**.
* Часто краще зрозуміти їхню **первинну проблему або варіант використання**, адже може бути кращий спосіб її вирішити, ніж те, що вони намагаються зробити.
### Попросіть закрити { #ask-to-close }
Якщо вони відповіли, дуже ймовірно, що ви розв'язали їхню проблему. Вітаю, **ви герой**! 🦸
* Тепер, якщо це вирішило їхню проблему, ви можете попросити:
* У GitHub Discussions: позначити коментар як **відповідь**.
* У GitHub Issues: **закрити** issue.
## Стежте за репозиторієм GitHub { #watch-the-github-repository }
Ви можете «спостерігати» за FastAPI на GitHub (натиснувши кнопку «watch» у верхньому правому куті): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀
Якщо вибрати «Watching» замість «Releases only», ви отримуватимете сповіщення, коли хтось створює нове issue або питання. Ви також можете вказати, що хочете отримувати сповіщення лише про нові issues, або discussions, або PR тощо.
Тоді ви зможете спробувати допомогти їм вирішити ці питання.
## Ставте питання { #ask-questions }
Ви можете [створити нове питання](https://github.com/fastapi/fastapi/discussions/new?category=questions) у репозиторії GitHub, наприклад, щоб:
* Поставити **питання** або запитати про **проблему**.
* Запропонувати нову **можливість**.
**Примітка**: якщо ви це зробите, я попрошу вас також допомагати іншим. 😉
## Переглядайте запити на витяг { #review-pull-requests }
Ви можете допомогти мені переглядати запити на витяг інших.
І знову, будь ласка, намагайтеся бути якомога доброзичливішими. 🤗
---
Ось що слід пам'ятати і як переглядати запит на витяг:
### Зрозумійте проблему { #understand-the-problem }
* Спочатку переконайтеся, що ви **розумієте проблему**, яку намагається вирішити запит на витяг. Може бути довша дискусія у GitHub Discussion або issue.
* Також є велика ймовірність, що запит на витяг насправді не потрібен, бо проблему можна розв'язати **іншим способом**. Тоді ви можете про це запропонувати або запитати.
### Не переймайтеся стилем { #dont-worry-about-style }
* Не надто хвилюйтеся про стиль повідомлень фіксацій, я зроблю squash and merge, налаштувавши фіксацію вручну.
* Також не переймайтеся правилами стилю, вже є автоматизовані інструменти, що це перевіряють.
А якщо будуть інші вимоги щодо стилю чи узгодженості, я попрошу про це безпосередньо або додам зверху фіксації з потрібними змінами.
### Перевірте код { #check-the-code }
* Перегляньте та прочитайте код, оцініть, чи він має сенс, **запустіть його локально** і перевірте, чи справді він розв'язує проблему.
* Потім залиште **коментар**, що ви це зробили, так я знатиму, що ви справді перевірили.
/// info
На жаль, я не можу просто довіряти PR, які мають кілька схвалень.
Бувало не раз, що PR мали 3, 5 або більше схвалень, мабуть тому, що опис привабливий, але коли я перевіряв PR, вони виявлялися зламаними, мали помилку або не розв'язували заявлену проблему. 😅
Тож дуже важливо, щоб ви справді прочитали і запустили код та повідомили в коментарях, що ви це зробили. 🤓
///
* Якщо PR можна якось спростити, ви можете про це попросити, але немає потреби бути надто прискіпливими, адже може бути багато суб'єктивних точок зору (і в мене також є своя 🙈), тож краще зосередитися на фундаментальних речах.
### Тести { #tests }
* Допоможіть перевірити, що PR має **тести**.
* Переконайтеся, що тести **падають** до PR. 🚨
* Потім перевірте, що тести **проходять** після PR. ✅
* Багато PRів не мають тестів, ви можете **нагадати** додати тести або навіть **запропонувати** деякі тести самі. Це одна з речей, яка найбільше забирає часу, і ви дуже допоможете.
* Потім також прокоментуйте, що саме ви спробували, так я знатиму, що ви це перевірили. 🤓
## Створіть запит на витяг { #create-a-pull-request }
Ви можете [зробити внесок](contributing.md) у вихідний код із запитами на витяг, наприклад:
* Щоб виправити описку, знайдену в документації.
* Щоб поділитися статтею, відео або подкастом про FastAPI, який ви створили або знайшли, [відредагувавши цей файл](https://github.com/fastapi/fastapi/edit/master/docs/en/data/external_links.yml).
* Обов'язково додайте ваше посилання на початок відповідного розділу.
* Щоб допомогти [перекласти документацію](contributing.md#translations) вашою мовою.
* Ви також можете допомогти з переглядом перекладів, створених іншими.
* Щоб запропонувати нові розділи документації.
* Щоб виправити наявну проблему/помилку.
* Обов'язково додайте тести.
* Щоб додати нову можливість.
* Обов'язково додайте тести.
* Обов'язково додайте документацію, якщо це доречно.
## Допоможіть підтримувати FastAPI { #help-maintain-fastapi }
Допоможіть мені підтримувати **FastAPI**! 🤓
Роботи багато, і більшу її частину можете зробити **ВИ**.
Основні завдання, які ви можете виконувати вже зараз:
* [Допомагайте іншим з питаннями на GitHub](#help-others-with-questions-in-github) (див. розділ вище).
* [Переглядайте запити на витяг](#review-pull-requests) (див. розділ вище).
Ці два завдання **найбільше споживають час**. Це основна робота з підтримки FastAPI.
Якщо ви можете допомогти мені з цим, **ви допомагаєте підтримувати FastAPI** і гарантуєте, що він **рухатиметься швидше й краще**. 🚀
## Долучайтеся до чату { #join-the-chat }
Долучайтеся до 👥 [серверу чату Discord](https://discord.gg/VQjSZaeJmf) 👥 і спілкуйтеся з іншими в спільноті FastAPI.
/// tip
Для запитань ставте їх у [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/new?category=questions), там значно вища ймовірність, що вам допоможуть [Експерти FastAPI](fastapi-people.md#fastapi-experts).
Використовуйте чат лише для інших загальних розмов.
///
### Не використовуйте чат для запитань { #dont-use-the-chat-for-questions }
Майте на увазі, що оскільки чати дозволяють більше «вільної розмови», легко ставити надто загальні питання, на які складніше відповісти, тож ви можете не отримати відповідей.
У GitHub шаблон підкаже вам, як написати правильне питання, щоб ви легше отримали хорошу відповідь, або навіть вирішили проблему самостійно ще до запиту. І в GitHub я можу гарантувати, що завжди на все відповім, навіть якщо це займе трохи часу. Особисто я не можу робити це в чатах. 😅
Розмови в чатах також не так просто шукати, як у GitHub, тож питання та відповіді можуть загубитися. І лише ті, що в GitHub, зараховуються, щоб стати [Експертом FastAPI](fastapi-people.md#fastapi-experts), тож швидше за все ви отримаєте більше уваги саме в GitHub.
З іншого боку, у чатах є тисячі користувачів, тож дуже ймовірно, що ви майже завжди знайдете там співрозмовника. 😄
## Спонсоруйте автора { #sponsor-the-author }
Якщо ваш **продукт/компанія** залежить від **FastAPI** або пов'язана з ним і ви хочете охопити його користувачів, ви можете спонсорувати автора (мене) через [GitHub sponsors](https://github.com/sponsors/tiangolo). Залежно від рівня ви можете отримати додаткові переваги, наприклад значок у документації. 🎁
---
Дякую! 🚀
================================================
FILE: docs/uk/docs/history-design-future.md
================================================
# Історія, проєктування і майбутнє { #history-design-and-future }
Деякий час тому [користувач **FastAPI** запитав](https://github.com/fastapi/fastapi/issues/3#issuecomment-454956920):
> Яка історія цього проєкту? Здається, він нізвідки за кілька тижнів став чудовим [...]
Ось трохи цієї історії.
## Альтернативи { #alternatives }
Я створював API зі складними вимогами протягом кількох років (машинне навчання, розподілені системи, асинхронні завдання, бази даних NoSQL тощо), очолюючи кілька команд розробників.
У межах цього мені довелося досліджувати, тестувати й використовувати багато альтернатив.
Історія **FastAPI** значною мірою - це історія його попередників.
Як сказано в розділі [Альтернативи](alternatives.md):
**FastAPI** не існував би без попередньої роботи інших.
Було створено багато інструментів, які надихнули на його створення.
Я роками уникав створення нового фреймворку. Спочатку я намагався вирішити всі можливості, які покриває **FastAPI**, використовуючи різні фреймворки, плагіни та інструменти.
Але в певний момент не залишилося іншого варіанту, окрім створити щось, що надає всі ці можливості, узявши найкращі ідеї з попередніх інструментів і поєднавши їх якнайкраще, використовуючи можливості самої мови, яких раніше взагалі не було (підказки типів Python 3.6+).
## Дослідження { #investigation }
Використовуючи всі попередні альтернативи, я мав змогу повчитися в кожної, узяти ідеї й поєднати їх якнайкраще для себе та команд розробників, з якими працював.
Наприклад, було очевидно, що в ідеалі все має ґрунтуватися на стандартних підказках типів Python.
Також найкращим підходом було використовувати вже наявні стандарти.
Тож, ще до початку написання коду **FastAPI**, я провів кілька місяців, вивчаючи специфікації OpenAPI, Схеми JSON, OAuth2 тощо. Розуміючи їхні взаємозв'язки, перетини та відмінності.
## Проєктування { #design }
Потім я приділив час проєктуванню «API» для розробника, яке я хотів мати як користувач (як розробник, що використовує FastAPI).
Я протестував кілька ідей у найпопулярніших Python-редакторах: PyCharm, VS Code, редакторах на основі Jedi.
За даними [Python Developer Survey](https://www.jetbrains.com/research/python-developers-survey-2018/#development-tools), це охоплює близько 80% користувачів.
Це означає, що **FastAPI** спеціально тестувався з редакторами, якими користуються 80% розробників Python. І оскільки більшість інших редакторів працюють подібно, усі ці переваги мають працювати практично у всіх редакторах.
Так я зміг знайти найкращі способи максимально зменшити дублювання коду, забезпечити автодоповнення всюди, перевірки типів і помилок тощо.
І все це так, щоб надати найкращий досвід розробки для всіх розробників.
## Вимоги { #requirements }
Після перевірки кількох альтернатив я вирішив використовувати [**Pydantic**](https://docs.pydantic.dev/) через його переваги.
Потім я зробив внески до нього, щоб зробити його повністю сумісним із Схемою JSON, додати підтримку різних способів оголошення обмежень і поліпшити підтримку редакторів (перевірки типів, автодоповнення) на основі тестів у кількох редакторах.
Під час розробки я також зробив внески до [**Starlette**](https://www.starlette.dev/), іншої ключової залежності.
## Розробка { #development }
Коли я взявся безпосередньо за створення **FastAPI**, більшість складових уже були на місцях: дизайн визначено, вимоги та інструменти підготовлено, знання про стандарти й специфікації - чіткі та свіжі.
## Майбутнє { #future }
На цей момент уже зрозуміло, що **FastAPI** зі своїми ідеями корисний для багатьох.
Його обирають замість попередніх альтернатив, бо він краще відповідає багатьом сценаріям використання.
Багато розробників і команд уже залежать від **FastAPI** у своїх проєктах (включно зі мною та моєю командою).
Але попереду ще багато покращень і можливостей.
**FastAPI** має велике майбутнє.
І [ваша допомога](help-fastapi.md) дуже цінується.
================================================
FILE: docs/uk/docs/how-to/authentication-error-status-code.md
================================================
# Використовуйте старі коди статусу помилки автентифікації 403 { #use-old-403-authentication-error-status-codes }
До версії FastAPI `0.122.0`, коли інтегровані засоби безпеки повертали клієнту помилку після невдалої автентифікації, вони використовували HTTP код статусу `403 Forbidden`.
Починаючи з версії FastAPI `0.122.0`, вони використовують більш доречний HTTP код статусу `401 Unauthorized` і повертають змістовний заголовок `WWW-Authenticate` у відповіді, відповідно до специфікацій HTTP, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized).
Але якщо з якоїсь причини ваші клієнти залежать від старої поведінки, ви можете повернутися до неї, переписавши метод `make_not_authenticated_error` у ваших класах безпеки.
Наприклад, ви можете створити підклас `HTTPBearer`, який повертатиме помилку `403 Forbidden` замість типового `401 Unauthorized`:
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
/// tip | Порада
Зверніть увагу, що функція повертає екземпляр винятку, вона не породжує його. Породження відбувається в іншій частині внутрішнього коду.
///
================================================
FILE: docs/uk/docs/how-to/conditional-openapi.md
================================================
# Умовний OpenAPI { #conditional-openapi }
Якщо потрібно, ви можете використовувати налаштування і змінні оточення, щоб умовно налаштовувати OpenAPI залежно від середовища, а також повністю вимикати його.
## Про безпеку, API та документацію { #about-security-apis-and-docs }
Приховування інтерфейсів документації у продукційному середовищі не має бути способом захисту вашого API.
Це не додає жодної додаткової безпеки вашому API, операції шляху й надалі будуть доступні там, де вони є.
Якщо у вашому коді є вразливість, вона залишиться.
Приховування документації лише ускладнює розуміння того, як взаємодіяти з вашим API, і може ускладнити для вас його налагодження у продакшні. Це можна вважати просто формою [Безпека через неясність](https://en.wikipedia.org/wiki/Security_through_obscurity).
Якщо ви хочете захистити ваш API, є кілька кращих дій, які ви можете зробити, наприклад:
- Переконайтеся, що у вас добре визначені моделі Pydantic для тіл запитів і відповідей.
- Налаштуйте потрібні дозволи та ролі за допомогою залежностей.
- Ніколи не зберігайте паролі у відкритому вигляді, лише хеші паролів.
- Реалізуйте та використовуйте відомі криптографічні інструменти, як-от pwdlib і токени JWT.
- Додайте більш детальний контроль дозволів із областями OAuth2 там, де це потрібно.
- ...тощо.
Втім, у вас може бути дуже специфічний випадок використання, коли справді потрібно вимкнути документацію API для певного середовища (наприклад, для продакшну) або залежно від конфігурацій зі змінних оточення.
## Умовний OpenAPI з налаштувань і змінних оточення { #conditional-openapi-from-settings-and-env-vars }
Ви можете легко використати ті самі налаштування Pydantic, щоб налаштувати згенерований OpenAPI та інтерфейси документації.
Наприклад:
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
Тут ми оголошуємо налаштування `openapi_url` з тим самим значенням за замовчуванням `"/openapi.json"`.
Потім ми використовуємо його під час створення застосунку `FastAPI`.
Далі ви можете вимкнути OpenAPI (включно з інтерфейсами документації), встановивши змінну оточення `OPENAPI_URL` у пусту строку, наприклад:
```console
$ OPENAPI_URL= uvicorn main:app
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Потім, якщо ви перейдете за URL `/openapi.json`, `/docs` або `/redoc`, ви просто отримаєте помилку `404 Not Found` на кшталт:
```JSON
{
"detail": "Not Found"
}
```
================================================
FILE: docs/uk/docs/how-to/configure-swagger-ui.md
================================================
# Налаштуйте Swagger UI { #configure-swagger-ui }
Ви можете налаштувати додаткові [параметри Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
Щоб їх налаштувати, передайте аргумент `swagger_ui_parameters` під час створення об’єкта додатка `FastAPI()` або до функції `get_swagger_ui_html()`.
`swagger_ui_parameters` отримує словник із налаштуваннями, що передаються безпосередньо до Swagger UI.
FastAPI перетворює ці налаштування на **JSON**, щоб зробити їх сумісними з JavaScript, оскільки саме це потрібно Swagger UI.
## Вимкніть підсвітку синтаксису { #disable-syntax-highlighting }
Наприклад, ви можете вимкнути підсвітку синтаксису в Swagger UI.
Без змін у налаштуваннях підсвітка синтаксису увімкнена за замовчуванням:
Але ви можете вимкнути її, встановивши `syntaxHighlight` у `False`:
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
...після цього Swagger UI більше не показуватиме підсвітку синтаксису:
## Змініть тему { #change-the-theme }
Так само ви можете задати тему підсвітки синтаксису ключем `"syntaxHighlight.theme"` (зверніть увагу, що посередині є крапка):
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
Це налаштування змінить колірну тему підсвітки синтаксису:
## Змініть параметри Swagger UI за замовчуванням { #change-default-swagger-ui-parameters }
FastAPI містить деякі параметри конфігурації за замовчуванням, що підходять для більшості випадків.
Вони включають такі типові налаштування:
{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *}
Ви можете переписати будь-яке з них, задавши інше значення в аргументі `swagger_ui_parameters`.
Наприклад, щоб вимкнути `deepLinking`, ви можете передати такі налаштування до `swagger_ui_parameters`:
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
## Інші параметри Swagger UI { #other-swagger-ui-parameters }
Щоб побачити всі можливі налаштування, які ви можете використовувати, прочитайте офіційну [документацію щодо параметрів Swagger UI](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/).
## Налаштування лише для JavaScript { #javascript-only-settings }
Swagger UI також дозволяє інші налаштування як об’єкти, що є тільки для **JavaScript** (наприклад, функції JavaScript).
FastAPI також включає такі налаштування `presets`, що є лише для JavaScript:
```JavaScript
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
]
```
Це об’єкти **JavaScript**, а не строки, тому ви не можете передати їх безпосередньо з коду Python.
Якщо вам потрібно використати такі налаштування лише для JavaScript, скористайтеся одним із методів вище. Повністю перепишіть операцію шляху Swagger UI та вручну напишіть потрібний JavaScript.
================================================
FILE: docs/uk/docs/how-to/custom-docs-ui-assets.md
================================================
# Користувацькі статичні ресурси інтерфейсу документації (самохостинг) { #custom-docs-ui-static-assets-self-hosting }
Документація API використовує **Swagger UI** і **ReDoc**, і кожному з них потрібні файли JavaScript та CSS.
Типово ці файли віддаються з CDN.
Але це можна налаштувати: ви можете вказати конкретний CDN або віддавати файли самостійно.
## Власний CDN для JavaScript і CSS { #custom-cdn-for-javascript-and-css }
Припустімо, що ви хочете використовувати інший CDN, наприклад `https://unpkg.com/`.
Це може бути корисно, якщо, наприклад, ви живете в країні, що обмежує деякі URL-адреси.
### Вимкніть автоматичну документацію { #disable-the-automatic-docs }
Перший крок - вимкнути автоматичну документацію, адже типово вона використовує стандартний CDN.
Щоб їх вимкнути, встановіть їхні URL у `None` під час створення вашого застосунку `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
### Додайте користувацьку документацію { #include-the-custom-docs }
Тепер ви можете створити *операції шляху* для користувацької документації.
Ви можете перевикористати внутрішні функції FastAPI для створення HTML-сторінок документації і передати їм потрібні аргументи:
- `openapi_url`: URL, за яким HTML-сторінка документації зможе отримати схему OpenAPI для вашого API. Тут можна використати атрибут `app.openapi_url`.
- `title`: заголовок вашого API.
- `oauth2_redirect_url`: тут можна використати `app.swagger_ui_oauth2_redirect_url`, щоб узяти значення за замовчуванням.
- `swagger_js_url`: URL, за яким HTML для Swagger UI зможе отримати файл **JavaScript**. Це URL вашого користувацького CDN.
- `swagger_css_url`: URL, за яким HTML для Swagger UI зможе отримати файл **CSS**. Це URL вашого користувацького CDN.
Аналогічно для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Порада
*Операція шляху* для `swagger_ui_redirect` - це допоміжний маршрут, коли ви використовуєте OAuth2.
Якщо ви інтегруєте ваш API з провайдером OAuth2, ви зможете автентифікуватися і повернутися до документації API з отриманими обліковими даними. І взаємодіяти з ним, використовуючи реальну автентифікацію OAuth2.
Swagger UI впорається з цим «за лаштунками», але йому потрібен цей «redirect» хелпер.
///
### Створіть операцію шляху для перевірки { #create-a-path-operation-to-test-it }
Тепер, щоб перевірити, що все працює, створіть *операцію шляху*:
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
### Перевірте { #test-it }
Тепер ви маєте змогу відкрити документацію за [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) і перезавантажити сторінку, вона завантажить ці ресурси з нового CDN.
## Самохостинг JavaScript і CSS для документації { #self-hosting-javascript-and-css-for-docs }
Самохостинг JavaScript і CSS може бути корисним, якщо, наприклад, ваш застосунок має працювати офлайн, без доступу до Інтернету, або в локальній мережі.
Тут ви побачите, як віддавати ці файли самостійно, у тому самому застосунку FastAPI, і налаштувати документацію на їх використання.
### Структура файлів проєкту { #project-file-structure }
Припустімо, що структура файлів вашого проєкту виглядає так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Тепер створіть каталог для збереження цих статичних файлів.
Нова структура файлів може виглядати так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Завантажте файли { #download-the-files }
Завантажте статичні файли, потрібні для документації, і помістіть їх у каталог `static/`.
Ви, ймовірно, можете натиснути правою кнопкою на кожному посиланні і вибрати опцію на кшталт «Зберегти посилання як...».
**Swagger UI** використовує файли:
- [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js)
- [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css)
А **ReDoc** використовує файл:
- [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js)
Після цього ваша структура файлів може виглядати так:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static
├── redoc.standalone.js
├── swagger-ui-bundle.js
└── swagger-ui.css
```
### Обслуговуйте статичні файли { #serve-the-static-files }
- Імпортуйте `StaticFiles`.
- Змонтуйте екземпляр `StaticFiles()` у певному шляху.
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
### Перевірте статичні файли { #test-the-static-files }
Запустіть ваш застосунок і перейдіть до [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js).
Ви маєте побачити дуже довгий файл JavaScript для **ReDoc**.
Він може починатися приблизно так:
```JavaScript
/*! For license information please see redoc.standalone.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):
...
```
Це підтверджує, що ви можете віддавати статичні файли з вашого застосунку і що ви розмістили статичні файли для документації у правильному місці.
Тепер ми можемо налаштувати застосунок на використання цих статичних файлів для документації.
### Вимкніть автоматичну документацію для статичних файлів { #disable-the-automatic-docs-for-static-files }
Як і при використанні користувацького CDN, першим кроком є вимкнення автоматичної документації, оскільки типово вона використовує CDN.
Щоб їх вимкнути, встановіть їхні URL у `None` під час створення вашого застосунку `FastAPI`:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
### Додайте користувацьку документацію для статичних файлів { #include-the-custom-docs-for-static-files }
Аналогічно користувацькому CDN, тепер ви можете створити *операції шляху* для користувацької документації.
Знову ж, ви можете перевикористати внутрішні функції FastAPI для створення HTML-сторінок документації і передати їм потрібні аргументи:
- `openapi_url`: URL, за яким HTML-сторінка документації зможе отримати схему OpenAPI для вашого API. Тут можна використати атрибут `app.openapi_url`.
- `title`: заголовок вашого API.
- `oauth2_redirect_url`: тут можна використати `app.swagger_ui_oauth2_redirect_url`, щоб узяти значення за замовчуванням.
- `swagger_js_url`: URL, за яким HTML для Swagger UI зможе отримати файл **JavaScript**. **Це той файл, який тепер віддає ваш власний застосунок**.
- `swagger_css_url`: URL, за яким HTML для Swagger UI зможе отримати файл **CSS**. **Це той файл, який тепер віддає ваш власний застосунок**.
Аналогічно для ReDoc...
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Порада
*Операція шляху* для `swagger_ui_redirect` - це допоміжний маршрут, коли ви використовуєте OAuth2.
Якщо ви інтегруєте ваш API з провайдером OAuth2, ви зможете автентифікуватися і повернутися до документації API з отриманими обліковими даними. І взаємодіяти з ним, використовуючи реальну автентифікацію OAuth2.
Swagger UI впорається з цим «за лаштунками», але йому потрібен цей «redirect» хелпер.
///
### Створіть операцію шляху для перевірки статичних файлів { #create-a-path-operation-to-test-static-files }
Тепер, щоб перевірити, що все працює, створіть *операцію шляху*:
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
### Перевірте UI зі статичними файлами { #test-static-files-ui }
Тепер ви маєте змогу вимкнути WiFi, відкрити документацію за [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) і перезавантажити сторінку.
І навіть без Інтернету ви зможете побачити документацію для вашого API і взаємодіяти з ним.
================================================
FILE: docs/uk/docs/how-to/custom-request-and-route.md
================================================
# Користувацькі класи Request та APIRoute { #custom-request-and-apiroute-class }
У деяких випадках ви можете захотіти перевизначити логіку, яку використовують класи `Request` та `APIRoute`.
Зокрема, це може бути доброю альтернативою логіці в проміжному програмному забезпеченні.
Наприклад, якщо потрібно прочитати або змінити тіло запиту до того, як його обробить ваш застосунок.
/// danger | Обережно
Це «просунута» можливість.
Якщо ви тільки починаєте працювати з **FastAPI**, можливо, варто пропустити цей розділ.
///
## Випадки використання { #use-cases }
Деякі варіанти використання:
- Перетворення не-JSON тіл запитів на JSON (наприклад, [`msgpack`](https://msgpack.org/index.html)).
- Розпакування тіл запитів, стиснених gzip.
- Автоматичне логування всіх тіл запитів.
## Обробка користувацьких кодувань тіла запиту { #handling-custom-request-body-encodings }
Розгляньмо, як використати користувацький підклас `Request` для розпакування gzip-запитів.
А також підклас `APIRoute`, щоб застосувати цей користувацький клас запиту.
### Створіть користувацький клас `GzipRequest` { #create-a-custom-gziprequest-class }
/// tip | Порада
Це навчальний приклад, щоб продемонструвати принцип роботи. Якщо вам потрібна підтримка Gzip, скористайтеся вбудованим [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware).
///
Спочатку створимо клас `GzipRequest`, який перевизначить метод `Request.body()`, щоб розпаковувати тіло за наявності відповідного заголовка.
Якщо в заголовку немає `gzip`, він не намагатиметься розпаковувати тіло.
Таким чином один і той самий клас маршруту зможе обробляти як стиснені gzip, так і нестиснені запити.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *}
### Створіть користувацький клас `GzipRoute` { #create-a-custom-gziproute-class }
Далі створимо користувацький підклас `fastapi.routing.APIRoute`, який використовуватиме `GzipRequest`.
Цього разу він перевизначить метод `APIRoute.get_route_handler()`.
Цей метод повертає функцію. І саме ця функція прийме запит і поверне відповідь.
Тут ми використовуємо її, щоб створити `GzipRequest` з початкового запиту.
{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *}
/// note | Технічні деталі
У `Request` є атрибут `request.scope` - це просто Python `dict`, що містить метадані, пов'язані із запитом.
Також `Request` має `request.receive` - це функція для «отримання» тіла запиту.
`scope` `dict` і функція `receive` є частиною специфікації ASGI.
І саме ці дві сутності - `scope` та `receive` - потрібні для створення нового екземпляра `Request`.
Щоб дізнатися більше про `Request`, перегляньте [документацію Starlette про запити](https://www.starlette.dev/requests/).
///
Єдине, що робить інакше функція, повернена `GzipRequest.get_route_handler`, - перетворює `Request` на `GzipRequest`.
Завдяки цьому наш `GzipRequest` подбає про розпакування даних (за потреби) перед передаванням їх у наші *операції шляху*.
Після цього вся логіка обробки залишається тією самою.
А завдяки змінам у `GzipRequest.body` тіло запиту за потреби буде автоматично розпаковане під час завантаження **FastAPI**.
## Доступ до тіла запиту в обробнику виключень { #accessing-the-request-body-in-an-exception-handler }
/// tip | Порада
Щоб розв’язати це саме завдання, скоріш за все, простіше використати `body` у користувацькому обробнику `RequestValidationError` ([Обробка помилок](../tutorial/handling-errors.md#use-the-requestvalidationerror-body)).
Але цей приклад усе ще корисний і показує, як взаємодіяти з внутрішніми компонентами.
///
Ми також можемо скористатися цим підходом, щоб отримати доступ до тіла запиту в обробнику виключень.
Усе, що потрібно, - обробити запит усередині блоку `try`/`except`:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *}
Якщо станеться виключення, екземпляр `Request` усе ще буде у видимості, тож ми зможемо прочитати й використати тіло запиту під час обробки помилки:
{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *}
## Користувацький клас `APIRoute` у маршрутизаторі { #custom-apiroute-class-in-a-router }
Можна також встановити параметр `route_class` у `APIRouter`:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *}
У цьому прикладі *операції шляху* в `router` використовуватимуть користувацький клас `TimedRoute` і матимуть додатковий заголовок відповіді `X-Response-Time` із часом, витраченим на формування відповіді:
{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *}
================================================
FILE: docs/uk/docs/how-to/extending-openapi.md
================================================
# Розширення OpenAPI { #extending-openapi }
У деяких випадках вам може знадобитися змінити згенеровану схему OpenAPI.
У цьому розділі ви побачите як це зробити.
## Звичайний процес { #the-normal-process }
Звичайний (типовий) процес такий.
Застосунок `FastAPI` (екземпляр) має метод `.openapi()`, який має повертати схему OpenAPI.
Під час створення об'єкта застосунку реєструється *операція шляху* для `/openapi.json` (або для того значення, яке ви встановили в `openapi_url`).
Вона просто повертає відповідь JSON з результатом методу `.openapi()` застосунку.
Типово метод `.openapi()` перевіряє властивість `.openapi_schema`, і якщо там вже є дані, повертає їх.
Якщо ні, він генерує їх за допомогою утилітарної функції `fastapi.openapi.utils.get_openapi`.
Функція `get_openapi()` приймає такі параметри:
- `title`: Заголовок OpenAPI, показується в документації.
- `version`: Версія вашого API, напр. `2.5.0`.
- `openapi_version`: Версія специфікації OpenAPI, що використовується. Типово остання: `3.1.0`.
- `summary`: Короткий підсумок API.
- `description`: Опис вашого API; може містити markdown і буде показаний у документації.
- `routes`: Список маршрутів, це кожна з зареєстрованих *операцій шляху*. Їх беруть з `app.routes`.
/// info | Інформація
Параметр `summary` доступний в OpenAPI 3.1.0 і вище, підтримується FastAPI 0.99.0 і вище.
///
## Переписування типових значень { #overriding-the-defaults }
Використовуючи наведене вище, ви можете скористатися тією ж утилітарною функцією для генерації схеми OpenAPI і переписати потрібні частини.
Наприклад, додаймо [розширення OpenAPI ReDoc для додавання власного логотипа](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo).
### Звичайний **FastAPI** { #normal-fastapi }
Спочатку напишіть ваш застосунок **FastAPI** як зазвичай:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
### Згенерувати схему OpenAPI { #generate-the-openapi-schema }
Далі використайте ту ж утилітарну функцію для генерації схеми OpenAPI всередині функції `custom_openapi()`:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
### Змінити схему OpenAPI { #modify-the-openapi-schema }
Тепер можна додати розширення ReDoc, додавши власний `x-logo` до «об'єкта» `info` у схемі OpenAPI:
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
### Кешувати схему OpenAPI { #cache-the-openapi-schema }
Ви можете використовувати властивість `.openapi_schema` як «кеш» для збереження згенерованої схеми.
Так вашому застосунку не доведеться щоразу генерувати схему, коли користувач відкриває документацію вашого API.
Вона буде згенерована лише один раз, а потім та сама закешована схема використовуватиметься для наступних запитів.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
### Переписати метод { #override-the-method }
Тепер ви можете замінити метод `.openapi()` вашою новою функцією.
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
### Перевірте { #check-it }
Коли ви перейдете за адресою [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc), побачите, що використовується ваш власний логотип (у цьому прикладі логотип **FastAPI**):
================================================
FILE: docs/uk/docs/how-to/general.md
================================================
# Загальне - Як зробити - Рецепти { #general-how-to-recipes }
Нижче наведено кілька вказівок до інших розділів документації для загальних або частих питань.
## Фільтрування даних - Безпека { #filter-data-security }
Щоб гарантувати, що ви не повертаєте більше даних, ніж слід, прочитайте документацію [Навчальний посібник - Модель відповіді - Тип повернення](../tutorial/response-model.md).
## Оптимізувати продуктивність відповіді - Модель відповіді - Тип повернення { #optimize-response-performance-response-model-return-type }
Щоб оптимізувати продуктивність під час повернення даних JSON, використовуйте тип повернення або модель відповіді, таким чином Pydantic виконуватиме серіалізацію в JSON на боці Rust, без проходження через Python. Докладніше читайте в документації [Навчальний посібник - Модель відповіді - Тип повернення](../tutorial/response-model.md).
## Мітки документації - OpenAPI { #documentation-tags-openapi }
Щоб додати мітки до ваших *операцій шляху* та згрупувати їх в інтерфейсі документації, прочитайте документацію [Навчальний посібник - Налаштування операції шляху - Мітки](../tutorial/path-operation-configuration.md#tags).
## Короткий опис і опис - OpenAPI { #documentation-summary-and-description-openapi }
Щоб додати короткий опис і опис до ваших *операцій шляху* і показати їх в інтерфейсі документації, прочитайте документацію [Навчальний посібник - Налаштування операції шляху - Короткий опис і опис](../tutorial/path-operation-configuration.md#summary-and-description).
## Опис відповіді в документації - OpenAPI { #documentation-response-description-openapi }
Щоб визначити опис відповіді, що відображається в інтерфейсі документації, прочитайте документацію [Навчальний посібник - Налаштування операції шляху - Опис відповіді](../tutorial/path-operation-configuration.md#response-description).
## Позначити застарілою *операцію шляху* - OpenAPI { #documentation-deprecate-a-path-operation-openapi }
Щоб позначити *операцію шляху* як застарілу і показати це в інтерфейсі документації, прочитайте документацію [Навчальний посібник - Налаштування операції шляху - Позначення як застаріле](../tutorial/path-operation-configuration.md#deprecate-a-path-operation).
## Перетворити будь-які дані на сумісні з JSON { #convert-any-data-to-json-compatible }
Щоб перетворити будь-які дані на сумісні з JSON, прочитайте документацію [Навчальний посібник - Кодувальник, сумісний з JSON](../tutorial/encoder.md).
## Метадані OpenAPI - Документація { #openapi-metadata-docs }
Щоб додати метадані до вашої схеми OpenAPI, зокрема ліцензію, версію, контактні дані тощо, прочитайте документацію [Навчальний посібник - Метадані та URL документації](../tutorial/metadata.md).
## Власний URL OpenAPI { #openapi-custom-url }
Щоб налаштувати URL OpenAPI (або прибрати його), прочитайте документацію [Навчальний посібник - Метадані та URL документації](../tutorial/metadata.md#openapi-url).
## URL документації OpenAPI { #openapi-docs-urls }
Щоб оновити URL, які використовуються для автоматично згенерованих інтерфейсів користувача документації, прочитайте документацію [Навчальний посібник - Метадані та URL документації](../tutorial/metadata.md#docs-urls).
================================================
FILE: docs/uk/docs/how-to/graphql.md
================================================
# GraphQL { #graphql }
Оскільки FastAPI базується на стандарті ASGI, дуже просто інтегрувати будь-яку бібліотеку GraphQL, сумісну з ASGI.
Ви можете поєднувати звичайні *операції шляху* FastAPI з GraphQL в одному застосунку.
/// tip | Порада
GraphQL розв’язує деякі дуже специфічні сценарії використання.
Порівняно зі звичайними веб-API він має переваги та недоліки.
Переконайтеся, що переваги для вашого випадку використання переважають недоліки. 🤓
///
## Бібліотеки GraphQL { #graphql-libraries }
Ось деякі бібліотеки GraphQL з підтримкою ASGI. Ви можете використовувати їх із FastAPI:
* [Strawberry](https://strawberry.rocks/) 🍓
* З [документацією для FastAPI](https://strawberry.rocks/docs/integrations/fastapi)
* [Ariadne](https://ariadnegraphql.org/)
* З [документацією для FastAPI](https://ariadnegraphql.org/docs/fastapi-integration)
* [Tartiflette](https://tartiflette.io/)
* З [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) для інтеграції з ASGI
* [Graphene](https://graphene-python.org/)
* З [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3)
## GraphQL зі Strawberry { #graphql-with-strawberry }
Якщо вам потрібен або ви хочете використовувати GraphQL, [Strawberry](https://strawberry.rocks/) - рекомендована бібліотека, адже її дизайн найближчий до дизайну FastAPI; усе базується на анотаціях типів.
Залежно від вашого сценарію використання ви можете надати перевагу іншій бібліотеці, але якби ви запитали мене, я, ймовірно, порадив би спробувати Strawberry.
Ось невеликий приклад того, як інтегрувати Strawberry з FastAPI:
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
Більше про Strawberry ви можете дізнатися в [документації Strawberry](https://strawberry.rocks/).
І також [документацію про Strawberry з FastAPI](https://strawberry.rocks/docs/integrations/fastapi).
## Застарілий `GraphQLApp` зі Starlette { #older-graphqlapp-from-starlette }
Попередні версії Starlette містили клас `GraphQLApp` для інтеграції з [Graphene](https://graphene-python.org/).
Його вилучено з Starlette як застарілий, але якщо у вас є код, що його використовував, ви можете легко мігрувати на [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3), який покриває той самий сценарій використання та має майже ідентичний інтерфейс.
/// tip | Порада
Якщо вам потрібен GraphQL, я все ж рекомендую звернути увагу на [Strawberry](https://strawberry.rocks/), адже він базується на анотаціях типів, а не на власних класах і типах.
///
## Дізнайтеся більше { #learn-more }
Ви можете дізнатися більше про GraphQL в [офіційній документації GraphQL](https://graphql.org/).
Також ви можете почитати більше про кожну з цих бібліотек за наведеними посиланнями.
================================================
FILE: docs/uk/docs/how-to/index.md
================================================
# Як зробити - Рецепти { #how-to-recipes }
Тут ви побачите різні рецепти або керівництва «як зробити» з **різних тем**.
Більшість із цих ідей є більш-менш **незалежними**, і в більшості випадків вам слід вивчати їх лише тоді, коли вони безпосередньо стосуються **вашого проєкту**.
Якщо щось здається цікавим і корисним для вашого проєкту, сміливо перевірте це, інакше, ймовірно, просто пропустіть.
/// tip | Порада
Якщо ви хочете **вивчити FastAPI** у структурований спосіб (рекомендується), натомість прочитайте [Навчальний посібник - Посібник користувача](../tutorial/index.md) розділ за розділом.
///
================================================
FILE: docs/uk/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
================================================
# Перехід з Pydantic v1 на Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }
Якщо у вас стара програма FastAPI, можливо, ви використовуєте Pydantic версії 1.
FastAPI версії 0.100.0 підтримував як Pydantic v1, так і v2. Використовувалася та версія, яку ви встановили.
FastAPI версії 0.119.0 запровадив часткову підтримку Pydantic v1 всередині Pydantic v2 (як `pydantic.v1`), щоб спростити перехід на v2.
FastAPI 0.126.0 припинив підтримку Pydantic v1, водночас ще певний час підтримував `pydantic.v1`.
/// warning | Попередження
Команда Pydantic припинила підтримку Pydantic v1 для останніх версій Python, починаючи з Python 3.14.
Це стосується і `pydantic.v1`, який більше не підтримується в Python 3.14 і новіших.
Якщо ви хочете використовувати найновіші можливості Python, вам потрібно переконатися, що ви використовуєте Pydantic v2.
///
Якщо у вас стара програма FastAPI з Pydantic v1, нижче я покажу, як мігрувати на Pydantic v2, а також можливості FastAPI 0.119.0, які допоможуть з поступовою міграцією.
## Офіційний посібник { #official-guide }
У Pydantic є офіційний [Посібник з міграції](https://docs.pydantic.dev/latest/migration/) з v1 на v2.
Там описано, що змінилося, як перевірки тепер стали коректнішими та суворішими, можливі застереження тощо.
Прочитайте його, щоб краще зрозуміти зміни.
## Тести { #tests }
Переконайтеся, що у вашій програмі є [тести](../tutorial/testing.md) і що ви запускаєте їх у системі безперервної інтеграції (CI).
Так ви зможете виконати оновлення і впевнитися, що все працює як очікується.
## `bump-pydantic` { #bump-pydantic }
У багатьох випадках, якщо ви використовуєте звичайні моделі Pydantic без налаштувань, більшу частину процесу міграції з Pydantic v1 на Pydantic v2 можна автоматизувати.
Ви можете скористатися [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) від тієї ж команди Pydantic.
Цей інструмент допоможе автоматично змінити більшість коду, який потрібно змінити.
Після цього запустіть тести й перевірте, чи все працює. Якщо так - ви все завершили. 😎
## Pydantic v1 у v2 { #pydantic-v1-in-v2 }
Pydantic v2 містить усе з Pydantic v1 як підмодуль `pydantic.v1`. Але це більше не підтримується у версіях Python вище 3.13.
Це означає, що ви можете встановити останню версію Pydantic v2 та імпортувати і використовувати старі компоненти Pydantic v1 з цього підмодуля, ніби у вас встановлено старий Pydantic v1.
{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}
### Підтримка FastAPI для Pydantic v1 у v2 { #fastapi-support-for-pydantic-v1-in-v2 }
Починаючи з FastAPI 0.119.0, також є часткова підтримка Pydantic v1 всередині Pydantic v2, щоб спростити перехід на v2.
Тож ви можете оновити Pydantic до останньої версії 2 і змінити імпорти на використання підмодуля `pydantic.v1`, і в багатьох випадках усе просто запрацює.
{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}
/// warning | Попередження
Майте на увазі, що оскільки команда Pydantic більше не підтримує Pydantic v1 у нових версіях Python, починаючи з Python 3.14, використання `pydantic.v1` також не підтримується в Python 3.14 і новіших.
///
### Pydantic v1 і v2 в одній програмі { #pydantic-v1-and-v2-on-the-same-app }
Pydantic не підтримує ситуацію, коли модель Pydantic v2 має власні поля, визначені як моделі Pydantic v1, або навпаки.
```mermaid
graph TB
subgraph "❌ Not Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V1Field["Pydantic v1 Model"]
end
subgraph V1["Pydantic v1 Model"]
V2Field["Pydantic v2 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
...але в одній програмі ви можете мати окремі моделі на Pydantic v1 і v2.
```mermaid
graph TB
subgraph "✅ Supported"
direction TB
subgraph V2["Pydantic v2 Model"]
V2Field["Pydantic v2 Model"]
end
subgraph V1["Pydantic v1 Model"]
V1Field["Pydantic v1 Model"]
end
end
style V2 fill:#f9fff3
style V1 fill:#fff6f0
style V1Field fill:#fff6f0
style V2Field fill:#f9fff3
```
У деяких випадках можна навіть використовувати моделі і Pydantic v1, і v2 в одній операції шляху у вашій програмі FastAPI:
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
У наведеному вище прикладі вхідна модель - це модель Pydantic v1, а вихідна модель (визначена як `response_model=ItemV2`) - модель Pydantic v2.
### Параметри Pydantic v1 { #pydantic-v1-parameters }
Якщо вам потрібно використовувати деякі специфічні для FastAPI інструменти для параметрів, як-от `Body`, `Query`, `Form` тощо, з моделями Pydantic v1, ви можете імпортувати їх з `fastapi.temp_pydantic_v1_params`, поки завершуєте міграцію на Pydantic v2:
{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}
### Покрокова міграція { #migrate-in-steps }
/// tip | Порада
Спершу спробуйте `bump-pydantic`: якщо ваші тести проходять і все працює - ви впоралися однією командою. ✨
///
Якщо `bump-pydantic` не підходить для вашого випадку, скористайтеся підтримкою одночасно Pydantic v1 і v2 в одній програмі, щоб виконати поступову міграцію на Pydantic v2.
Спочатку ви можете оновити Pydantic до останньої версії 2 і змінити імпорти на `pydantic.v1` для всіх ваших моделей.
Потім починайте переносити моделі з Pydantic v1 на v2 групами, поступовими кроками. 🚶
================================================
FILE: docs/uk/docs/how-to/separate-openapi-schemas.md
================================================
# Окремі схеми OpenAPI для введення та виведення, чи ні { #separate-openapi-schemas-for-input-and-output-or-not }
Відколи вийшов **Pydantic v2**, згенерований OpenAPI став трохи точнішим і більш коректним, ніж раніше. 😎
Насправді подекуди буде навіть **дві схеми JSON** в OpenAPI для тієї самої моделі Pydantic: для введення та для виведення - залежно від наявності значень за замовчуванням.
Розгляньмо, як це працює, і як це змінити за потреби.
## Моделі Pydantic для введення та виведення { #pydantic-models-for-input-and-output }
Припустімо, у вас є модель Pydantic зі значеннями за замовчуванням, наприклад:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *}
### Модель для введення { #model-for-input }
Якщо ви використовуєте цю модель як введення, як тут:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *}
…тоді поле `description` не буде обов'язковим, адже воно має значення за замовчуванням `None`.
### Модель для введення в документації { #input-model-in-docs }
У документації ви побачите, що поле `description` не має **червоної зірочки** - воно не позначене як обов'язкове:
### Модель для виведення { #model-for-output }
Але якщо ви використовуєте цю саму модель для виведення, як тут:
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
…тоді, оскільки `description` має значення за замовчуванням, якщо ви нічого не повернете для цього поля, воно все одно матиме це **значення за замовчуванням**.
### Модель для даних відповіді при виведенні { #model-for-output-response-data }
Якщо ви скористаєтеся документацією та перевірите відповідь, навіть попри те, що код нічого не додав в одне з полів `description`, JSON-відповідь містить значення за замовчуванням (`null`):
Це означає, що воно **завжди матиме значення** - просто іноді це значення може бути `None` (або `null` у JSON).
Отже, клієнтам, які використовують ваш API, не потрібно перевіряти, чи існує значення - вони можуть **припустити, що поле завжди буде присутнє**, але в окремих випадках воно матиме значення за замовчуванням `None`.
У OpenAPI це описується тим, що поле позначається як **обов'язкове**, адже воно завжди присутнє.
Тому Схема JSON для моделі може відрізнятися залежно від того, чи використовується вона для **введення або виведення**:
- для **введення** `description` не буде обов'язковим
- для **виведення** воно буде **обов'язковим** (і можливо `None`, або в термінах JSON - `null`)
### Модель для виведення в документації { #model-for-output-in-docs }
У документації ви також можете перевірити модель для виведення: **і** `name`, і `description` позначені як **обов'язкові** червоною зірочкою:
### Модель для введення та виведення в документації { #model-for-input-and-output-in-docs }
Якщо відкрити всі наявні Схеми (схеми JSON) в OpenAPI, то ви побачите дві: `Item-Input` і `Item-Output`.
Для `Item-Input` поле `description` **не є обов'язковим** - червоної зірочки немає.
А для `Item-Output` `description` **є обов'язковим** - є червона зірочка.
Завдяки цій можливості у **Pydantic v2** ваша документація API стає більш **точною**, а якщо у вас є згенеровані клієнти та SDK, вони також будуть точнішими - з кращим **досвідом розробника** та узгодженістю. 🎉
## Не розділяти схеми { #do-not-separate-schemas }
Втім іноді може знадобитися мати **одну й ту саму схему для введення та виведення**.
Основний випадок - коли у вас вже є згенерований клієнтський код/SDK, і ви поки не бажаєте оновлювати весь такий згенерований код/SDK. Ймовірно, ви зробите це пізніше, але не зараз.
У такому разі ви можете вимкнути цю можливість у **FastAPI** параметром `separate_input_output_schemas=False`.
/// info | Інформація
Підтримку `separate_input_output_schemas` додано у FastAPI `0.102.0`. 🤓
///
{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *}
### Одна схема для моделей введення та виведення в документації { #same-schema-for-input-and-output-models-in-docs }
Тепер для цієї моделі буде лише одна спільна схема для введення та виведення - тільки `Item`, і в ній `description` буде **не обов'язковим**:
================================================
FILE: docs/uk/docs/how-to/testing-database.md
================================================
# Тестування бази даних { #testing-a-database }
Ви можете ознайомитися з базами даних, SQL і SQLModel у [документації SQLModel](https://sqlmodel.tiangolo.com/). 🤓
Є невеликий [навчальний посібник про використання SQLModel з FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/). ✨
Цей навчальний посібник містить розділ про [тестування баз даних SQL](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/). 😎
================================================
FILE: docs/uk/docs/index.md
================================================
# FastAPI { #fastapi }
Фреймворк FastAPI - це висока продуктивність, легко вивчати, швидко писати код, готовий до продакшину
---
**Документація**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com/uk)
**Вихідний код**: [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi)
---
FastAPI - це сучасний, швидкий (високопродуктивний) вебфреймворк для створення API за допомогою Python, що базується на стандартних підказках типів Python.
Ключові особливості:
* **Швидкий**: дуже висока продуктивність, на рівні з **NodeJS** та **Go** (завдяки Starlette та Pydantic). [Один із найшвидших Python-фреймворків](#performance).
* **Швидке написання коду**: пришвидшує розробку функціоналу приблизно на 200%–300%. *
* **Менше помилок**: зменшує приблизно на 40% кількість помилок, спричинених людиною (розробником). *
* **Інтуїтивний**: чудова підтримка редакторами коду. Автодоповнення всюди. Менше часу на налагодження.
* **Простий**: спроєктований так, щоб бути простим у використанні та вивченні. Менше часу на читання документації.
* **Короткий**: мінімізує дублювання коду. Кілька можливостей з кожного оголошення параметра. Менше помилок.
* **Надійний**: ви отримуєте код, готовий до продакшину. З автоматичною інтерактивною документацією.
* **Заснований на стандартах**: базується на (і повністю сумісний з) відкритими стандартами для API: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) (раніше відомий як Swagger) та [JSON Schema](https://json-schema.org/).
* оцінка на основі тестів, проведених внутрішньою командою розробників, що створює продакшн-застосунки.
## Спонсори { #sponsors }
### Ключовий спонсор { #keystone-sponsor }
{% for sponsor in sponsors.keystone -%}
{% endfor -%}
### Золоті та срібні спонсори { #gold-and-silver-sponsors }
{% for sponsor in sponsors.gold -%}
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
{% endfor %}
[Інші спонсори](https://fastapi.tiangolo.com/uk/fastapi-people/#sponsors)
## Враження { #opinions }
"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._"
---
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber(ref)
---
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix(ref)
---
"_I’m over the moon excited about **FastAPI**. It’s so fun!_"
Brian Okken - [Python Bytes](https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855) podcast host(ref)
---
"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._"
---
"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_"
"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_"
---
"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._"
---
## Міні-документальний фільм про FastAPI { #fastapi-mini-documentary }
Наприкінці 2025 року вийшов [міні-документальний фільм про FastAPI](https://www.youtube.com/watch?v=mpR8ngthqiE), ви можете переглянути його онлайн:
## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis }
Якщо ви створюєте застосунок CLI для використання в терміналі замість веб-API, зверніть увагу на [**Typer**](https://typer.tiangolo.com/).
**Typer** - молодший брат FastAPI. І його задумано як **FastAPI для CLI**. ⌨️ 🚀
## Вимоги { #requirements }
FastAPI стоїть на плечах гігантів:
* [Starlette](https://www.starlette.dev/) для вебчастини.
* [Pydantic](https://docs.pydantic.dev/) для частини даних.
## Встановлення { #installation }
Створіть і активуйте [віртуальне середовище](https://fastapi.tiangolo.com/uk/virtual-environments/), а потім встановіть FastAPI:
**Примітка**: переконайтеся, що ви взяли `"fastapi[standard]"` у лапки, щоб це працювало в усіх терміналах.
## Приклад { #example }
### Створіть { #create-it }
Створіть файл `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, q: str | None = None):
return {"item_id": item_id, "q": q}
```
Або використайте async def...
Якщо ваш код використовує `async` / `await`, скористайтеся `async def`:
```Python hl_lines="7 12"
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
```
**Примітка**:
Якщо ви не знаєте, перегляньте розділ _"In a hurry?"_ про [`async` та `await` у документації](https://fastapi.tiangolo.com/uk/async/#in-a-hurry).
### Запустіть { #run-it }
Запустіть сервер за допомогою:
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
Про команду fastapi dev...
Команда `fastapi dev` автоматично читає ваш файл `main.py`, знаходить у ньому застосунок **FastAPI** і запускає сервер за допомогою [Uvicorn](https://www.uvicorn.dev).
За замовчуванням `fastapi dev` запускається з авто-перезавантаженням для локальної розробки.
Докладніше читайте в [документації FastAPI CLI](https://fastapi.tiangolo.com/uk/fastapi-cli/).
### Перевірте { #check-it }
Відкрийте браузер і перейдіть на [http://127.0.0.1:8000/items/5?q=somequery](http://127.0.0.1:8000/items/5?q=somequery).
Ви побачите JSON-відповідь:
```JSON
{"item_id": 5, "q": "somequery"}
```
Ви вже створили API, який:
* Отримує HTTP-запити за _шляхами_ `/` та `/items/{item_id}`.
* Обидва _шляхи_ приймають `GET` операції (також відомі як HTTP _методи_).
* _Шлях_ `/items/{item_id}` містить _параметр шляху_ `item_id`, який має бути типу `int`.
* _Шлях_ `/items/{item_id}` містить необовʼязковий `str` _параметр запиту_ `q`.
### Інтерактивна документація API { #interactive-api-docs }
Тепер перейдіть на [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите автоматичну інтерактивну документацію API (надану [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Альтернативна документація API { #alternative-api-docs }
А тепер перейдіть на [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Ви побачите альтернативну автоматичну документацію (надану [ReDoc](https://github.com/Rebilly/ReDoc)):

## Приклад оновлення { #example-upgrade }
Тепер змініть файл `main.py`, щоб отримувати тіло `PUT`-запиту.
Оголосіть тіло, використовуючи стандартні типи Python, завдяки Pydantic.
```Python hl_lines="2 7-10 23-25"
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```
Сервер `fastapi dev` має автоматично перезавантажитися.
### Оновлення інтерактивної документації API { #interactive-api-docs-upgrade }
Тепер перейдіть на [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
* Інтерактивна документація API буде автоматично оновлена, включно з новим тілом:

* Натисніть кнопку "Try it out", вона дозволяє заповнити параметри та безпосередньо взаємодіяти з API:

* Потім натисніть кнопку "Execute", інтерфейс користувача зв'яжеться з вашим API, надішле параметри, отримає результати та покаже їх на екрані:

### Оновлення альтернативної документації API { #alternative-api-docs-upgrade }
А тепер перейдіть на [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
* Альтернативна документація також відобразить новий параметр запиту та тіло:

### Підсумки { #recap }
Отже, ви оголошуєте **один раз** типи параметрів, тіла тощо як параметри функції.
Ви робите це за допомогою стандартних сучасних типів Python.
Вам не потрібно вивчати новий синтаксис, методи чи класи конкретної бібліотеки тощо.
Лише стандартний **Python**.
Наприклад, для `int`:
```Python
item_id: int
```
або для складнішої моделі `Item`:
```Python
item: Item
```
...і з цим єдиним оголошенням ви отримуєте:
* Підтримку редактора, включно з:
* Автодоповненням.
* Перевіркою типів.
* Валідацію даних:
* Автоматичні та зрозумілі помилки, коли дані некоректні.
* Валідацію навіть для глибоко вкладених JSON-обʼєктів.
* Перетворення вхідних даних: з мережі до даних і типів Python. Читання з:
* JSON.
* Параметрів шляху.
* Параметрів запиту.
* Cookies.
* Headers.
* Forms.
* Files.
* Перетворення вихідних даних: перетворення з даних і типів Python у мережеві дані (як JSON):
* Перетворення типів Python (`str`, `int`, `float`, `bool`, `list`, тощо).
* Обʼєктів `datetime`.
* Обʼєктів `UUID`.
* Моделей бази даних.
* ...та багато іншого.
* Автоматичну інтерактивну документацію API, включно з 2 альтернативними інтерфейсами користувача:
* Swagger UI.
* ReDoc.
---
Повертаючись до попереднього прикладу коду, **FastAPI**:
* Перевірить, що `item_id` є у шляху для `GET` та `PUT`-запитів.
* Перевірить, що `item_id` має тип `int` для `GET` та `PUT`-запитів.
* Якщо це не так, клієнт побачить корисну, зрозумілу помилку.
* Перевірить, чи є необов'язковий параметр запиту з назвою `q` (як у `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET`-запитів.
* Оскільки параметр `q` оголошено як `= None`, він необов'язковий.
* Без `None` він був би обов'язковим (як і тіло у випадку з `PUT`).
* Для `PUT`-запитів до `/items/{item_id}` прочитає тіло як JSON:
* Перевірить, що є обовʼязковий атрибут `name`, який має бути типу `str`.
* Перевірить, що є обовʼязковий атрибут `price`, який має бути типу `float`.
* Перевірить, що є необовʼязковий атрибут `is_offer`, який має бути типу `bool`, якщо він присутній.
* Усе це також працюватиме для глибоко вкладених JSON-обʼєктів.
* Автоматично перетворюватиме з та в JSON.
* Документуватиме все за допомогою OpenAPI, який може бути використано в:
* Інтерактивних системах документації.
* Системах автоматичної генерації клієнтського коду для багатьох мов.
* Надаватиме безпосередньо 2 вебінтерфейси інтерактивної документації.
---
Ми лише трішки доторкнулися до поверхні, але ви вже маєте уявлення про те, як усе працює.
Спробуйте змінити рядок:
```Python
return {"item_name": item.name, "item_id": item_id}
```
...із:
```Python
... "item_name": item.name ...
```
...на:
```Python
... "item_price": item.price ...
```
...і побачите, як ваш редактор автоматично доповнюватиме атрибути та знатиме їхні типи:

Для більш повного прикладу, що включає більше можливостей, перегляньте Навчальний посібник - Посібник користувача.
**Попередження про спойлер**: навчальний посібник - посібник користувача містить:
* Оголошення **параметрів** з інших різних місць, як-от: **headers**, **cookies**, **form fields** та **files**.
* Як встановлювати **обмеження валідації** як `maximum_length` або `regex`.
* Дуже потужну і просту у використанні систему **Впровадження залежностей**.
* Безпеку та автентифікацію, включно з підтримкою **OAuth2** з **JWT tokens** та **HTTP Basic** auth.
* Досконаліші (але однаково прості) техніки для оголошення **глибоко вкладених моделей JSON** (завдяки Pydantic).
* Інтеграцію **GraphQL** з [Strawberry](https://strawberry.rocks) та іншими бібліотеками.
* Багато додаткових можливостей (завдяки Starlette) як-от:
* **WebSockets**
* надзвичайно прості тести на основі HTTPX та `pytest`
* **CORS**
* **Cookie Sessions**
* ...та більше.
### Розгортання застосунку (необовʼязково) { #deploy-your-app-optional }
За бажання ви можете розгорнути ваш застосунок FastAPI у [FastAPI Cloud](https://fastapicloud.com), перейдіть і приєднайтеся до списку очікування, якщо ви ще цього не зробили. 🚀
Якщо у вас вже є обліковий запис **FastAPI Cloud** (ми запросили вас зі списку очікування 😉), ви можете розгорнути ваш застосунок однією командою.
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Ось і все! Тепер ви можете отримати доступ до вашого застосунку за цією URL-адресою. ✨
#### Про FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** створено тим самим автором і командою, що стоять за **FastAPI**.
Він спрощує процес **створення**, **розгортання** та **доступу** до API з мінімальними зусиллями.
Він забезпечує той самий **developer experience** створення застосунків на FastAPI під час їх **розгортання** у хмарі. 🎉
FastAPI Cloud - основний спонсор і джерело фінансування open source проєктів *FastAPI and friends*. ✨
#### Розгортання в інших хмарних провайдерів { #deploy-to-other-cloud-providers }
FastAPI - open source проект і базується на стандартах. Ви можете розгортати застосунки FastAPI в будь-якому хмарному провайдері, який ви оберете.
Дотримуйтеся інструкцій вашого хмарного провайдера, щоб розгорнути застосунки FastAPI у нього. 🤓
## Продуктивність { #performance }
Незалежні тести TechEmpower показують застосунки **FastAPI**, які працюють під керуванням Uvicorn, як [одні з найшвидших доступних Python-фреймворків](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7), поступаючись лише Starlette та Uvicorn (які внутрішньо використовуються в FastAPI). (*)
Щоб дізнатися більше, перегляньте розділ [Benchmarks](https://fastapi.tiangolo.com/uk/benchmarks/).
## Залежності { #dependencies }
FastAPI залежить від Pydantic і Starlette.
### Залежності `standard` { #standard-dependencies }
Коли ви встановлюєте FastAPI за допомогою `pip install "fastapi[standard]"`, ви отримуєте групу необовʼязкових залежностей `standard`:
Використовується Pydantic:
* [`email-validator`](https://github.com/JoshData/python-email-validator) - для валідації електронної пошти.
Використовується Starlette:
* [`httpx`](https://www.python-httpx.org) - потрібно, якщо ви хочете використовувати `TestClient`.
* [`jinja2`](https://jinja.palletsprojects.com) - потрібно, якщо ви хочете використовувати конфігурацію шаблонів за замовчуванням.
* [`python-multipart`](https://github.com/Kludex/python-multipart) - потрібно, якщо ви хочете підтримувати форми з «парсингом» через `request.form()`.
Використовується FastAPI:
* [`uvicorn`](https://www.uvicorn.dev) - для сервера, який завантажує та обслуговує ваш застосунок. Це включає `uvicorn[standard]`, до якого входять деякі залежності (наприклад, `uvloop`), потрібні для високопродуктивної роботи сервера.
* `fastapi-cli[standard]` - щоб надати команду `fastapi`.
* Це включає `fastapi-cloud-cli`, який дозволяє розгортати ваш застосунок FastAPI у [FastAPI Cloud](https://fastapicloud.com).
### Без залежностей `standard` { #without-standard-dependencies }
Якщо ви не хочете включати необовʼязкові залежності `standard`, ви можете встановити через `pip install fastapi` замість `pip install "fastapi[standard]"`.
### Без `fastapi-cloud-cli` { #without-fastapi-cloud-cli }
Якщо ви хочете встановити FastAPI зі стандартними залежностями, але без `fastapi-cloud-cli`, ви можете встановити через `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
### Додаткові необовʼязкові залежності { #additional-optional-dependencies }
Є ще деякі додаткові залежності, які ви можете захотіти встановити.
Додаткові необовʼязкові залежності Pydantic:
* [`pydantic-settings`](https://docs.pydantic.dev/latest/usage/pydantic_settings/) - для керування налаштуваннями.
* [`pydantic-extra-types`](https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/) - для додаткових типів, що можуть бути використані з Pydantic.
Додаткові необовʼязкові залежності FastAPI:
* [`orjson`](https://github.com/ijl/orjson) - потрібно, якщо ви хочете використовувати `ORJSONResponse`.
* [`ujson`](https://github.com/esnme/ultrajson) - потрібно, якщо ви хочете використовувати `UJSONResponse`.
## Ліцензія { #license }
Цей проєкт ліцензовано згідно з умовами ліцензії MIT.
================================================
FILE: docs/uk/docs/learn/index.md
================================================
# Навчання { #learn }
У цьому розділі надані вступні розділи та навчальні посібники для вивчення **FastAPI**.
Це можна розглядати як **книгу**, **курс**, або **офіційний** та рекомендований спосіб освоїти FastAPI. 😎
================================================
FILE: docs/uk/docs/project-generation.md
================================================
# Шаблон Full Stack FastAPI { #full-stack-fastapi-template }
Шаблони, хоча зазвичай постачаються з певним налаштуванням, спроєктовані бути гнучкими та налаштовуваними. Це дає змогу змінювати їх і адаптувати до вимог вашого проєкту, що робить їх чудовою відправною точкою. 🏁
Ви можете використати цей шаблон для старту, адже в ньому вже виконано значну частину початкового налаштування, безпеки, роботи з базою даних і деяких кінцевих точок API.
Репозиторій GitHub: [Шаблон Full Stack FastAPI](https://github.com/tiangolo/full-stack-fastapi-template)
## Шаблон Full Stack FastAPI - стек технологій і можливості { #full-stack-fastapi-template-technology-stack-and-features }
- ⚡ [**FastAPI**](https://fastapi.tiangolo.com/uk) для бекенд API на Python.
- 🧰 [SQLModel](https://sqlmodel.tiangolo.com) для взаємодії з SQL-базою даних у Python (ORM).
- 🔍 [Pydantic](https://docs.pydantic.dev), який використовується FastAPI, для перевірки даних і керування налаштуваннями.
- 💾 [PostgreSQL](https://www.postgresql.org) як SQL-база даних.
- 🚀 [React](https://react.dev) для фронтенду.
- 💃 Використання TypeScript, хуків, Vite та інших частин сучасного фронтенд-стеку.
- 🎨 [Tailwind CSS](https://tailwindcss.com) і [shadcn/ui](https://ui.shadcn.com) для фронтенд-компонентів.
- 🤖 Автоматично згенерований фронтенд-клієнт.
- 🧪 [Playwright](https://playwright.dev) для End-to-End тестування.
- 🦇 Підтримка темного режиму.
- 🐋 [Docker Compose](https://www.docker.com) для розробки та продакшену.
- 🔒 Безпечне хешування паролів за замовчуванням.
- 🔑 Автентифікація JWT (JSON Web Token).
- 📫 Відновлення пароля на основі електронної пошти.
- ✅ Тести з [Pytest](https://pytest.org).
- 📞 [Traefik](https://traefik.io) як зворотний представник / балансувальник навантаження.
- 🚢 Інструкції з розгортання з Docker Compose, включно з налаштуванням фронтенд-представника Traefik для автоматичних HTTPS-сертифікатів.
- 🏭 CI (безперервна інтеграція) і CD (безперервне розгортання) на базі GitHub Actions.
================================================
FILE: docs/uk/docs/python-types.md
================================================
# Вступ до типів Python { #python-types-intro }
Python підтримує додаткові «підказки типів» (також звані «анотаціями типів»).
Ці **«підказки типів»** або анотації — це спеціальний синтаксис, що дозволяє оголошувати тип змінної.
За допомогою оголошення типів для ваших змінних редактори та інструменти можуть надати вам кращу підтримку.
Це лише **швидкий навчальний посібник / нагадування** про підказки типів у Python. Він покриває лише мінімум, необхідний щоб використовувати їх з **FastAPI**... що насправді дуже мало.
**FastAPI** повністю базується на цих підказках типів, вони дають йому багато переваг і користі.
Але навіть якщо ви ніколи не використаєте **FastAPI**, вам буде корисно дізнатись трохи про них.
/// note | Примітка
Якщо ви експерт у Python і ви вже знаєте все про підказки типів, перейдіть до наступного розділу.
///
## Мотивація { #motivation }
Давайте почнемо з простого прикладу:
{* ../../docs_src/python_types/tutorial001_py310.py *}
Виклик цієї програми виводить:
```
John Doe
```
Функція виконує наступне:
* Бере `first_name` та `last_name`.
* Перетворює першу літеру кожного з них у верхній регістр за допомогою `title()`.
* Конкатенує їх разом із пробілом по середині.
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
### Редагуйте це { #edit-it }
Це дуже проста програма.
Але тепер уявіть, що ви писали це з нуля.
У певний момент ви розпочали б визначення функції, у вас були б готові параметри...
Але тоді вам потрібно викликати «той метод, який перетворює першу літеру у верхній регістр».
Це буде `upper`? Чи `uppercase`? `first_uppercase`? `capitalize`?
Тоді ви спробуєте давнього друга програміста — автозаповнення редактора коду.
Ви надрукуєте перший параметр функції, `first_name`, тоді крапку (`.`), а тоді натиснете `Ctrl+Space`, щоб запустити автозаповнення.
Але, на жаль, ви не отримаєте нічого корисного:
### Додайте типи { #add-types }
Давайте змінимо один рядок з попередньої версії.
Ми змінимо саме цей фрагмент, параметри функції, з:
```Python
first_name, last_name
```
на:
```Python
first_name: str, last_name: str
```
Ось і все.
Це «підказки типів»:
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
Це не те саме, що оголошення значень за замовчуванням, як це було б з:
```Python
first_name="john", last_name="doe"
```
Це зовсім інше.
Ми використовуємо двокрапку (`:`), не знак дорівнює (`=`).
І додавання підказок типів зазвичай не змінює того, що відбувається, порівняно з тим, що відбувалося б без них.
Але тепер уявіть, що ви знову посеред процесу створення функції, але з підказками типів.
У той самий момент ви спробуєте викликати автозаповнення за допомогою `Ctrl+Space` і побачите:
Разом з цим ви можете прокручувати, переглядаючи опції, допоки не знайдете ту, що «щось вам підказує»:
## Більше мотивації { #more-motivation }
Перевірте цю функцію, вона вже має підказки типів:
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
Оскільки редактор знає типи змінних, ви не тільки отримаєте автозаповнення, ви також отримаєте перевірку помилок:
Тепер ви знаєте, щоб виправити це, вам потрібно перетворити `age` у строку за допомогою `str(age)`:
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
## Оголошення типів { #declaring-types }
Щойно ви побачили основне місце для оголошення підказок типів. Як параметри функції.
Це також основне місце, де ви б їх використовували у **FastAPI**.
### Прості типи { #simple-types }
Ви можете оголошувати усі стандартні типи у Python, не тільки `str`.
Ви можете використовувати, наприклад:
* `int`
* `float`
* `bool`
* `bytes`
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
### Модуль `typing` { #typing-module }
Для деяких додаткових випадків використання може знадобитися імпортувати елементи зі стандартної бібліотеки, модуля `typing`. Наприклад, коли ви хочете оголосити, що щось має «будь-який тип», ви можете використати `Any` з `typing`:
```python
from typing import Any
def some_function(data: Any):
print(data)
```
### Generic типи { #generic-types }
Деякі типи можуть приймати «параметри типів» у квадратних дужках, щоб визначити їх внутрішні типи. Наприклад, «list строк» буде оголошений як `list[str]`.
Ці типи, які можуть приймати параметри типів, називаються **generic типами** або **generics**.
Ви можете використовувати ті самі вбудовані типи як generics (з квадратними дужками та типами всередині):
* `list`
* `tuple`
* `set`
* `dict`
#### List { #list }
Наприклад, давайте визначимо змінну, яка буде `list` із `str`.
Оголосіть змінну з тим самим синтаксисом двокрапки (`:`).
Як тип вкажіть `list`.
Оскільки список є типом, який містить деякі внутрішні типи, ви поміщаєте їх у квадратні дужки:
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
/// info | Інформація
Ці внутрішні типи в квадратних дужках називаються «параметрами типу».
У цьому випадку `str` — це параметр типу, переданий у `list`.
///
Це означає: «змінна `items` — це `list`, і кожен з елементів у цьому списку — `str`».
Зробивши це, ваш редактор може надати підтримку навіть під час обробки елементів зі списку:
Без типів цього майже неможливо досягти.
Зверніть увагу, що змінна `item` є одним із елементів у списку `items`.
І все ж редактор знає, що це `str`, і надає підтримку для цього.
#### Tuple and Set { #tuple-and-set }
Ви повинні зробити те ж саме, щоб оголосити `tuple` і `set`:
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
Це означає:
* Змінна `items_t` — це `tuple` з 3 елементами: `int`, ще `int`, та `str`.
* Змінна `items_s` — це `set`, і кожен його елемент має тип `bytes`.
#### Dict { #dict }
Щоб оголосити `dict`, вам потрібно передати 2 параметри типу, розділені комами.
Перший параметр типу для ключів у `dict`.
Другий параметр типу для значень у `dict`:
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
Це означає:
* Змінна `prices` — це `dict`:
* Ключі цього `dict` мають тип `str` (скажімо, назва кожного предмета).
* Значення цього `dict` мають тип `float` (скажімо, ціна кожного предмета).
#### Union { #union }
Ви можете оголосити, що змінна може бути будь-яким із **кількох типів**, наприклад `int` або `str`.
Щоб визначити це, використовуйте вертикальну риску (`|`), щоб розділити обидва типи.
Це називається «union», тому що змінна може бути чимось із об’єднання цих двох множин типів.
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```
Це означає, що `item` може бути `int` або `str`.
#### Можливо `None` { #possibly-none }
Ви можете оголосити, що значення може мати тип, наприклад `str`, але також може бути `None`.
//// tab | Python 3.10+
```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```
////
Використання `str | None` замість просто `str` дозволить редактору допомогти вам виявити помилки, коли ви могли б вважати, що значенням завжди є `str`, хоча насправді воно також може бути `None`.
### Класи як типи { #classes-as-types }
Ви також можете оголосити клас як тип змінної.
Скажімо, у вас є клас `Person` з імʼям:
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
Потім ви можете оголосити змінну типу `Person`:
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
І знову ж таки, ви отримуєте всю підтримку редактора:
Зверніть увагу, що це означає: «`one_person` — це **екземпляр** класу `Person`».
Це не означає: «`one_person` — це **клас** з назвою `Person`».
## Pydantic моделі { #pydantic-models }
[Pydantic](https://docs.pydantic.dev/) — це бібліотека Python для валідації даних.
Ви оголошуєте «форму» даних як класи з атрибутами.
І кожен атрибут має тип.
Потім ви створюєте екземпляр цього класу з деякими значеннями, і він перевірить ці значення, перетворить їх у відповідний тип (якщо є потреба) і надасть вам об’єкт з усіма даними.
І ви отримуєте всю підтримку редактора з цим отриманим об’єктом.
Приклад з офіційної документації Pydantic:
{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Інформація
Щоб дізнатись більше про [Pydantic, перегляньте його документацію](https://docs.pydantic.dev/).
///
**FastAPI** повністю базується на Pydantic.
Ви побачите набагато більше цього всього на практиці в [Навчальний посібник - Посібник користувача](tutorial/index.md).
## Підказки типів з анотаціями метаданих { #type-hints-with-metadata-annotations }
У Python також є можливість додавати **додаткові метадані** до цих підказок типів за допомогою `Annotated`.
Ви можете імпортувати `Annotated` з `typing`.
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
Сам Python нічого не робить із цим `Annotated`. А для редакторів та інших інструментів тип усе ще є `str`.
Але ви можете використати це місце в `Annotated`, щоб надати **FastAPI** додаткові метадані про те, як ви хочете, щоб ваш застосунок поводився.
Важливо пам’ятати, що **перший *параметр типу***, який ви передаєте в `Annotated`, — це **фактичний тип**. Решта — це лише метадані для інших інструментів.
Наразі вам просто потрібно знати, що `Annotated` існує і що це стандартний Python. 😎
Пізніше ви побачите, наскільки **потужним** це може бути.
/// tip | Порада
Той факт, що це **стандартний Python**, означає, що ви й надалі отримуватимете **найкращий можливий досвід розробки** у вашому редакторі, з інструментами, які ви використовуєте для аналізу та рефакторингу коду тощо. ✨
А також те, що ваш код буде дуже сумісним із багатьма іншими інструментами та бібліотеками Python. 🚀
///
## Підказки типів у **FastAPI** { #type-hints-in-fastapi }
**FastAPI** використовує ці підказки типів для виконання кількох речей.
З **FastAPI** ви оголошуєте параметри з підказками типів, і отримуєте:
* **Підтримку редактора**.
* **Перевірку типів**.
...і **FastAPI** використовує ті самі оголошення для:
* **Визначення вимог**: з параметрів шляху запиту, параметрів запиту, заголовків, тіл, залежностей тощо.
* **Перетворення даних**: із запиту в необхідний тип.
* **Перевірки даних**: що надходять від кожного запиту:
* Генерування **автоматичних помилок**, що повертаються клієнту, коли дані недійсні.
* **Документування** API за допомогою OpenAPI:
* який потім використовується для автоматичної інтерактивної документації користувальницьких інтерфейсів.
Все це може здатися абстрактним. Не хвилюйтеся. Ви побачите все це в дії в [Навчальний посібник - Посібник користувача](tutorial/index.md).
Важливо те, що за допомогою стандартних типів Python в одному місці (замість того, щоб додавати більше класів, декораторів тощо), **FastAPI** зробить багато роботи за вас.
/// info | Інформація
Якщо ви вже пройшли весь навчальний посібник і повернулися, щоб дізнатися більше про типи, ось хороший ресурс: [«шпаргалка» від `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
///
================================================
FILE: docs/uk/docs/resources/index.md
================================================
# Ресурси { #resources }
Додаткові ресурси, зовнішні посилання та інше. ✈️
================================================
FILE: docs/uk/docs/translation-banner.md
================================================
/// details | 🌐 Переклад ШІ та людьми
Цей переклад виконано ШІ під керівництвом людей. 🤝
Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖
Ви можете покращити цей переклад, [допомігши нам краще спрямовувати AI LLM](https://fastapi.tiangolo.com/uk/contributing/#translations).
[Англійська версія](ENGLISH_VERSION_URL)
///
================================================
FILE: docs/uk/docs/tutorial/background-tasks.md
================================================
# Фонові задачі { #background-tasks }
Ви можете створювати фонові задачі, які будуть виконуватися після повернення відповіді.
Це корисно для операцій, які потрібно виконати після обробки запиту, але клієнту не обов’язково чекати завершення цієї операції перед отриманням відповіді.
Це включає, наприклад:
* Надсилання email-сповіщень після виконання певної дії:
* Підключення до поштового сервера та надсилання листа може займати кілька секунд. Ви можете відразу повернути відповідь, а email-сповіщення надіслати у фоні.
* Обробка даних:
* Наприклад, якщо ви отримали файл, який потрібно обробити довготривалим процесом, можна повернути відповідь «Accepted» (HTTP 202) і виконати обробку файлу у фоні.
## Використання `BackgroundTasks` { #using-backgroundtasks }
Спочатку імпортуйте `BackgroundTasks` і оголосіть параметр у вашій функції операції шляху з анотацією типу `BackgroundTasks`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
**FastAPI** створить для вас об’єкт типу `BackgroundTasks` і передасть його як цей параметр.
## Створення функції задачі { #create-a-task-function }
Створіть функцію, яка буде виконуватися як фонова задача.
Це звичайна функція, яка може отримувати параметри.
Вона може бути асинхронною `async def` або звичайною `def` функцією – **FastAPI** обробить її правильно.
У нашому випадку функція записує у файл (імітуючи надсилання email).
І оскільки операція запису не використовує `async` та `await`, ми визначаємо функцію як звичайну `def`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
## Додавання фонової задачі { #add-the-background-task }
Усередині вашої функції операції шляху, передайте функцію задачі в об'єкт background tasks, використовуючи метод `.add_task()`:
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
`.add_task()` приймає аргументи:
* Функцію задачі, яка буде виконуватися у фоновому режимі (`write_notification`).
* Будь-яку послідовність аргументів, які потрібно передати у функцію задачі у відповідному порядку (`email`).
* Будь-які іменовані аргументи, які потрібно передати у функцію задачі (`message="some notification"`).
## Впровадження залежностей { #dependency-injection }
Використання `BackgroundTasks` також працює з системою впровадження залежностей. Ви можете оголосити параметр типу `BackgroundTasks` на різних рівнях: у функції операції шляху, у залежності (залежний), у підзалежності тощо.
**FastAPI** знає, як діяти в кожному випадку і як повторно використовувати один і той самий об'єкт, щоб усі фонові задачі були об’єднані та виконувалися у фоновому режимі після завершення основного запиту:
{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *}
У цьому прикладі повідомлення будуть записані у файл `log.txt` після того, як відповідь буде надіслана.
Якщо у запиті був переданий параметр запиту, він буде записаний у лог у фоновій задачі.
А потім інша фонова задача, згенерована у функції операції шляху, запише повідомлення з використанням параметра шляху `email`.
## Технічні деталі { #technical-details }
Клас `BackgroundTasks` походить безпосередньо з [`starlette.background`](https://www.starlette.dev/background/).
Він імпортується/включається безпосередньо у FastAPI, щоб ви могли імпортувати його з `fastapi` і випадково не імпортували альтернативний `BackgroundTask` (без `s` в кінці) з `starlette.background`.
Якщо використовувати лише `BackgroundTasks` (а не `BackgroundTask`), то його можна передавати як параметр у функції операції шляху, і **FastAPI** подбає про все інше, так само як і про використання об'єкта `Request`.
Також можна використовувати `BackgroundTask` окремо в FastAPI, але для цього вам доведеться створити об'єкт у коді та повернути Starlette `Response`, включаючи його.
Детальніше можна почитати в [офіційній документації Starlette про Background Tasks](https://www.starlette.dev/background/).
## Застереження { #caveat }
Якщо вам потрібно виконувати складні фонові обчислення, і при цьому нема потреби запускати їх у тому ж процесі (наприклад, не потрібно спільного доступу до пам’яті чи змінних), можливо, варто скористатися більш потужними інструментами, такими як [Celery](https://docs.celeryq.dev).
Такі інструменти зазвичай потребують складнішої конфігурації та менеджера черги повідомлень/завдань, наприклад, RabbitMQ або Redis. Однак вони дозволяють виконувати фонові задачі в кількох процесах і особливо — на кількох серверах.
Якщо ж вам потрібно отримати доступ до змінних і об’єктів із тієї ж **FastAPI**-програми або виконувати невеликі фонові завдання (наприклад, надсилати email-сповіщення), достатньо просто використовувати `BackgroundTasks`.
## Підсумок { #recap }
Імпортуйте та використовуйте `BackgroundTasks` як параметри у функціях операції шляху та залежностях, щоб додавати фонові задачі.
================================================
FILE: docs/uk/docs/tutorial/bigger-applications.md
================================================
# Більші застосунки - кілька файлів { #bigger-applications-multiple-files }
Якщо ви створюєте застосунок або веб-API, рідко вдається вмістити все в один файл.
**FastAPI** надає зручний інструмент для структурування вашого застосунку, зберігаючи всю гнучкість.
/// info | Інформація
Якщо ви прийшли з Flask, це еквівалент «Blueprints» у Flask.
///
## Приклад структури файлів { #an-example-file-structure }
Припустімо, у вас така структура файлів:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py
```
/// tip | Порада
Тут кілька файлів `__init__.py`: по одному в кожному каталозі та підкаталозі.
Саме це дозволяє імпортувати код з одного файлу в інший.
Наприклад, у `app/main.py` ви можете мати рядок:
```
from app.routers import items
```
///
* Каталог `app` містить усе. І він має порожній файл `app/__init__.py`, тож це «пакет Python» (збірка «модулів Python»): `app`.
* Він містить файл `app/main.py`. Оскільки він усередині пакета Python (каталог з файлом `__init__.py`), це «модуль» цього пакета: `app.main`.
* Є також файл `app/dependencies.py`, так само як `app/main.py`, це «модуль»: `app.dependencies`.
* Є підкаталог `app/routers/` з іншим файлом `__init__.py`, отже це «підпакет Python»: `app.routers`.
* Файл `app/routers/items.py` знаходиться в пакеті `app/routers/`, отже це підмодуль: `app.routers.items`.
* Так само і `app/routers/users.py`, це інший підмодуль: `app.routers.users`.
* Є також підкаталог `app/internal/` з іншим файлом `__init__.py`, отже це інший «підпакет Python»: `app.internal`.
* І файл `app/internal/admin.py` - ще один підмодуль: `app.internal.admin`.
Та сама структура файлів з коментарями:
```bash
.
├── app # «app» - це пакет Python
│ ├── __init__.py # цей файл робить «app» «пакетом Python»
│ ├── main.py # модуль «main», напр. import app.main
│ ├── dependencies.py # модуль «dependencies», напр. import app.dependencies
│ └── routers # «routers» - це «підпакет Python»
│ │ ├── __init__.py # робить «routers» «підпакетом Python»
│ │ ├── items.py # підмодуль «items», напр. import app.routers.items
│ │ └── users.py # підмодуль «users», напр. import app.routers.users
│ └── internal # «internal» - це «підпакет Python»
│ ├── __init__.py # робить «internal» «підпакетом Python»
│ └── admin.py # підмодуль «admin», напр. import app.internal.admin
```
## `APIRouter` { #apirouter }
Припустімо, файл, присвячений обробці лише користувачів, - це підмодуль у `/app/routers/users.py`.
Ви хочете мати *операції шляху*, пов'язані з користувачами, відокремлено від решти коду, щоб зберегти порядок.
Але це все одно частина того самого застосунку/веб-API **FastAPI** (це частина того самого «пакета Python»).
Ви можете створювати *операції шляху* для цього модуля, використовуючи `APIRouter`.
### Імпортуйте `APIRouter` { #import-apirouter }
Імпортуйте його та створіть «екземпляр» так само, як ви б робили з класом `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
### *Операції шляху* з `APIRouter` { #path-operations-with-apirouter }
Потім використовуйте його для оголошення *операцій шляху*.
Використовуйте його так само, як і клас `FastAPI`:
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
Можете думати про `APIRouter` як про «міні `FastAPI`».
Підтримуються ті самі опції.
Ті самі `parameters`, `responses`, `dependencies`, `tags` тощо.
/// tip | Порада
У цьому прикладі змінна називається `router`, але ви можете назвати її як завгодно.
///
Ми включимо цей `APIRouter` у основний застосунок `FastAPI`, але спочатку розгляньмо залежності та інший `APIRouter`.
## Залежності { #dependencies }
Бачимо, що нам знадобляться кілька залежностей, які використовуються в різних місцях застосунку.
Тож помістимо їх у власний модуль `dependencies` (`app/dependencies.py`).
Тепер використаємо просту залежність для читання користувацького заголовка `X-Token`:
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
/// tip | Порада
Ми використовуємо вигаданий заголовок, щоб спростити приклад.
Але в реальних випадках ви отримаєте кращі результати, використовуючи інтегровані [засоби безпеки](security/index.md).
///
## Інший модуль з `APIRouter` { #another-module-with-apirouter }
Припустімо, у вас також є кінцеві точки для обробки «items» у модулі `app/routers/items.py`.
У вас є *операції шляху* для:
* `/items/`
* `/items/{item_id}`
Структура така сама, як у `app/routers/users.py`.
Але ми хочемо бути розумнішими й трохи спростити код.
Ми знаємо, що всі *операції шляху* в цьому модулі мають однакові:
* Префікс шляху `prefix`: `/items`.
* `tags`: (лише одна мітка: `items`).
* Додаткові `responses`.
* `dependencies`: усім потрібна залежність `X-Token`, яку ми створили.
Тож замість додавання цього до кожної *операції шляху*, ми можемо додати це до `APIRouter`.
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
Оскільки шлях кожної *операції шляху* має починатися з `/`, як у:
```Python hl_lines="1"
@router.get("/{item_id}")
async def read_item(item_id: str):
...
```
...префікс не має містити кінцевий `/`.
Отже, у цьому випадку префікс - це `/items`.
Ми також можемо додати список `tags` та додаткові `responses`, які застосовуватимуться до всіх *операцій шляху*, включених у цей router.
І ми можемо додати список `dependencies`, які буде додано до всіх *операцій шляху* у router і які виконуватимуться/вирішуватимуться для кожного запиту до них.
/// tip | Порада
Зверніть увагу, що так само як і для [залежностей у декораторах *операцій шляху*](dependencies/dependencies-in-path-operation-decorators.md), жодне значення не буде передано вашій *функції операції шляху*.
///
У підсумку шляхи предметів тепер:
* `/items/`
* `/items/{item_id}`
...як ми і планували.
* Вони будуть позначені списком міток, що містить один рядок `"items"`.
* Ці «мітки» особливо корисні для автоматичної інтерактивної документації (за допомогою OpenAPI).
* Усі вони включатимуть наперед визначені `responses`.
* Для всіх цих *операцій шляху* список `dependencies` буде оцінений/виконаний перед ними.
* Якщо ви також оголосите залежності в конкретній *операції шляху*, **вони також будуть виконані**.
* Спочатку виконуються залежності router'а, потім [`dependencies` у декораторі](dependencies/dependencies-in-path-operation-decorators.md), а потім звичайні параметричні залежності.
* Ви також можете додати [`Security` залежності з `scopes`](../advanced/security/oauth2-scopes.md).
/// tip | Порада
Наявність `dependencies` у `APIRouter` можна використати, наприклад, щоб вимагати автентифікацію для всієї групи *операцій шляху*. Навіть якщо залежності не додані до кожної з них окремо.
///
/// check | Перевірте
Параметри `prefix`, `tags`, `responses` і `dependencies` - це (як і в багатьох інших випадках) просто можливість **FastAPI**, яка допомагає уникати дублювання коду.
///
### Імпортуйте залежності { #import-the-dependencies }
Цей код живе в модулі `app.routers.items`, у файлі `app/routers/items.py`.
І нам потрібно отримати функцію залежності з модуля `app.dependencies`, файлу `app/dependencies.py`.
Тож ми використовуємо відносний імпорт з `..` для залежностей:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
#### Як працюють відносні імпорти { #how-relative-imports-work }
/// tip | Порада
Якщо ви досконало знаєте, як працюють імпорти, перейдіть до наступного розділу нижче.
///
Одна крапка `.`, як у:
```Python
from .dependencies import get_token_header
```
означає:
* Починаючи в тому самому пакеті, де знаходиться цей модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* знайти модуль `dependencies` (уявний файл `app/routers/dependencies.py`)...
* і з нього імпортувати функцію `get_token_header`.
Але такого файлу не існує, наші залежності у файлі `app/dependencies.py`.
Згадайте, як виглядає структура нашого застосунку/файлів:
---
Дві крапки `..`, як у:
```Python
from ..dependencies import get_token_header
```
означають:
* Починаючи в тому самому пакеті, де знаходиться цей модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* перейти до батьківського пакета (каталог `app/`)...
* і там знайти модуль `dependencies` (файл `app/dependencies.py`)...
* і з нього імпортувати функцію `get_token_header`.
Це працює правильно! 🎉
---
Так само, якби ми використали три крапки `...`, як у:
```Python
from ...dependencies import get_token_header
```
це б означало:
* Починаючи в тому самому пакеті, де знаходиться цей модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
* перейти до батьківського пакета (каталог `app/`)...
* потім перейти до батьківського пакета від того (немає батьківського пакета, `app` - верхній рівень 😱)...
* і там знайти модуль `dependencies` (файл `app/dependencies.py`)...
* і з нього імпортувати функцію `get_token_header`.
Це б посилалося на якийсь пакет вище за `app/` з власним файлом `__init__.py` тощо. Але в нас такого немає. Тож у нашому прикладі це спричинить помилку. 🚨
Але тепер ви знаєте, як це працює, тож можете використовувати відносні імпорти у власних застосунках, незалежно від їхньої складності. 🤓
### Додайте користувацькі `tags`, `responses` і `dependencies` { #add-some-custom-tags-responses-and-dependencies }
Ми не додаємо префікс `/items` ані `tags=["items"]` до кожної *операції шляху*, бо додали їх до `APIRouter`.
Але ми все ще можемо додати _ще_ `tags`, які будуть застосовані до конкретної *операції шляху*, а також додаткові `responses`, специфічні для цієї *операції шляху*:
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
/// tip | Порада
Остання операція шляху матиме комбінацію міток: `["items", "custom"]`.
І вона також матиме в документації обидві відповіді: одну для `404` і одну для `403`.
///
## Основний `FastAPI` { #the-main-fastapi }
Тепер розгляньмо модуль `app/main.py`.
Тут ви імпортуєте і використовуєте клас `FastAPI`.
Це буде головний файл вашого застосунку, який усе поєднує.
І оскільки більшість вашої логіки тепер житиме у власних модулях, головний файл буде досить простим.
### Імпортуйте `FastAPI` { #import-fastapi }
Імпортуйте та створіть клас `FastAPI`, як зазвичай.
І ми навіть можемо оголосити [глобальні залежності](dependencies/global-dependencies.md), які будуть поєднані із залежностями кожного `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
### Імпортуйте `APIRouter` { #import-the-apirouter }
Тепер імпортуємо інші підмодулі, що мають `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
Оскільки файли `app/routers/users.py` та `app/routers/items.py` - це підмодулі, що є частиною того самого пакета Python `app`, ми можемо використати одну крапку `.` для «відносних імпортів».
### Як працює імпорт { #how-the-importing-works }
Розділ:
```Python
from .routers import items, users
```
означає:
* Починаючи в тому самому пакеті, де знаходиться цей модуль (файл `app/main.py`) (каталог `app/`)...
* знайти підпакет `routers` (каталог `app/routers/`)...
* і з нього імпортувати підмодулі `items` (файл `app/routers/items.py`) і `users` (файл `app/routers/users.py`)...
Модуль `items` матиме змінну `router` (`items.router`). Це та сама, що ми створили у файлі `app/routers/items.py`, це об’єкт `APIRouter`.
Потім ми робимо те саме для модуля `users`.
Ми також могли б імпортувати їх так:
```Python
from app.routers import items, users
```
/// info | Інформація
Перша версія - «відносний імпорт»:
```Python
from .routers import items, users
```
Друга версія - «абсолютний імпорт»:
```Python
from app.routers import items, users
```
Щоб дізнатися більше про пакети й модулі Python, прочитайте [офіційну документацію Python про модулі](https://docs.python.org/3/tutorial/modules.html).
///
### Уникайте колізій імен { #avoid-name-collisions }
Ми імпортуємо підмодуль `items` напряму, замість імпорту лише його змінної `router`.
Це тому, що в підмодулі `users` також є змінна з назвою `router`.
Якби ми імпортували один за одним, як:
```Python
from .routers.items import router
from .routers.users import router
```
`router` з `users` перезаписав би той, що з `items`, і ми не змогли б використовувати їх одночасно.
Щоб мати змогу використовувати обидва в одному файлі, ми імпортуємо підмодулі напряму:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
### Додайте `APIRouter` для `users` і `items` { #include-the-apirouters-for-users-and-items }
Тепер додаймо `router` з підмодулів `users` і `items`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
/// info | Інформація
`users.router` містить `APIRouter` у файлі `app/routers/users.py`.
А `items.router` містить `APIRouter` у файлі `app/routers/items.py`.
///
За допомогою `app.include_router()` ми можемо додати кожен `APIRouter` до основного застосунку `FastAPI`.
Це включить усі маршрути з цього router'а як частину застосунку.
/// note | Технічні деталі
Фактично, всередині для кожної *операції шляху*, оголошеної в `APIRouter`, буде створена окрема *операція шляху*.
Тобто за лаштунками все працюватиме так, ніби це один і той самий застосунок.
///
/// check | Перевірте
Вам не потрібно перейматися продуктивністю під час включення router'ів.
Це займе мікросекунди і відбуватиметься лише під час запуску.
Тож це не вплине на продуктивність. ⚡
///
### Додайте `APIRouter` з власними `prefix`, `tags`, `responses` і `dependencies` { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
Уявімо, що ваша організація надала вам файл `app/internal/admin.py`.
Він містить `APIRouter` з кількома адміністративними *операціями шляху*, які організація спільно використовує між кількома проєктами.
Для цього прикладу він буде дуже простим. Але припустімо, що оскільки його спільно використовують з іншими проєктами організації, ми не можемо модифікувати його та додавати `prefix`, `dependencies`, `tags` тощо прямо до `APIRouter`:
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
Але ми все одно хочемо встановити користувацький `prefix` під час включення `APIRouter`, щоб усі його *операції шляху* починалися з `/admin`, хочемо захистити його за допомогою `dependencies`, які вже є в цьому проєкті, і хочемо додати `tags` та `responses`.
Ми можемо оголосити все це, не змінюючи оригінальний `APIRouter`, передавши ці параметри до `app.include_router()`:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
Таким чином, вихідний `APIRouter` залишиться без змін, тож ми все ще зможемо спільно використовувати той самий файл `app/internal/admin.py` з іншими проєктами в організації.
У результаті в нашому застосунку кожна з *операцій шляху* з модуля `admin` матиме:
* Префікс `/admin`.
* Мітку `admin`.
* Залежність `get_token_header`.
* Відповідь `418`. 🍵
Але це вплине лише на цей `APIRouter` у нашому застосунку, а не на будь-який інший код, який його використовує.
Наприклад, інші проєкти можуть використовувати той самий `APIRouter` з іншим методом автентифікації.
### Додайте *операцію шляху* { #include-a-path-operation }
Ми також можемо додавати *операції шляху* безпосередньо до застосунку `FastAPI`.
Тут ми це робимо... просто щоб показати, що так можна 🤷:
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
і це працюватиме коректно разом з усіма іншими *операціями шляху*, доданими через `app.include_router()`.
/// info | Дуже технічні деталі
Примітка: це дуже технічна деталь, яку ви, ймовірно, можете просто пропустити.
---
`APIRouter` не «монтуються», вони не ізольовані від решти застосунку.
Це тому, що ми хочемо включати їхні *операції шляху* в схему OpenAPI та інтерфейси користувача.
Оскільки ми не можемо просто ізолювати їх і «змонтувати» незалежно від решти, *операції шляху* «клонуються» (створюються заново), а не включаються безпосередньо.
///
## Налаштуйте `entrypoint` у `pyproject.toml` { #configure-the-entrypoint-in-pyproject-toml }
Оскільки ваш об'єкт FastAPI `app` знаходиться в `app/main.py`, ви можете налаштувати `entrypoint` у файлі `pyproject.toml` так:
```toml
[tool.fastapi]
entrypoint = "app.main:app"
```
це еквівалентно імпорту:
```python
from app.main import app
```
Таким чином команда `fastapi` знатиме, де знайти ваш застосунок.
/// Note | Примітка
Ви також могли б передати шлях команді, наприклад:
```console
$ fastapi dev app/main.py
```
Але тоді вам доведеться щоразу пам'ятати, щоб передавати правильний шлях, коли ви викликаєте команду `fastapi`.
Крім того, інші інструменти можуть не знайти його, наприклад [розширення VS Code](../editor-support.md) або [FastAPI Cloud](https://fastapicloud.com), тому рекомендовано використовувати `entrypoint` у `pyproject.toml`.
///
## Перевірте автоматичну документацію API { #check-the-automatic-api-docs }
Тепер запустіть ваш застосунок:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
І відкрийте документацію за адресою [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите автоматичну документацію API, що включає шляхи з усіх підмодулів, з правильними шляхами (і префіксами) та правильними мітками:
## Включайте той самий router кілька разів з різними `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
Ви також можете використовувати `.include_router()` кілька разів з одним і тим самим router'ом, але з різними префіксами.
Це може бути корисно, наприклад, щоб публікувати той самий API під різними префіксами, наприклад `/api/v1` і `/api/latest`.
Це просунуте використання, яке вам може й не знадобитися, але воно є на випадок, якщо потрібно.
## Включіть один `APIRouter` до іншого { #include-an-apirouter-in-another }
Так само як ви можете включити `APIRouter` у застосунок `FastAPI`, ви можете включити `APIRouter` в інший `APIRouter`, використовуючи:
```Python
router.include_router(other_router)
```
Переконайтеся, що ви робите це до включення `router` в застосунок `FastAPI`, щоб *операції шляху* з `other_router` також були включені.
================================================
FILE: docs/uk/docs/tutorial/body-fields.md
================================================
# Тіло — Поля { #body-fields }
Так само як ви можете оголошувати додаткову валідацію та метадані в параметрах *функції операції шляху* за допомогою `Query`, `Path` та `Body`, ви можете оголошувати валідацію та метадані всередині моделей Pydantic, використовуючи `Field` від Pydantic.
## Імпорт `Field` { #import-field }
Спочатку вам потрібно імпортувати це:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *}
/// warning | Попередження
Зверніть увагу, що `Field` імпортується безпосередньо з `pydantic`, а не з `fastapi`, як усе інше (`Query`, `Path`, `Body` тощо).
///
## Оголошення атрибутів моделі { #declare-model-attributes }
Потім ви можете використовувати `Field` з атрибутами моделі:
{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *}
`Field` працює так само, як `Query`, `Path` і `Body`, має ті самі параметри тощо.
/// note | Технічні деталі
Насправді `Query`, `Path` та інші, які ви побачите далі, створюють об'єкти підкласів спільного класу `Param`, який сам є підкласом класу `FieldInfo` з Pydantic.
І `Field` від Pydantic також повертає екземпляр `FieldInfo`.
`Body` також безпосередньо повертає об'єкти підкласу `FieldInfo`. І є інші, які ви побачите пізніше, що є підкласами класу `Body`.
Пам'ятайте, що коли ви імпортуєте `Query`, `Path` та інші з `fastapi`, це фактично функції, які повертають спеціальні класи.
///
/// tip | Порада
Зверніть увагу, що кожен атрибут моделі з типом, значенням за замовчуванням і `Field` має ту саму структуру, що й параметр *функції операції шляху*, з `Field` замість `Path`, `Query` і `Body`.
///
## Додавання додаткової інформації { #add-extra-information }
Ви можете оголошувати додаткову інформацію в `Field`, `Query`, `Body` тощо. І вона буде включена до згенерованої JSON Schema.
Ви дізнаєтеся більше про додавання додаткової інформації пізніше в документації, коли вивчатимете, як оголошувати приклади.
/// warning | Попередження
Додаткові ключі, передані в `Field`, також будуть присутні в отриманій схемі OpenAPI для вашого застосунку.
Оскільки ці ключі не обов'язково є частиною специфікації OpenAPI, деякі інструменти OpenAPI, наприклад [валідатор OpenAPI](https://validator.swagger.io/), можуть не працювати з вашою згенерованою схемою.
///
## Підсумок { #recap }
Ви можете використовувати `Field` від Pydantic, щоб оголошувати додаткову валідацію та метадані для атрибутів моделі.
Ви також можете використовувати додаткові keyword arguments, щоб передавати додаткові метадані JSON Schema.
================================================
FILE: docs/uk/docs/tutorial/body-multiple-params.md
================================================
# Тіло - Декілька параметрів { #body-multiple-parameters }
Тепер, коли ми побачили, як використовувати `Path` і `Query`, розгляньмо більш просунуті варіанти оголошення тіла запиту.
## Змішування `Path`, `Query` та параметрів тіла { #mix-path-query-and-body-parameters }
По-перше, звісно, ви можете вільно змішувати оголошення параметрів `Path`, `Query` та тіла запиту, і **FastAPI** знатиме, що робити.
Також ви можете оголошувати параметри тіла як необов’язкові, встановивши для них значення за замовчуванням `None`:
{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *}
/// note | Примітка
Зверніть увагу, що в цьому випадку параметр `item`, який береться з тіла, є необов'язковим. Оскільки має значення за замовчуванням `None`.
///
## Декілька параметрів тіла { #multiple-body-parameters }
У попередньому прикладi *операції шляху* очікували б JSON-тіло з атрибутами `Item`, наприклад:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
Але ви також можете оголосити декілька параметрів тіла, наприклад `item` та `user`:
{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *}
У цьому випадку **FastAPI** помітить, що у функції є більше ніж один параметр тіла (є два параметри, які є моделями Pydantic).
Тож він використає назви параметрів як ключі (назви полів) у тілі та очікуватиме тіло такого вигляду:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
```
/// note | Примітка
Зверніть увагу, що хоча `item` оголошено так само, як і раніше, тепер він очікується всередині тіла з ключем `item`.
///
**FastAPI** виконає автоматичне перетворення із запиту, щоб параметр `item` отримав свій конкретний вміст, і те ж саме для `user`.
Він виконає валідацію складених даних і задокументує це таким чином у схемі OpenAPI та в автоматичній документації.
## Одиничні значення в тілі { #singular-values-in-body }
Так само як є `Query` і `Path` для визначення додаткових даних для параметрів запиту та шляху, **FastAPI** надає еквівалентний `Body`.
Наприклад, розширивши попередню модель, ви можете вирішити додати ще один ключ `importance` у те саме тіло, окрім `item` і `user`.
Якщо оголосити його як є, оскільки це одиничне значення, **FastAPI** припустить, що це параметр запиту.
Але ви можете вказати **FastAPI** обробляти його як інший ключ тіла, використовуючи `Body`:
{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *}
У цьому випадку **FastAPI** очікуватиме тіло такого вигляду:
```JSON
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
```
Знову ж таки, він перетворюватиме типи даних, перевірятиме, документуватиме тощо.
## Декілька параметрів тіла та query { #multiple-body-params-and-query }
Звісно, ви також можете оголошувати додаткові query параметри щоразу, коли це потрібно, додатково до будь-яких параметрів тіла.
Оскільки за замовчуванням одиничні значення інтерпретуються як параметри запиту, вам не потрібно явно додавати `Query`, ви можете просто зробити:
```Python
q: str | None = None
```
Наприклад:
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
/// info | Інформація
`Body` також має всі ті самі додаткові параметри валідації та метаданих, що й `Query`, `Path` та інші, які ви побачите пізніше.
///
## Вбудувати один параметр тіла { #embed-a-single-body-parameter }
Скажімо, у вас є лише один параметр тіла `item` з моделі Pydantic `Item`.
За замовчуванням **FastAPI** очікуватиме його тіло безпосередньо.
Але якщо ви хочете, щоб він очікував JSON з ключем `item`, а всередині нього - вміст моделі, як це відбувається, коли ви оголошуєте додаткові параметри тіла, ви можете використати спеціальний параметр `Body` - `embed`:
```Python
item: Item = Body(embed=True)
```
як у прикладі:
{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *}
У цьому випадку **FastAPI** очікуватиме тіло такого вигляду:
```JSON hl_lines="2"
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
```
замість:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
```
## Підсумок { #recap }
Ви можете додавати кілька параметрів тіла до вашої *функції операції шляху*, навіть якщо запит може мати лише одне тіло.
Але **FastAPI** обробить це, надасть вам правильні дані у функції та перевірить і задокументує правильну схему в *операції шляху*.
Також ви можете оголошувати одиничні значення, щоб отримувати їх як частину тіла.
І ви можете вказати **FastAPI** вбудовувати тіло в ключ, навіть коли оголошено лише один параметр.
================================================
FILE: docs/uk/docs/tutorial/body-nested-models.md
================================================
# Тіло - Вкладені моделі { #body-nested-models }
З **FastAPI** ви можете визначати, перевіряти, документувати та використовувати моделі, які можуть бути вкладені на будь-яку глибину (завдяки Pydantic).
## Поля списку { #list-fields }
Ви можете визначити атрибут як підтип. Наприклад, Python-список (`list`):
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Це зробить `tags` списком, хоча не визначається тип елементів списку.
## Поля списку з параметром типу { #list-fields-with-type-parameter }
Але Python має специфічний спосіб оголошення списків з внутрішніми типами або «параметрами типу»:
### Оголошення `list` з параметром типу { #declare-a-list-with-a-type-parameter }
Щоб оголосити типи з параметрами типу (внутрішніми типами), такими як `list`, `dict`, `tuple`,
передайте внутрішні тип(и) як «параметри типу», використовуючи квадратні дужки: `[` та `]`
```Python
my_list: list[str]
```
Це стандартний синтаксис Python для оголошення типів.
Використовуйте той самий стандартний синтаксис для атрибутів моделей з внутрішніми типами.
Отже, у нашому прикладі, ми можемо зробити `tags` саме «списком рядків»:
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
## Типи множин { #set-types }
Але потім ми подумали, що теги не повинні повторюватися, вони, ймовірно, повинні бути унікальними рядками.
І Python має спеціальний тип даних для множин унікальних елементів — це `set`.
Тому ми можемо оголосити `tags` як множину рядків:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
Навіть якщо ви отримаєте запит з дубльованими даними, він буде перетворений у множину унікальних елементів.
І коли ви будете виводити ці дані, навіть якщо джерело містить дублікати, вони будуть виведені як множина унікальних елементів.
І це буде анотовано/документовано відповідно.
## Вкладені моделі { #nested-models }
Кожен атрибут моделі Pydantic має тип.
Але цей тип сам може бути іншою моделлю Pydantic.
Отже, ви можете оголосити глибоко вкладені JSON «об'єкти» з конкретними іменами атрибутів, типами та перевірками.
Усе це, вкладене без обмежень.
### Визначення підмоделі { #define-a-submodel }
Наприклад, ми можемо визначити модель `Image`:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
### Використання підмоделі як типу { #use-the-submodel-as-a-type }
А потім ми можемо використовувати її як тип атрибута:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Це означатиме, що **FastAPI** очікуватиме тіло запиту такого вигляду:
```JSON
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
```
Завдяки такій декларації у **FastAPI** ви отримуєте:
* Підтримку в редакторі (автозавершення тощо), навіть для вкладених моделей
* Конвертацію даних
* Валідацію даних
* Автоматичну документацію
## Спеціальні типи та валідація { #special-types-and-validation }
Окрім звичайних типів, таких як `str`, `int`, `float`, та ін. ви можете використовувати складніші типи, які наслідують `str`.
Щоб побачити всі доступні варіанти, ознайомтеся з [Оглядом типів у Pydantic](https://docs.pydantic.dev/latest/concepts/types/). Деякі приклади будуть у наступному розділі.
Наприклад, у моделі `Image` є поле `url`, тому ми можемо оголосити його як `HttpUrl` від Pydantic замість `str`:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Рядок буде перевірено як дійсну URL-адресу і задокументовано в JSON Schema / OpenAPI як URL.
## Атрибути зі списками підмоделей { #attributes-with-lists-of-submodels }
У Pydantic ви можете використовувати моделі як підтипи для `list`, `set` тощо:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Це означає, що **FastAPI** буде очікувати (конвертувати, валідувати, документувати тощо) JSON тіло запиту у вигляді:
```JSON hl_lines="11"
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
```
/// info | Інформація
Зверніть увагу, що тепер ключ `images` містить список об'єктів зображень.
///
## Глибоко вкладені моделі { #deeply-nested-models }
Ви можете визначати вкладені моделі довільної глибини:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Інформація
Зверніть увагу, що в моделі `Offer` є список `Item`ів, які, своєю чергою, можуть мати необов'язковий список `Image`ів.
///
## Тіла запитів, що складаються зі списків { #bodies-of-pure-lists }
Якщо верхній рівень JSON тіла, яке ви очікуєте, є JSON `масивом` (у Python — `list`), ви можете оголосити тип у параметрі функції, як і в моделях Pydantic:
```Python
images: list[Image]
```
наприклад:
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
## Підтримка в редакторі всюди { #editor-support-everywhere }
Ви отримаєте підтримку в редакторі всюди.
Навіть для елементів у списках:
Ви не змогли б отримати таку підтримку в редакторі, якби працювали напряму зі `dict`, а не з моделями Pydantic.
Але вам не потрібно турбуватися про це: вхідні dict'и автоматично конвертуються, а вихідні дані автоматично перетворюються в JSON.
## Тіла з довільними `dict` { #bodies-of-arbitrary-dicts }
Ви також можете оголосити тіло як `dict` з ключами одного типу та значеннями іншого типу.
Це корисно, якщо ви не знаєте наперед, які імена полів будуть дійсними (як у випадку з моделями Pydantic).
Це буде корисно, якщо ви хочете приймати ключі, які заздалегідь невідомі.
---
Це також зручно, якщо ви хочете мати ключі іншого типу (наприклад, `int`).
Ось що ми розглянемо далі.
У цьому випадку ви можете приймати будь-який `dict`, якщо його ключі — це `int`, а значення — `float`:
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
/// tip | Порада
Майте на увазі, що в JSON тілі ключі можуть бути лише рядками (`str`).
Але Pydantic автоматично конвертує дані.
Це означає, що навіть якщо клієнти вашого API надсилатимуть ключі у вигляді рядків, якщо вони містять цілі числа, Pydantic конвертує їх і проведе валідацію.
Тобто `dict`, який ви отримаєте як `weights`, матиме ключі типу `int` та значення типу `float`.
///
## Підсумок { #recap }
З **FastAPI** ви маєте максимальну гнучкість завдяки моделям Pydantic, зберігаючи при цьому код простим, коротким та елегантним.
А також отримуєте всі переваги:
* Підтримка в редакторі (автодоповнення всюди!)
* Конвертація даних (парсинг/серіалізація)
* Валідація даних
* Документація схем
* Автоматичне створення документації
================================================
FILE: docs/uk/docs/tutorial/body-updates.md
================================================
# Тіло — Оновлення { #body-updates }
## Оновлення із заміною за допомогою `PUT` { #update-replacing-with-put }
Щоб оновити елемент, ви можете використати [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) операцію.
Ви можете використати `jsonable_encoder`, щоб перетворити вхідні дані на такі, які можна зберігати як JSON (наприклад, у NoSQL базі даних). Наприклад, перетворюючи `datetime` у `str`.
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
`PUT` використовується для отримання даних, які мають замінити чинні дані.
### Попередження про заміну { #warning-about-replacing }
Це означає, що якщо Ви хочете оновити елемент `bar`, використовуючи `PUT` з тілом:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
оскільки він не містить вже збереженого атрибута `"tax": 20.2`, модель введення прийме значення за замовчуванням `"tax": 10.5`.
І дані будуть збережені з цим "новим" значенням `tax` = `10.5`.
## Часткові оновлення з `PATCH` { #partial-updates-with-patch }
Ви також можете використовувати операцію [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) для *часткового* оновлення даних.
Це означає, що Ви можете надіслати лише ті дані, які хочете оновити, залишаючи інші без змін.
/// note | Примітка
`PATCH` менш поширений і менш відомий, ніж `PUT`.
І багато команд використовують лише `PUT`, навіть для часткових оновлень.
Ви **вільні** використовувати їх так, як хочете, **FastAPI** не накладає жодних обмежень.
Але цей посібник показує вам, більш-менш, як їх задумано використовувати.
///
### Використання параметра `exclude_unset` у Pydantic { #using-pydantics-exclude-unset-parameter }
Якщо Ви хочете отримувати часткові оновлення, дуже корисно використовувати параметр `exclude_unset` у `.model_dump()` моделі Pydantic.
Наприклад: `item.model_dump(exclude_unset=True)`.
Це згенерує `dict` лише з тими даними, які були встановлені під час створення моделі `item`, виключаючи значення за замовчуванням.
Тоді Ви можете використовувати це, щоб згенерувати `dict` лише з даними, які були встановлені (надіслані у запиті), пропускаючи значення за замовчуванням:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
### Використання параметра `update` у Pydantic { #using-pydantics-update-parameter }
Тепер Ви можете створити копію наявної моделі за допомогою `.model_copy()`, і передати параметр `update` з `dict`, який містить дані для оновлення.
Наприклад: `stored_item_model.model_copy(update=update_data)`:
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
### Підсумок часткових оновлень { #partial-updates-recap }
У підсумку, щоб застосувати часткові оновлення, Ви:
* (Опціонально) використовуєте `PATCH` замість `PUT`.
* Отримуєте збережені дані.
* Поміщаєте ці дані в модель Pydantic.
* Генеруєте `dict` без значень за замовчуванням з моделі введення (використовуючи `exclude_unset`).
* Таким чином Ви оновите лише ті значення, які були явно задані користувачем, замість того, щоб перезаписувати вже збережені значення значеннями за замовчуванням з вашої моделі.
* Створюєте копію збереженої моделі, оновлюючи її атрибути отриманими частковими оновленнями (використовуючи параметр `update`).
* Перетворюєте скопійовану модель на щось, що можна зберегти у вашу БД (наприклад, використовуючи `jsonable_encoder`).
* Це можна порівняти з повторним використанням методу `.model_dump()` моделі, але це гарантує (і перетворює) значення у типи даних, які можна перетворити на JSON, наприклад, `datetime` на `str`.
* Зберігаєте дані у вашу БД.
* Повертаєте оновлену модель.
{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *}
/// tip | Порада
Насправді Ви можете використовувати цю саму техніку і з операцією HTTP `PUT`.
Але приклад тут використовує `PATCH`, тому що він був створений для таких випадків.
///
/// note | Примітка
Зверніть увагу, що модель запиту все ще проходить валідацію.
Тож, якщо Ви хочете отримувати часткові оновлення, які можуть пропускати всі атрибути, Вам потрібно мати модель, де всі атрибути позначені як необов’язкові (зі значеннями за замовчуванням або `None`).
Щоб розрізняти моделі з усіма необов’язковими значеннями для **оновлення** і моделі з обов’язковими значеннями для **створення**, Ви можете скористатись ідеями, описаними у [Додаткові моделі](extra-models.md).
///
================================================
FILE: docs/uk/docs/tutorial/body.md
================================================
# Тіло запиту { #request-body }
Коли вам потрібно надіслати дані з клієнта (скажімо, браузера) до вашого API, ви надсилаєте їх як **тіло запиту**.
Тіло **запиту** - це дані, надіслані клієнтом до вашого API. Тіло **відповіді** - це дані, які ваш API надсилає клієнту.
Ваш API майже завжди має надсилати тіло **відповіді**. Але клієнтам не обов’язково потрібно постійно надсилати тіла **запитів** — інколи вони лише запитують шлях, можливо з деякими параметрами запиту, але не надсилають тіло.
Щоб оголосити тіло **запиту**, ви використовуєте [Pydantic](https://docs.pydantic.dev/) моделі з усією їх потужністю та перевагами.
/// info | Інформація
Щоб надіслати дані, ви повинні використовувати один із: `POST` (більш поширений), `PUT`, `DELETE` або `PATCH`.
Надсилання тіла із запитом `GET` має невизначену поведінку в специфікаціях, проте воно підтримується FastAPI лише для дуже складних/екстремальних випадків використання.
Оскільки це не рекомендується, інтерактивна документація з Swagger UI не відображатиме документацію для тіла запиту під час використання `GET`, і проксі-сервери в середині можуть не підтримувати її.
///
## Імпортуйте `BaseModel` від Pydantic { #import-pydantics-basemodel }
Спочатку вам потрібно імпортувати `BaseModel` з `pydantic`:
{* ../../docs_src/body/tutorial001_py310.py hl[2] *}
## Створіть свою модель даних { #create-your-data-model }
Потім ви оголошуєте свою модель даних як клас, який успадковується від `BaseModel`.
Використовуйте стандартні типи Python для всіх атрибутів:
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
Так само, як і при оголошенні параметрів запиту, коли атрибут моделі має значення за замовчуванням, він не є обов’язковим. В іншому випадку це потрібно. Використовуйте `None`, щоб зробити його просто необов'язковим.
Наприклад, ця модель вище оголошує JSON "`об'єкт`" (або Python `dict`), як:
```JSON
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
...оскільки `description` і `tax` є необов'язковими (зі значенням за замовчуванням `None`), цей JSON "`об'єкт`" також буде дійсним:
```JSON
{
"name": "Foo",
"price": 45.2
}
```
## Оголосіть її як параметр { #declare-it-as-a-parameter }
Щоб додати модель даних до вашої *операції шляху*, оголосіть її так само, як ви оголосили параметри шляху та запиту:
{* ../../docs_src/body/tutorial001_py310.py hl[16] *}
...і вкажіть її тип як модель, яку ви створили, `Item`.
## Результати { #results }
Лише з цим оголошенням типу Python **FastAPI** буде:
* Читати тіло запиту як JSON.
* Перетворювати відповідні типи (якщо потрібно).
* Валідувати дані.
* Якщо дані недійсні, він поверне гарну та чітку помилку, вказуючи, де саме і які дані були неправильними.
* Надавати отримані дані у параметрі `item`.
* Оскільки ви оголосили його у функції як тип `Item`, ви також матимете всю підтримку редактора (автозаповнення, тощо) для всіх атрибутів та їх типів.
* Генерувати визначення [Схеми JSON](https://json-schema.org) для вашої моделі, ви також можете використовувати їх де завгодно, якщо це має сенс для вашого проекту.
* Ці схеми будуть частиною згенерованої схеми OpenAPI і використовуватимуться автоматичною документацією UIs.
## Автоматична документація { #automatic-docs }
Схеми JSON ваших моделей будуть частиною вашої схеми, згенерованої OpenAPI, і будуть показані в інтерактивній API документації:
А також використовуватимуться в API документації всередині кожної *операції шляху*, якій вони потрібні:
## Підтримка редактора { #editor-support }
У вашому редакторі, всередині вашої функції, ви будете отримувати підказки типу та завершення скрізь (це б не сталося, якби ви отримали `dict` замість моделі Pydantic):
Ви також отримуєте перевірку помилок на наявність операцій з неправильним типом:
Це не випадково, весь каркас був побудований навколо цього дизайну.
І він був ретельно перевірений на етапі проектування, перед будь-яким впровадженням, щоб переконатися, що він працюватиме з усіма редакторами.
Були навіть деякі зміни в самому Pydantic, щоб підтримати це.
Попередні скріншоти були зроблені у [Visual Studio Code](https://code.visualstudio.com).
Але ви отримаєте ту саму підтримку редактора у [PyCharm](https://www.jetbrains.com/pycharm/) та більшість інших редакторів Python:
/// tip | Порада
Якщо ви використовуєте [PyCharm](https://www.jetbrains.com/pycharm/) як ваш редактор, ви можете використати [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/).
Він покращує підтримку редакторів для моделей Pydantic за допомогою:
* автозаповнення
* перевірки типу
* рефакторингу
* пошуку
* інспекції
///
## Використовуйте модель { #use-the-model }
Усередині функції ви можете отримати прямий доступ до всіх атрибутів об’єкта моделі:
{* ../../docs_src/body/tutorial002_py310.py *}
## Тіло запиту + параметри шляху { #request-body-path-parameters }
Ви можете одночасно оголошувати параметри шляху та тіло запиту.
**FastAPI** розпізнає, що параметри функції, які відповідають параметрам шляху, мають бути **взяті з шляху**, а параметри функції, які оголошуються як моделі Pydantic, **взяті з тіла запиту**.
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
## Тіло запиту + шлях + параметри запиту { #request-body-path-query-parameters }
Ви також можете оголосити параметри **тіло**, **шлях** і **запит** одночасно.
**FastAPI** розпізнає кожен з них і візьме дані з потрібного місця.
{* ../../docs_src/body/tutorial004_py310.py hl[16] *}
Параметри функції будуть розпізнаватися наступним чином:
* Якщо параметр також оголошено в **шляху**, він використовуватиметься як параметр шляху.
* Якщо параметр має **сингулярний тип** (наприклад, `int`, `float`, `str`, `bool` тощо), він буде інтерпретуватися як параметр **запиту**.
* Якщо параметр оголошується як тип **Pydantic моделі**, він інтерпретується як **тіло** **запиту**.
/// note | Примітка
FastAPI буде знати, що значення `q` не є обов'язковим через значення за замовчуванням `= None`.
`str | None` FastAPI не використовує, щоб визначити, що значення не є обов’язковим - він знатиме, що воно не є обов’язковим, тому що має значення за замовчуванням `= None`.
Але додавання анотацій типів дозволить вашому редактору надати вам кращу підтримку та виявляти помилки.
///
## Без Pydantic { #without-pydantic }
Якщо ви не хочете використовувати моделі Pydantic, ви також можете використовувати параметри **Body**. Перегляньте документацію для [Тіло - Кілька параметрів: Окремі значення в тілі](body-multiple-params.md#singular-values-in-body).
================================================
FILE: docs/uk/docs/tutorial/cookie-param-models.md
================================================
# Моделі параметрів Cookie { #cookie-parameter-models }
Якщо у вас є група **cookies**, які пов'язані між собою, ви можете створити **Pydantic-модель**, щоб оголосити їх. 🍪
Це дозволить вам повторно **використовувати модель** у **різних місцях**, а також оголосити валідацію та метадані для всіх параметрів одночасно. 😎
/// note | Примітка
Це підтримується з версії FastAPI `0.115.0`. 🤓
///
/// tip | Порада
Ця ж техніка застосовується до `Query`, `Cookie` та `Header`. 😎
///
## Cookie з Pydantic-моделлю { #cookies-with-a-pydantic-model }
Оголосіть **cookie**-параметри, які вам потрібні, у **Pydantic-моделі**, а потім оголосіть параметр як `Cookie`:
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *}
**FastAPI** буде **витягувати** дані для **кожного поля** з **cookies**, отриманих у запиті, і передавати вам Pydantic-модель, яку ви визначили.
## Перевірка у документації { #check-the-docs }
Ви можете побачити визначені cookies в інтерфейсі документації за адресою `/docs`:
/// info | Інформація
Майте на увазі, що оскільки **браузери обробляють cookies** особливим чином і «за лаштунками», вони **не** дозволяють **JavaScript** легко з ними працювати.
Якщо ви зайдете до **інтерфейсу документації API** за адресою `/docs`, ви зможете побачити **документацію** для cookies у ваших *операціях шляху*.
Але навіть якщо ви заповните дані й натиснете "Execute", оскільки інтерфейс документації працює з **JavaScript**, cookies не будуть відправлені, і ви побачите **помилку**, ніби ви не ввели жодних значень.
///
## Заборона додаткових cookie { #forbid-extra-cookies }
У деяких спеціальних випадках (ймовірно, не дуже поширених) ви можете захотіти **обмежити** cookies, які хочете отримувати.
Ваш API тепер має можливість контролювати власну згоду на cookies. 🤪🍪
Ви можете використовувати налаштування моделі Pydantic, щоб `forbid` будь-які `extra` поля:
{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *}
Якщо клієнт спробує надіслати якісь **додаткові cookies**, він отримає відповідь з **помилкою**.
Бідні банери cookie, які так старанно намагаються отримати вашу згоду, щоб API її відхилила. 🍪
Наприклад, якщо клієнт спробує надіслати cookie `santa_tracker` зі значенням `good-list-please`, він отримає відповідь з помилкою, яка повідомить, що `santa_tracker` cookie не дозволено:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please",
}
]
}
```
## Підсумок { #summary }
Ви можете використовувати **Pydantic-моделі** для оголошення **cookies** у **FastAPI**. 😎
================================================
FILE: docs/uk/docs/tutorial/cookie-params.md
================================================
# Параметри кукі { #cookie-parameters }
Ви можете визначати параметри кукі таким же чином, як визначаються параметри `Query` і `Path`.
## Імпорт `Cookie` { #import-cookie }
Спочатку імпортуйте `Cookie`:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *}
## Визначення параметрів `Cookie` { #declare-cookie-parameters }
Потім визначте параметри кукі, використовуючи таку ж конструкцію як для `Path` і `Query`.
Ви можете визначити значення за замовчуванням, а також усі додаткові параметри валідації чи анотації:
{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *}
/// note | Технічні деталі
`Cookie` це "сестра" класів `Path` і `Query`. Вони також наслідуються від одного спільного класу `Param`.
Але пам'ятайте, що коли ви імпортуєте `Query`, `Path`, `Cookie` та інше з `fastapi`, це фактично функції, що повертають спеціальні класи.
///
/// info
Для визначення кукі ви маєте використовувати `Cookie`, тому що в іншому випадку параметри будуть інтерпретовані як параметри запиту.
///
/// info
Майте на увазі, що оскільки **браузери обробляють кукі** спеціальним чином і за лаштунками, вони **не** дозволяють **JavaScript** легко взаємодіяти з ними.
Якщо ви перейдете до **інтерфейсу документації API** за адресою `/docs`, ви зможете побачити **документацію** для кукі для ваших *операцій шляху*.
Але навіть якщо ви **заповните дані** і натиснете "Execute", оскільки інтерфейс документації працює з **JavaScript**, кукі не буде надіслано, і ви побачите повідомлення про **помилку**, ніби ви не ввели жодних значень.
///
## Підсумки { #recap }
Визначайте кукі за допомогою `Cookie`, використовуючи той же спільний шаблон, що і `Query` та `Path`.
================================================
FILE: docs/uk/docs/tutorial/cors.md
================================================
# CORS (Обмін ресурсами між різними джерелами) { #cors-cross-origin-resource-sharing }
[CORS або «Cross-Origin Resource Sharing»](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) є ситуація, коли фронтенд, що працює в браузері, містить JavaScript-код, який взаємодіє з бекендом, розташованим в іншому «джерелі» (origin).
## Джерело (Origin) { #origin }
Джерело визначається комбінацією протоколу (`http`, `https`), домену (`myapp.com`, `localhost`, `localhost.tiangolo.com`), порту (`80`, `443`, `8080`).
Наприклад, такі адреси вважаються різними джерелами:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Навіть якщо вони всі містять `localhost`, вони мають різні протоколи або порти, що робить їх окремими «джерелами».
## Кроки { #steps }
Припустимо, що ваш фронтенд працює в браузері на `http://localhost:8080`, а його JavaScript намагається відправити запит до бекенду, який працює на `http://localhost` (Оскільки ми не вказуємо порт, браузер за замовчуванням припускає порт `80`).
Потім браузер надішле HTTP-запит `OPTIONS` до бекенду на порту `:80`, і якщо бекенд надішле відповідні заголовки, що дозволяють комунікацію з цього іншого джерела (`http://localhost:8080`), тоді браузер на порту `:8080` дозволить JavaScript у фронтенді надіслати свій запит до бекенду на порту `:80`.
Щоб досягти цього, бекенд на порту `:80` повинен мати список «дозволених джерел».
У цьому випадку список має містити `http://localhost:8080`, щоб фронтенд на порту `:8080` працював коректно.
## Дикі карти { #wildcards }
Можна також оголосити список як `"*"` (дика карта), що означає дозвіл для всіх джерел.
Однак це дозволить лише певні типи комунікації, виключаючи все, що пов'язане з обліковими даними: кукі, заголовки авторизації, як-от ті, що використовуються з токенами носія, тощо.
Тому для коректної роботи краще явно вказувати дозволені джерела.
## Використання `CORSMiddleware` { #use-corsmiddleware }
Ви можете налаштувати це у вашому додатку **FastAPI** за допомогою `CORSMiddleware`.
* Імпортуйте `CORSMiddleware`.
* Створіть список дозволених джерел (у вигляді рядків).
* Додайте його як «проміжне програмне забезпечення» у ваш додаток **FastAPI**.
Також можна вказати, чи дозволяє ваш бекенд:
* Облікові дані (заголовки авторизації, кукі, тощо).
* Конкретні HTTP-методи (`POST`, `PUT`) або всі за допомогою `"*"`
* Конкретні HTTP-заголовки або всі за допомогою `"*"`.
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
Параметри за замовчуванням у реалізації `CORSMiddleware` є досить обмеженими, тому вам потрібно явно увімкнути конкретні джерела, методи або заголовки, щоб браузерам було дозволено використовувати їх у міждоменному контексті.
Підтримуються такі аргументи:
* `allow_origins` - Список джерел, яким дозволено здійснювати міждоменні запити. Наприклад `['https://example.org', 'https://www.example.org']`. Ви можете використовувати `['*']`, щоб дозволити будь-яке джерело.
* `allow_origin_regex` - Рядок регулярного виразу для відповідності джерелам, яким дозволено здійснювати міждоменні запити. Наприклад, `'https://.*\.example\.org'`.
* `allow_methods` - Список HTTP-методів, дозволених для міждоменних запитів. За замовчуванням `['GET']`. Ви можете використовувати `['*']`, щоб дозволити всі стандартні методи.
* `allow_headers` - Список HTTP-заголовків запиту, які підтримуються для міждоменних запитів. За замовчуванням `[]`. Ви можете використовувати `['*']`, щоб дозволити всі заголовки. Заголовки `Accept`, `Accept-Language`, `Content-Language` і `Content-Type` завжди дозволені для [простих CORS-запитів](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests).
* `allow_credentials` - Визначає, чи повинні підтримуватися кукі для міждоменних запитів. За замовчуванням `False`.
Жоден із параметрів `allow_origins`, `allow_methods` і `allow_headers` не можна встановлювати як `['*']`, якщо `allow_credentials` встановлено як `True`. Усі вони мають бути [явно вказані](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards).
* `expose_headers` - Вказує, які заголовки відповіді повинні бути доступні для браузера. За замовчуванням `[]`.
* `max_age` - Встановлює максимальний час (у секундах) для кешування CORS-відповідей у браузерах. За замовчуванням `600`.
Це проміжне програмне забезпечення обробляє два типи HTTP-запитів...
### Попередні CORS-запити { #cors-preflight-requests }
Це будь-які `OPTIONS` - запити, що містять заголовки `Origin` та `Access-Control-Request-Method`.
У такому випадку проміжне програмне забезпечення перехопить вхідний запит і відповість відповідними CORS-заголовками, повертаючи або `200`, або `400` для інформаційних цілей.
### Прості запити { #simple-requests }
Будь-які запити із заголовком `Origin`. У цьому випадку проміжне програмне забезпечення пропустить запит як звичайний, але додасть відповідні CORS-заголовки у відповідь.
## Додаткова інформація { #more-info }
Більше про CORS можна дізнатися в [документації Mozilla про CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
/// note | Технічні деталі
Також можна використовувати `from starlette.middleware.cors import CORSMiddleware`.
**FastAPI** надає кілька проміжних програмних забезпечень у `fastapi.middleware` для зручності розробників. Але більшість доступного проміжного програмного забезпечення походить безпосередньо зі Starlette.
///
================================================
FILE: docs/uk/docs/tutorial/debugging.md
================================================
# Налагодження { #debugging }
Ви можете під'єднати дебагер у вашому редакторі коду, наприклад, у Visual Studio Code або PyCharm.
## Виклик `uvicorn` { #call-uvicorn }
У вашому FastAPI-додатку імпортуйте та запустіть `uvicorn` безпосередньо:
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
### Про `__name__ == "__main__"` { #about-name-main }
Головна мета використання `__name__ == "__main__"` — це забезпечення виконання певного коду лише тоді, коли ваш файл запускається так:
```console
$ python myapp.py
```
але не виконується, коли інший файл імпортує його, наприклад:
```Python
from myapp import app
```
#### Детальніше { #more-details }
Припустимо, ваш файл називається `myapp.py`.
Якщо ви запустите його так:
```console
$ python myapp.py
```
тоді внутрішня змінна `__name__` у вашому файлі, яка створюється автоматично Python, матиме значення рядка `"__main__"`.
Отже, цей блок коду:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
буде виконаний.
---
Це не станеться, якщо ви імпортуєте цей модуль (файл).
Отже, якщо у вас є інший файл `importer.py` з:
```Python
from myapp import app
# Ще трохи коду
```
у цьому випадку автоматично створена змінна `__name__` всередині `myapp.py` не матиме значення `"__main__"`.
Отже, рядок:
```Python
uvicorn.run(app, host="0.0.0.0", port=8000)
```
не буде виконано.
/// info
Для отримання додаткової інформації дивіться [офіційну документацію Python](https://docs.python.org/3/library/__main__.html).
///
## Запуск коду з вашим дебагером { #run-your-code-with-your-debugger }
Оскільки ви запускаєте сервер Uvicorn безпосередньо з вашого коду, ви можете запустити вашу Python програму (ваш FastAPI-додаток) безпосередньо з дебагера.
---
Наприклад, у Visual Studio Code ви можете:
* Перейдіть на панель «Debug».
* «Add configuration...».
* Виберіть «Python»
* Запустіть дебагер з опцією "`Python: Current File (Integrated Terminal)`".
Після цього він запустить сервер з вашим кодом **FastAPI**, зупиниться на точках зупину тощо.
Ось як це може виглядати:
---
Якщо ви використовуєте PyCharm, ви можете:
* Відкрити меню «Run».
* Вибрати опцію «Debug...».
* Потім з'явиться контекстне меню.
* Вибрати файл для налагодження (у цьому випадку, `main.py`).
Після цього він запустить сервер з вашим кодом **FastAPI**, зупиниться на точках зупину тощо.
Ось як це може виглядати:
================================================
FILE: docs/uk/docs/tutorial/dependencies/classes-as-dependencies.md
================================================
# Класи як залежності { #classes-as-dependencies }
Перш ніж заглибитися у систему **впровадження залежностей**, оновімо попередній приклад.
## `dict` з попереднього прикладу { #a-dict-from-the-previous-example }
У попередньому прикладі ми повертали `dict` із нашого «залежного»:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Але тоді ми отримуємо `dict` у параметрі `commons` функції операції шляху.
І ми знаємо, що редактори не можуть надати багато підтримки (наприклад, автодоповнення) для `dict`, адже вони не знають їхніх ключів і типів значень.
Можна зробити краще…
## Що робить об’єкт залежністю { #what-makes-a-dependency }
Дотепер ви бачили залежності, оголошені як функції.
Але це не єдиний спосіб оголошувати залежності (хоча, ймовірно, найпоширеніший).
Ключовий момент у тому, що залежність має бути «викликаємим».
«Викликаємий» у Python - це будь-що, що Python може «викликати», як функцію.
Отже, якщо у вас є об’єкт `something` (який може й не бути функцією) і ви можете «викликати» його (виконати) так:
```Python
something()
```
або
```Python
something(some_argument, some_keyword_argument="foo")
```
тоді це «викликаємий».
## Класи як залежності { #classes-as-dependencies_1 }
Ви могли помітити, що для створення екземпляра класу Python ви використовуєте той самий синтаксис.
Наприклад:
```Python
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
```
У цьому випадку `fluffy` - екземпляр класу `Cat`.
А для створення `fluffy` ви «викликаєте» `Cat`.
Отже, клас Python також є **викликаємим**.
Тож у **FastAPI** ви можете використовувати клас Python як залежність.
Насправді **FastAPI** перевіряє, що це «викликаємий» об’єкт (функція, клас чи щось інше) і які параметри в нього оголошені.
Якщо ви передаєте «викликаємий» як залежність у **FastAPI**, він проаналізує параметри цього об’єкта і обробить їх так само, як параметри для функції операції шляху. Включно з підзалежностями.
Це також стосується викликаємих без жодних параметрів. Так само, як і для функцій операцій шляху без параметрів.
Тоді ми можемо змінити залежність `common_parameters` вище на клас `CommonQueryParams`:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Зверніть увагу на метод `__init__`, який використовують для створення екземпляра класу:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
…він має ті самі параметри, що й наш попередній `common_parameters`:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Саме ці параметри **FastAPI** використає, щоб «розв’язати» залежність.
В обох випадках буде:
- Необов’язковий параметр запиту `q`, який є `str`.
- Параметр запиту `skip`, який є `int`, зі значенням за замовчуванням `0`.
- Параметр запиту `limit`, який є `int`, зі значенням за замовчуванням `100`.
В обох випадках дані будуть перетворені, перевірені й задокументовані в схемі OpenAPI тощо.
## Використання { #use-it }
Тепер ви можете оголосити залежність, використовуючи цей клас.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
**FastAPI** викликає клас `CommonQueryParams`. Це створює «екземпляр» цього класу, і цей екземпляр буде передано як параметр `commons` у вашу функцію.
## Анотація типів проти `Depends` { #type-annotation-vs-depends }
Зверніть увагу, що вище ми двічі пишемо `CommonQueryParams`:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
Останній `CommonQueryParams` у:
```Python
... Depends(CommonQueryParams)
```
- це те, що **FastAPI** фактично використає, щоб дізнатися, яка залежність.
Саме з нього **FastAPI** витягне оголошені параметри і саме його **FastAPI** буде викликати.
---
У цьому випадку перший `CommonQueryParams` у:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, ...
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons: CommonQueryParams ...
```
////
…не має жодного особливого значення для **FastAPI**. FastAPI не використовуватиме його для перетворення даних, перевірки тощо (адже для цього використовується `Depends(CommonQueryParams)`).
Насправді ви могли б написати просто:
//// tab | Python 3.10+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons = Depends(CommonQueryParams)
```
////
…як у:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Але оголошувати тип рекомендується - так ваш редактор знатиме, що буде передано в параметр `commons`, і зможе допомагати з автодоповненням, перевірками типів тощо:
## Скорочення { #shortcut }
Але ви бачите, що тут маємо деяке дублювання коду - `CommonQueryParams` пишемо двічі:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
**FastAPI** надає скорочення для таких випадків, коли залежність - це саме клас, який **FastAPI** «викличе», щоб створити екземпляр цього класу.
Для таких випадків ви можете зробити так:
Замість того щоб писати:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons: CommonQueryParams = Depends(CommonQueryParams)
```
////
…напишіть:
//// tab | Python 3.10+
```Python
commons: Annotated[CommonQueryParams, Depends()]
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу варіанту з `Annotated`, якщо це можливо.
///
```Python
commons: CommonQueryParams = Depends()
```
////
Ви оголошуєте залежність як тип параметра і використовуєте `Depends()` без параметрів, замість того щоб вдруге писати повний клас усередині `Depends(CommonQueryParams)`.
Той самий приклад виглядатиме так:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
…і **FastAPI** знатиме, що робити.
/// tip | Порада
Якщо це здається заплутанішим, ніж корисним, просто не використовуйте це - воно не є обов’язковим.
Це лише скорочення. Адже **FastAPI** дбає про мінімізацію дублювання коду.
///
================================================
FILE: docs/uk/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md
================================================
# Залежності в декораторах операцій шляху { #dependencies-in-path-operation-decorators }
Іноді вам насправді не потрібне значення, яке повертає залежність, у вашій *функції операції шляху*.
Або залежність узагалі не повертає значення.
Але її все одно потрібно виконати/опрацювати.
Для таких випадків, замість оголошення параметра *функції операції шляху* з `Depends`, ви можете додати `list` `dependencies` до *декоратора операції шляху*.
## Додайте `dependencies` до *декоратора операції шляху* { #add-dependencies-to-the-path-operation-decorator }
*Декоратор операції шляху* приймає необов'язковий аргумент `dependencies`.
Це має бути `list` з `Depends()`:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
Ці залежності будуть виконані/оброблені так само, як звичайні залежності. Але їхні значення (якщо щось повертають) не передаватимуться у вашу *функцію операції шляху*.
/// tip | Порада
Деякі редактори перевіряють невикористані параметри функцій і показують їх як помилки.
Використовуючи ці `dependencies` у *декораторі операції шляху*, ви можете гарантувати їх виконання та водночас уникнути помилок редактора/інструментів.
Це також може допомогти уникнути плутанини для нових розробників, які бачать невикористаний параметр у вашому коді й можуть вирішити, що він зайвий.
///
/// info | Інформація
У цьому прикладі ми використовуємо вигадані власні заголовки `X-Key` і `X-Token`.
Але в реальних випадках, під час впровадження безпеки, ви отримаєте більше користі, використовуючи вбудовані [Інструменти безпеки (наступний розділ)](../security/index.md).
///
## Помилки залежностей і значення, що повертаються { #dependencies-errors-and-return-values }
Ви можете використовувати ті самі *функції* залежностей, що й зазвичай.
### Вимоги залежностей { #dependency-requirements }
Вони можуть оголошувати вимоги до запиту (наприклад, заголовки) або інші підзалежності:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
### Підіймати винятки { #raise-exceptions }
Ці залежності можуть `raise` винятки, так само як і звичайні залежності:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
### Значення, що повертаються { #return-values }
Вони можуть повертати значення або ні - ці значення не використовуватимуться.
Отже, ви можете перевикористати звичайну залежність (яка повертає значення), яку вже застосовуєте деінде, і навіть якщо значення не буде використано, залежність буде виконана:
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
## Залежності для групи операцій шляху { #dependencies-for-a-group-of-path-operations }
Далі, читаючи про структурування великих застосунків ([Більші застосунки - декілька файлів](../../tutorial/bigger-applications.md)), можливо з кількома файлами, ви дізнаєтеся, як оголосити один параметр `dependencies` для групи *операцій шляху*.
## Глобальні залежності { #global-dependencies }
Далі ми побачимо, як додати залежності до всього застосунку `FastAPI`, щоб вони застосовувалися до кожної *операції шляху*.
================================================
FILE: docs/uk/docs/tutorial/dependencies/dependencies-with-yield.md
================================================
# Залежності з yield { #dependencies-with-yield }
FastAPI підтримує залежності, які виконують деякі додаткові кроки після завершення.
Щоб це зробити, використовуйте `yield` замість `return` і напишіть додаткові кроки (код) після нього.
/// tip | Порада
Переконайтесь, що ви використовуєте `yield` лише один раз на залежність.
///
/// note | Технічні деталі
Будь-яка функція, яку можна використовувати з:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) або
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
буде придатною як залежність у **FastAPI**.
Насправді FastAPI використовує ці два декоратори внутрішньо.
///
## Залежність бази даних з `yield` { #a-database-dependency-with-yield }
Наприклад, ви можете використати це, щоб створити сесію бази даних і закрити її після завершення.
Перед створенням відповіді виконується лише код до і включно з оператором `yield`:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
Значення, передане `yield`, впроваджується в *операції шляху* та інші залежності:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
Код після оператора `yield` виконується після відповіді:
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
/// tip | Порада
Можете використовувати як `async`, так і звичайні функції.
**FastAPI** зробить усе правильно з кожною з них, так само як і зі звичайними залежностями.
///
## Залежність з `yield` та `try` { #a-dependency-with-yield-and-try }
Якщо ви використовуєте блок `try` в залежності з `yield`, ви отримаєте будь-який виняток, який був згенерований під час використання залежності.
Наприклад, якщо якийсь код десь посередині, в іншій залежності або в *операції шляху*, зробив «rollback» транзакції бази даних або створив будь-який інший виняток, ви отримаєте цей виняток у своїй залежності.
Тож ви можете обробити цей конкретний виняток усередині залежності за допомогою `except SomeException`.
Так само ви можете використовувати `finally`, щоб гарантувати виконання завершальних кроків незалежно від того, був виняток чи ні.
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
## Підзалежності з `yield` { #sub-dependencies-with-yield }
Ви можете мати підзалежності та «дерева» підзалежностей будь-якого розміру і форми, і будь-яка або всі з них можуть використовувати `yield`.
**FastAPI** гарантує, що «exit code» у кожній залежності з `yield` буде виконано в правильному порядку.
Наприклад, `dependency_c` може залежати від `dependency_b`, а `dependency_b` - від `dependency_a`:
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
І всі вони можуть використовувати `yield`.
У цьому випадку `dependency_c`, щоб виконати свій завершальний код, потребує, щоб значення з `dependency_b` (тут `dep_b`) все ще було доступним.
І, у свою чергу, `dependency_b` потребує, щоб значення з `dependency_a` (тут `dep_a`) було доступним для свого завершального коду.
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
Так само ви можете мати деякі залежності з `yield`, а інші - з `return`, і частина з них може залежати від інших.
І ви можете мати одну залежність, яка вимагає кілька інших залежностей з `yield` тощо.
Ви можете мати будь-які комбінації залежностей, які вам потрібні.
**FastAPI** подбає, щоб усе виконувалося в правильному порядку.
/// note | Технічні деталі
Це працює завдяки Python [Менеджерам контексту](https://docs.python.org/3/library/contextlib.html).
**FastAPI** використовує їх внутрішньо, щоб досягти цього.
///
## Залежності з `yield` та `HTTPException` { #dependencies-with-yield-and-httpexception }
Ви бачили, що можна використовувати залежності з `yield` і мати блоки `try`, які намагаються виконати деякий код, а потім запускають завершальний код після `finally`.
Також можна використовувати `except`, щоб перехопити згенерований виняток і щось із ним зробити.
Наприклад, ви можете підняти інший виняток, як-от `HTTPException`.
/// tip | Порада
Це доволі просунута техніка, і в більшості випадків вона вам не знадобиться, адже ви можете піднімати винятки (включно з `HTTPException`) всередині іншого коду вашого застосунку, наприклад, у *функції операції шляху*.
Але вона є, якщо вам це потрібно. 🤓
///
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
Якщо ви хочете перехоплювати винятки та створювати на їх основі користувацьку відповідь, створіть [Користувацький обробник винятків](../handling-errors.md#install-custom-exception-handlers).
## Залежності з `yield` та `except` { #dependencies-with-yield-and-except }
Якщо ви перехоплюєте виняток за допомогою `except` у залежності з `yield` і не піднімаєте його знову (або не піднімаєте новий виняток), FastAPI не зможе помітити, що стався виняток, так само як це було б у звичайному Python:
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
У цьому випадку клієнт побачить відповідь *HTTP 500 Internal Server Error*, як і має бути, з огляду на те, що ми не піднімаємо `HTTPException` або подібний виняток, але на сервері **не буде жодних логів** чи інших ознак того, що це була за помилка. 😱
### Завжди використовуйте `raise` у залежностях з `yield` та `except` { #always-raise-in-dependencies-with-yield-and-except }
Якщо ви перехоплюєте виняток у залежності з `yield`, якщо тільки ви не піднімаєте інший `HTTPException` або подібний, **вам слід повторно підняти початковий виняток**.
Ви можете повторно підняти той самий виняток, використовуючи `raise`:
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
Тепер клієнт отримає ту саму відповідь *HTTP 500 Internal Server Error*, але сервер матиме наш користувацький `InternalError` у логах. 😎
## Виконання залежностей з `yield` { #execution-of-dependencies-with-yield }
Послідовність виконання приблизно така, як на цій діаграмі. Час тече зверху вниз. І кожна колонка - це одна з частин, що взаємодіють або виконують код.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,operation: Can raise exceptions, including HTTPException
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise Exception
dep -->> handler: Raise Exception
handler -->> client: HTTP error response
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> dep: Raise Exception (e.g. HTTPException)
opt handle
dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception
end
handler -->> client: HTTP error response
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> tasks: Handle exceptions in the background task code
end
```
/// info | Інформація
Лише **одна відповідь** буде надіслана клієнту. Це може бути одна з помилкових відповідей або відповідь від *операції шляху*.
Після відправлення однієї з цих відповідей іншу відправити не можна.
///
/// tip | Порада
Якщо ви піднімаєте будь-який виняток у коді з *функції операції шляху*, він буде переданий у залежності з `yield`, включно з `HTTPException`. У більшості випадків ви захочете повторно підняти той самий виняток або новий із залежності з `yield`, щоб переконатися, що його коректно оброблено.
///
## Ранній вихід і `scope` { #early-exit-and-scope }
Зазвичай завершальний код залежностей з `yield` виконується **після того**, як відповідь надіслано клієнту.
Але якщо ви знаєте, що вам не потрібно використовувати залежність після повернення з *функції операції шляху*, ви можете використати `Depends(scope="function")`, щоб сказати FastAPI, що слід закрити залежність після повернення з *функції операції шляху*, але **до** надсилання **відповіді**.
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
`Depends()` приймає параметр `scope`, який може бути:
* `"function"`: запустити залежність перед *функцією операції шляху*, що обробляє запит, завершити залежність після завершення *функції операції шляху*, але **до** того, як відповідь буде відправлена клієнту. Тобто функція залежності буде виконуватися **навколо** *функції операції **шляху***.
* `"request"`: запустити залежність перед *функцією операції шляху*, що обробляє запит (подібно до `"function"`), але завершити **після** того, як відповідь буде відправлена клієнту. Тобто функція залежності буде виконуватися **навколо** циклу **запиту** та відповіді.
Якщо не вказано, і залежність має `yield`, за замовчуванням `scope` дорівнює `"request"`.
### `scope` для підзалежностей { #scope-for-sub-dependencies }
Коли ви оголошуєте залежність із `scope="request"` (за замовчуванням), будь-яка підзалежність також має мати `scope` рівний `"request"`.
Але залежність з `scope` рівним `"function"` може мати залежності з `scope` `"function"` і `scope` `"request"`.
Це тому, що будь-яка залежність має бути здатною виконати свій завершальний код раніше за підзалежності, оскільки вона може все ще потребувати їх під час свого завершального коду.
```mermaid
sequenceDiagram
participant client as Client
participant dep_req as Dep scope="request"
participant dep_func as Dep scope="function"
participant operation as Path Operation
client ->> dep_req: Start request
Note over dep_req: Run code up to yield
dep_req ->> dep_func: Pass dependency
Note over dep_func: Run code up to yield
dep_func ->> operation: Run path operation with dependency
operation ->> dep_func: Return from path operation
Note over dep_func: Run code after yield
Note over dep_func: ✅ Dependency closed
dep_func ->> client: Send response to client
Note over client: Response sent
Note over dep_req: Run code after yield
Note over dep_req: ✅ Dependency closed
```
## Залежності з `yield`, `HTTPException`, `except` і фоновими задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
Залежності з `yield` еволюціонували з часом, щоб покрити різні сценарії та виправити деякі проблеми.
Якщо ви хочете дізнатися, що змінювалося в різних версіях FastAPI, прочитайте про це в просунутому посібнику користувача: [Розширені залежності - Залежності з `yield`, `HTTPException`, `except` і фоновими задачами](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks).
## Менеджери контексту { #context-managers }
### Що таке «Менеджери контексту» { #what-are-context-managers }
«Менеджери контексту» - це будь-які Python-об'єкти, які можна використовувати в операторі `with`.
Наприклад, [можна використати `with`, щоб прочитати файл](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files):
```Python
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
```
Під капотом `open("./somefile.txt")` створює об'єкт, який називається «Менеджер контексту».
Коли блок `with` завершується, він гарантує закриття файлу, навіть якщо були винятки.
Коли ви створюєте залежність з `yield`, **FastAPI** внутрішньо створить для неї менеджер контексту й поєднає його з іншими пов'язаними інструментами.
### Використання менеджерів контексту в залежностях з `yield` { #using-context-managers-in-dependencies-with-yield }
/// warning | Попередження
Це, загалом, «просунута» ідея.
Якщо ви тільки починаєте з **FastAPI**, можливо, варто наразі пропустити це.
///
У Python ви можете створювати Менеджери контексту, [створивши клас із двома методами: `__enter__()` і `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers).
Ви також можете використовувати їх усередині залежностей **FastAPI** з `yield`, використовуючи
`with` або `async with` у середині функції залежності:
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
/// tip | Порада
Інший спосіб створити менеджер контексту:
* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) або
* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
використовуючи їх для декорування функції з одним `yield`.
Саме це **FastAPI** використовує внутрішньо для залежностей з `yield`.
Але вам не потрібно використовувати ці декоратори для залежностей FastAPI (і не варто).
FastAPI зробить це за вас внутрішньо.
///
================================================
FILE: docs/uk/docs/tutorial/dependencies/global-dependencies.md
================================================
# Глобальні залежності { #global-dependencies }
Для деяких типів застосунків ви можете захотіти додати залежності до всього застосунку.
Подібно до того, як ви можете [додавати `dependencies` до *декораторів операцій шляху*](dependencies-in-path-operation-decorators.md), ви можете додати їх до застосунку `FastAPI`.
У такому разі вони будуть застосовані до всіх *операцій шляху* в застосунку:
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
Усі ідеї з розділу про [додавання `dependencies` до *декораторів операцій шляху*](dependencies-in-path-operation-decorators.md) так само застосовні, але в цьому випадку - до всіх *операцій шляху* в застосунку.
## Залежності для груп *операцій шляху* { #dependencies-for-groups-of-path-operations }
Пізніше, читаючи про структуру більших застосунків ([Більші застосунки - кілька файлів](../../tutorial/bigger-applications.md)), можливо з кількома файлами, ви дізнаєтеся, як оголосити єдиний параметр `dependencies` для групи *операцій шляху*.
================================================
FILE: docs/uk/docs/tutorial/dependencies/index.md
================================================
# Залежності { #dependencies }
**FastAPI** має дуже потужну, але інтуїтивну систему **Впровадження залежностей**.
Вона створена так, щоб бути дуже простою у використанні та щоб полегшити будь-якому розробнику інтеграцію інших компонентів з **FastAPI**.
## Що таке «Впровадження залежностей» { #what-is-dependency-injection }
У програмуванні **«Впровадження залежностей»** означає, що існує спосіб для вашого коду (у цьому випадку ваших *функцій операцій шляху*) задекларувати речі, які йому потрібні для роботи: «залежності».
А потім ця система (у цьому випадку **FastAPI**) подбає про все необхідне, щоб надати вашому коду ці потрібні залежності («інжектувати» залежності).
Це дуже корисно, коли вам потрібно:
* Мати спільну логіку (одна й та сама логіка знову і знову).
* Ділитися з’єднаннями з базою даних.
* Примусово застосовувати безпеку, автентифікацію, вимоги до ролей тощо.
* І багато іншого...
Все це з мінімізацією дублювання коду.
## Перші кроки { #first-steps }
Розгляньмо дуже простий приклад. Він буде таким простим, що поки що не дуже корисним.
Але так ми зможемо зосередитися на тому, як працює система **Впровадження залежностей**.
### Створіть залежність або «залежний» { #create-a-dependency-or-dependable }
Спочатку зосередьмося на залежності.
Це просто функція, яка може приймати ті самі параметри, що й *функція операції шляху*:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *}
Ось і все.
**2 рядки**.
І вона має ту саму форму та структуру, що й усі ваші *функції операцій шляху*.
Можете думати про неї як про *функцію операції шляху* без «декоратора» (без `@app.get("/some-path")`).
І вона може повертати будь-що.
У цьому випадку ця залежність очікує:
* Необов’язковий параметр запиту `q` типу `str`.
* Необов’язковий параметр запиту `skip` типу `int`, за замовчуванням `0`.
* Необов’язковий параметр запиту `limit` типу `int`, за замовчуванням `100`.
Потім вона просто повертає `dict`, що містить ці значення.
/// info | Інформація
FastAPI додав підтримку `Annotated` (і почав її рекомендувати) у версії 0.95.0.
Якщо у вас старіша версія, ви отримаєте помилки при спробі використати `Annotated`.
Переконайтеся, що ви [Оновіть версію FastAPI](../../deployment/versions.md#upgrading-the-fastapi-versions) щонайменше до 0.95.1 перед використанням `Annotated`.
///
### Імпортуйте `Depends` { #import-depends }
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *}
### Оголосіть залежність у «залежному» { #declare-the-dependency-in-the-dependant }
Так само, як ви використовуєте `Body`, `Query` тощо з параметрами вашої *функції операції шляху*, використовуйте `Depends` з новим параметром:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *}
Хоча ви використовуєте `Depends` у параметрах вашої функції так само, як `Body`, `Query` тощо, `Depends` працює трохи інакше.
Ви передаєте в `Depends` лише один параметр.
Цей параметр має бути чимось на кшталт функції.
Ви її не викликаєте безпосередньо (не додавайте дужки в кінці), ви просто передаєте її як параметр у `Depends()`.
І ця функція приймає параметри так само, як і *функції операцій шляху*.
/// tip | Порада
У наступному розділі ви побачите, які ще «речі», окрім функцій, можна використовувати як залежності.
///
Щоразу, коли надходить новий запит, **FastAPI** подбає про:
* Виклик вашої функції-залежності («залежного») з правильними параметрами.
* Отримання результату з вашої функції.
* Присвоєння цього результату параметру у вашій *функції операції шляху*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
Таким чином ви пишете спільний код один раз, а **FastAPI** подбає про його виклик для ваших *операцій шляху*.
/// check | Перевірте
Зверніть увагу, що вам не потрібно створювати спеціальний клас і передавати його кудись у **FastAPI**, щоб «зареєструвати» його чи щось подібне.
Ви просто передаєте його в `Depends`, і **FastAPI** знає, що робити далі.
///
## Спільне використання залежностей `Annotated` { #share-annotated-dependencies }
У наведених вище прикладах видно невелике **дублювання коду**.
Коли вам потрібно використати залежність `common_parameters()`, доводиться писати весь параметр з анотацією типу та `Depends()`:
```Python
commons: Annotated[dict, Depends(common_parameters)]
```
Але оскільки ми використовуємо `Annotated`, ми можемо зберегти це значення `Annotated` у змінній і використовувати його в кількох місцях:
{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *}
/// tip | Порада
Це просто стандартний Python, це називається «псевдонім типу» і насправді не є специфічним для **FastAPI**.
Але оскільки **FastAPI** базується на стандартах Python, включно з `Annotated`, ви можете використати цей трюк у своєму коді. 😎
///
Залежності продовжать працювати як очікується, і **найкраще** те, що **інформація про типи буде збережена**, а це означає, що ваш редактор зможе й надалі надавати **автозаповнення**, **помилки в рядку** тощо. Те саме і для інших інструментів, як-от `mypy`.
Це буде особливо корисно у **великій кодовій базі**, де ви використовуєте **одні й ті самі залежності** знову і знову в **багатьох *операціях шляху***.
## Бути `async` чи не бути `async` { #to-async-or-not-to-async }
Оскільки залежності також викликатимуться **FastAPI** (так само, як і ваші *функції операцій шляху*), під час визначення ваших функцій діють ті самі правила.
Ви можете використовувати `async def` або звичайний `def`.
І ви можете оголошувати залежності з `async def` всередині звичайних *функцій операцій шляху* з `def`, або залежності з `def` всередині *функцій операцій шляху* з `async def` тощо.
Це не має значення. **FastAPI** знатиме, що робити.
/// note | Примітка
Якщо ви не впевнені, перегляньте розділ [Async: *"In a hurry?"*](../../async.md#in-a-hurry) про `async` і `await` у документації.
///
## Інтегровано з OpenAPI { #integrated-with-openapi }
Усі декларації запитів, перевірки та вимоги ваших залежностей (і субзалежностей) будуть інтегровані в ту саму схему OpenAPI.
Тож інтерактивна документація також міститиме всю інформацію з цих залежностей:
## Просте використання { #simple-usage }
Якщо придивитися, *функції операцій шляху* оголошуються для використання щоразу, коли збігаються *шлях* та *операція*, а потім **FastAPI** подбає про виклик функції з правильними параметрами, витягуючи дані із запиту.
Насправді всі (або більшість) вебфреймворків працюють так само.
Ви ніколи не викликаєте ці функції безпосередньо. Їх викликає ваш фреймворк (у цьому випадку **FastAPI**).
За допомогою системи впровадження залежностей ви також можете вказати **FastAPI**, що ваша *функція операції шляху* також «залежить» від чогось, що має бути виконано до вашої *функції операції шляху*, і **FastAPI** подбає про виконання цього та «інжектування» результатів.
Інші поширені терміни для цієї самої ідеї «впровадження залежностей»:
* ресурси
* провайдери
* сервіси
* інжектовані об’єкти
* компоненти
## Плагіни **FastAPI** { #fastapi-plug-ins }
Інтеграції та «плагіни» можна будувати за допомогою системи **Впровадження залежностей**. Але насправді **немає потреби створювати «плагіни»**, оскільки, використовуючи залежності, можна оголосити безмежну кількість інтеграцій та взаємодій, які стають доступними для ваших *функцій операцій шляху*.
І залежності можна створювати дуже просто та інтуїтивно, що дозволяє вам просто імпортувати потрібні пакунки Python і інтегрувати їх з вашими функціями API за кілька рядків коду, буквально.
Ви побачите приклади цього в наступних розділах, про реляційні та NoSQL бази даних, безпеку тощо.
## Сумісність **FastAPI** { #fastapi-compatibility }
Простота системи впровадження залежностей робить **FastAPI** сумісним з:
* усіма реляційними базами даних
* NoSQL базами даних
* зовнішніми пакунками
* зовнішніми API
* системами автентифікації та авторизації
* системами моніторингу використання API
* системами інжекції даних у відповідь
* тощо.
## Просто і потужно { #simple-and-powerful }
Хоча ієрархічна система впровадження залежностей дуже проста у визначенні та використанні, вона все ще дуже потужна.
Ви можете визначати залежності, які своєю чергою можуть визначати власні залежності.
Зрештою будується ієрархічне дерево залежностей, і система **Впровадження залежностей** подбає про розв’язання всіх цих залежностей (і їхніх субзалежностей) та надання (інжектування) результатів на кожному кроці.
Наприклад, припустімо, у вас є 4 кінцеві точки API (*операції шляху*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
тоді ви могли б додати різні вимоги до дозволів для кожної з них лише за допомогою залежностей і субзалежностей:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Інтегровано з **OpenAPI** { #integrated-with-openapi_1 }
Усі ці залежності, декларуючи свої вимоги, також додають параметри, перевірки тощо до ваших *операцій шляху*.
**FastAPI** подбає про додавання всього цього до схеми OpenAPI, щоб це відображалося в інтерактивних системах документації.
================================================
FILE: docs/uk/docs/tutorial/dependencies/sub-dependencies.md
================================================
# Підзалежності { #sub-dependencies }
Ви можете створювати залежності, які мають підзалежності.
Вони можуть бути настільки глибокими, наскільки потрібно.
FastAPI подбає про їх розв'язання.
## Перша залежність «dependable» { #first-dependency-dependable }
Можна створити першу залежність («dependable») так:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *}
Вона оголошує необов'язковий параметр запиту `q` типу `str`, а потім просто повертає його.
Це досить просто (не дуже корисно), але допоможе зосередитися на тому, як працюють підзалежності.
## Друга залежність, «dependable» і «dependant» { #second-dependency-dependable-and-dependant }
Далі ви можете створити іншу функцію залежності («dependable»), яка водночас оголошує власну залежність (тож вона також є «dependant»):
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *}
Зосередьмося на оголошених параметрах:
- Хоча ця функція сама є залежністю («dependable»), вона також оголошує іншу залежність (вона «залежить» від чогось).
- Вона залежить від `query_extractor` і присвоює значення, яке він повертає, параметру `q`.
- Вона також оголошує необов'язкове кукі `last_query` типу `str`.
- Якщо користувач не надав параметр запиту `q`, ми використовуємо останній запит, який зберегли раніше в кукі.
## Використання залежності { #use-the-dependency }
Потім ми можемо використати залежність так:
{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *}
/// info | Інформація
Зверніть увагу, що ми оголошуємо лише одну залежність у функції операції шляху — `query_or_cookie_extractor`.
Але FastAPI знатиме, що спочатку треба розв'язати `query_extractor`, щоб передати його результат у `query_or_cookie_extractor` під час виклику.
///
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Використання тієї ж залежності кілька разів { #using-the-same-dependency-multiple-times }
Якщо одна з ваших залежностей оголошена кілька разів для однієї операції шляху, наприклад, кілька залежностей мають спільну підзалежність, FastAPI знатиме, що цю підзалежність потрібно викликати лише один раз на запит.
І він збереже повернуте значення у «кеш» і передасть його всім «dependants», яким воно потрібне в цьому конкретному запиті, замість того щоб викликати залежність кілька разів для одного й того ж запиту.
У просунутому сценарії, коли ви знаєте, що залежність має викликатися на кожному кроці (можливо, кілька разів) у межах того самого запиту замість використання «кешованого» значення, ви можете встановити параметр `use_cache=False` при використанні `Depends`:
//// tab | Python 3.10+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
return {"fresh_value": fresh_value}
```
////
//// tab | Python 3.10+ без Annotated
/// tip | Порада
Надавайте перевагу версії з `Annotated`, якщо це можливо.
///
```Python hl_lines="1"
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
```
////
## Підсумок { #recap }
Попри всі модні терміни, система впровадження залежностей досить проста.
Це просто функції, які виглядають так само, як функції операцій шляху.
Втім вона дуже потужна і дозволяє оголошувати довільно глибоко вкладені «графи» залежностей (дерева).
/// tip | Порада
Усе це може здаватися не надто корисним на простих прикладах.
Але ви побачите, наскільки це корисно, у розділах про **безпеку**.
І також побачите, скільки коду це вам заощадить.
///
================================================
FILE: docs/uk/docs/tutorial/encoder.md
================================================
# JSON-сумісний кодувальник { #json-compatible-encoder }
Існують випадки, коли вам може знадобитися перетворити тип даних (наприклад, модель Pydantic) на щось сумісне з JSON (наприклад, `dict`, `list` тощо).
Наприклад, якщо вам потрібно зберегти це в базі даних.
Для цього **FastAPI** надає функцію `jsonable_encoder()`.
## Використання `jsonable_encoder` { #using-the-jsonable-encoder }
Давайте уявимо, що у вас є база даних `fake_db`, яка приймає лише дані, сумісні з JSON.
Наприклад, вона не приймає об'єкти типу `datetime`, оскільки вони не сумісні з JSON.
Отже, об'єкт типу `datetime` потрібно перетворити на `str`, який містить дані в [форматі ISO](https://en.wikipedia.org/wiki/ISO_8601).
Так само ця база даних не прийматиме модель Pydantic (об'єкт з атрибутами), а лише `dict`.
Ви можете використовувати `jsonable_encoder` для цього.
Вона приймає об'єкт, такий як модель Pydantic, і повертає його версію, сумісну з JSON:
{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *}
У цьому прикладі вона конвертує модель Pydantic у `dict`, а `datetime` у `str`.
Результат виклику цієї функції — це щось, що можна кодувати з використанням стандарту Python [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps).
Вона не повертає великий `str`, який містить дані у форматі JSON (як рядок). Вона повертає стандартну структуру даних Python (наприклад, `dict`) зі значеннями та підзначеннями, які є сумісними з JSON.
/// note | Примітка
`jsonable_encoder` фактично використовується **FastAPI** внутрішньо для перетворення даних. Проте вона корисна в багатьох інших сценаріях.
///
================================================
FILE: docs/uk/docs/tutorial/extra-data-types.md
================================================
# Додаткові типи даних { #extra-data-types }
До цього часу ви використовували загальнопоширені типи даних, такі як:
* `int`
* `float`
* `str`
* `bool`
Але ви також можете використовувати більш складні типи даних.
І ви все ще матимете ті ж можливості, які були показані до цього:
* Чудова підтримка редактора.
* Конвертація даних з вхідних запитів.
* Конвертація даних для відповіді.
* Валідація даних.
* Автоматична анотація та документація.
## Інші типи даних { #other-data-types }
Ось додаткові типи даних для використання:
* `UUID`:
* Стандартний "Універсальний унікальний ідентифікатор", який часто використовується як ID у багатьох базах даних та системах.
* У запитах та відповідях буде представлений як `str`.
* `datetime.datetime`:
* Пайтонівський `datetime.datetime`.
* У запитах та відповідях буде представлений як `str` у форматі ISO 8601, як: `2008-09-15T15:53:00+05:00`.
* `datetime.date`:
* Пайтонівський `datetime.date`.
* У запитах та відповідях буде представлений як `str` у форматі ISO 8601, як: `2008-09-15`.
* `datetime.time`:
* Пайтонівський `datetime.time`.
* У запитах та відповідях буде представлений як `str` у форматі ISO 8601, як: `14:23:55.003`.
* `datetime.timedelta`:
* Пайтонівський `datetime.timedelta`.
* У запитах та відповідях буде представлений як `float` загальної кількості секунд.
* Pydantic також дозволяє представляти це як "ISO 8601 time diff encoding", [дивіться документацію для отримання додаткової інформації](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers).
* `frozenset`:
* У запитах і відповідях це буде оброблено так само, як і `set`:
* У запитах список буде зчитано, дублікати буде видалено, і його буде перетворено на `set`.
* У відповідях `set` буде перетворено на `list`.
* Згенерована схема буде вказувати, що значення `set` є унікальними (з використанням JSON Schema's `uniqueItems`).
* `bytes`:
* Стандартний Пайтонівський `bytes`.
* У запитах і відповідях це буде оброблено як `str`.
* Згенерована схема буде вказувати, що це `str` з "форматом" `binary`.
* `Decimal`:
* Стандартний Пайтонівський `Decimal`.
* У запитах і відповідях це буде оброблено так само, як і `float`.
* Ви можете перевірити всі дійсні типи даних Pydantic тут: [типи даних Pydantic](https://docs.pydantic.dev/latest/usage/types/types/).
## Приклад { #example }
Ось приклад *операції шляху* з параметрами, використовуючи деякі з вищезазначених типів.
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *}
Зверніть увагу, що параметри всередині функції мають свій звичайний тип даних, і ви можете, наприклад, виконувати звичайні маніпуляції з датами, такі як:
{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *}
================================================
FILE: docs/uk/docs/tutorial/extra-models.md
================================================
# Додаткові моделі { #extra-models }
Продовжуючи попередній приклад, часто потрібно мати більше ніж одну пов’язану модель.
Особливо це стосується моделей користувача, тому що:
* **вхідна модель** повинна мати пароль.
* **вихідна модель** не повинна містити пароль.
* **модель бази даних**, ймовірно, повинна містити хеш пароля.
/// danger | Обережно
Ніколи не зберігайте паролі користувачів у відкритому вигляді. Завжди зберігайте «безпечний хеш», який потім можна перевірити.
Якщо ви ще не знаєте, що таке «хеш пароля», ви дізнаєтесь у [розділах про безпеку](security/simple-oauth2.md#password-hashing).
///
## Кілька моделей { #multiple-models }
Ось загальна ідея того, як можуть виглядати моделі з їхніми полями пароля та місцями використання:
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
### Про `**user_in.model_dump()` { #about-user-in-model-dump }
#### `.model_dump()` у Pydantic { #pydantics-model-dump }
`user_in` - це модель Pydantic класу `UserIn`.
Моделі Pydantic мають метод `.model_dump()`, який повертає `dict` з даними моделі.
Отже, якщо ми створимо об’єкт Pydantic `user_in` так:
```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```
і викличемо:
```Python
user_dict = user_in.model_dump()
```
тепер ми маємо `dict` з даними у змінній `user_dict` (це `dict`, а не об’єкт моделі Pydantic).
А якщо викликати:
```Python
print(user_dict)
```
ми отримаємо Python `dict` з:
```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```
#### Розпакування `dict` { #unpacking-a-dict }
Якщо взяти `dict`, наприклад `user_dict`, і передати його у функцію (або клас) як `**user_dict`, Python «розпакує» його. Ключі та значення `user_dict` будуть передані безпосередньо як іменовані аргументи.
Отже, продовжуючи з `user_dict` вище, запис:
```Python
UserInDB(**user_dict)
```
дасть еквівалентний результат:
```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```
А точніше, використовуючи безпосередньо `user_dict`, з будь-яким його вмістом у майбутньому:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```
#### Модель Pydantic зі вмісту іншої { #a-pydantic-model-from-the-contents-of-another }
Як у прикладі вище ми отримали `user_dict` з `user_in.model_dump()`, цей код:
```Python
user_dict = user_in.model_dump()
UserInDB(**user_dict)
```
буде еквівалентним:
```Python
UserInDB(**user_in.model_dump())
```
...тому що `user_in.model_dump()` - це `dict`, а ми змушуємо Python «розпакувати» його, передаючи в `UserInDB` з префіксом `**`.
Тож ми отримуємо модель Pydantic з даних іншої моделі Pydantic.
#### Розпакування `dict` і додаткові ключові аргументи { #unpacking-a-dict-and-extra-keywords }
Додаючи додатковий іменований аргумент `hashed_password=hashed_password`, як тут:
```Python
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
```
...у підсумку це дорівнює:
```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```
/// warning | Попередження
Додаткові допоміжні функції `fake_password_hasher` і `fake_save_user` лише демонструють можливий потік даних і, звісно, не забезпечують реальної безпеки.
///
## Зменшення дублювання { #reduce-duplication }
Зменшення дублювання коду - одна з ключових ідей у **FastAPI**.
Адже дублювання коду підвищує ймовірність помилок, проблем безпеки, розсинхронізації коду (коли ви оновлюєте в одному місці, але не в інших) тощо.
І ці моделі спільно використовують багато даних та дублюють назви і типи атрибутів.
Можна зробити краще.
Можна оголосити модель `UserBase`, яка буде базовою для інших моделей. Потім створити підкласи цієї моделі, що наслідуватимуть її атрибути (оголошення типів, валідацію тощо).
Уся конвертація даних, валідація, документація тощо працюватимуть як зазвичай.
Таким чином, ми оголошуємо лише відмінності між моделями (з відкритим `password`, з `hashed_password` і без пароля):
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
## `Union` або `anyOf` { #union-or-anyof }
Ви можете оголосити відповідь як `Union` двох або більше типів - це означає, що відповідь може бути будь-якого з них.
В OpenAPI це буде визначено як `anyOf`.
Для цього використайте стандартну підказку типу Python [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union):
/// note | Примітка
Під час визначення [`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions) спочатку вказуйте найконкретніший тип, а потім менш конкретний. У прикладі нижче більш конкретний `PlaneItem` стоїть перед `CarItem` у `Union[PlaneItem, CarItem]`.
///
{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *}
### `Union` у Python 3.10 { #union-in-python-3-10 }
У цьому прикладі ми передаємо `Union[PlaneItem, CarItem]` як значення аргументу `response_model`.
Оскільки ми передаємо його як значення аргументу, а не в анотації типу, потрібно використовувати `Union` навіть у Python 3.10.
Якби це була анотація типу, можна було б використати вертикальну риску, наприклад:
```Python
some_variable: PlaneItem | CarItem
```
Але якщо записати це як присвоєння `response_model=PlaneItem | CarItem`, отримаємо помилку, тому що Python спробує виконати невалідну операцію між `PlaneItem` і `CarItem`, замість того щоб трактувати це як анотацію типу.
## Список моделей { #list-of-models }
Аналогічно можна оголошувати відповіді як списки об’єктів.
Для цього використайте стандартний Python `list`:
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
## Відповідь з довільним `dict` { #response-with-arbitrary-dict }
Також можна оголосити відповідь, використовуючи звичайний довільний `dict`, вказавши лише типи ключів і значень, без моделі Pydantic.
Це корисно, якщо ви заздалегідь не знаєте допустимі назви полів/атрибутів (які були б потрібні для моделі Pydantic).
У такому разі можна використати `dict`:
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
## Підсумок { #recap }
Використовуйте кілька моделей Pydantic і вільно наслідуйте для кожного випадку.
Не обов’язково мати одну модель даних на сутність, якщо ця сутність може мати різні «стани». Як у випадку сутності користувача зі станами: з `password`, з `password_hash` і без пароля.
================================================
FILE: docs/uk/docs/tutorial/first-steps.md
================================================
# Перші кроки { #first-steps }
Найпростіший файл FastAPI може виглядати так:
{* ../../docs_src/first_steps/tutorial001_py310.py *}
Скопіюйте це до файлу `main.py`.
Запустіть live-сервер:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
У виводі є рядок приблизно такого змісту:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Цей рядок показує URL, за яким ваш застосунок обслуговується на вашій локальній машині.
### Перевірте { #check-it }
Відкрийте браузер за адресою [http://127.0.0.1:8000](http://127.0.0.1:8000).
Ви побачите JSON-відповідь:
```JSON
{"message": "Hello World"}
```
### Інтерактивна API документація { #interactive-api-docs }
Тепер перейдіть сюди [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите автоматичну інтерактивну API документацію (надається [Swagger UI](https://github.com/swagger-api/swagger-ui)):

### Альтернативна API документація { #alternative-api-docs }
А тепер перейдіть сюди [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
Ви побачите альтернативну автоматичну документацію (надається [ReDoc](https://github.com/Rebilly/ReDoc)):

### OpenAPI { #openapi }
**FastAPI** генерує «схему» з усім вашим API, використовуючи стандарт **OpenAPI** для визначення API.
#### «Схема» { #schema }
«Схема» — це визначення або опис чогось. Це не код, який його реалізує, а просто абстрактний опис.
#### API «схема» { #api-schema }
У цьому випадку, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) є специфікацією, яка визначає, як описати схему вашого API.
Це визначення схеми включає шляхи (paths) вашого API, можливі параметри, які вони приймають, тощо.
#### «Схема» даних { #data-schema }
Термін «схема» також може відноситися до форми деяких даних, наприклад, вмісту JSON.
У цьому випадку це означає атрибути JSON і типи даних, які вони мають, тощо.
#### OpenAPI і JSON Schema { #openapi-and-json-schema }
OpenAPI описує схему API для вашого API. І ця схема включає визначення (або «схеми») даних, що надсилаються та отримуються вашим API, за допомогою **JSON Schema**, стандарту для схем даних JSON.
#### Перевірте `openapi.json` { #check-the-openapi-json }
Якщо вас цікавить, як виглядає «сирий» OpenAPI schema, FastAPI автоматично генерує JSON (schema) з описами всього вашого API.
Ви можете побачити це напряму тут: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json).
Ви побачите JSON, що починається приблизно так:
```JSON
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### Для чого потрібний OpenAPI { #what-is-openapi-for }
OpenAPI schema — це те, на чому працюють дві включені системи інтерактивної документації.
Також існують десятки альтернатив, і всі вони засновані на OpenAPI. Ви можете легко додати будь-яку з цих альтернатив до вашого застосунку, створеного з **FastAPI**.
Ви також можете використовувати його для автоматичної генерації коду для клієнтів, які взаємодіють з вашим API. Наприклад, для фронтенд-, мобільних або IoT-застосунків.
### Налаштуйте `entrypoint` застосунку в `pyproject.toml` { #configure-the-app-entrypoint-in-pyproject-toml }
Ви можете налаштувати, де знаходиться ваш застосунок, у файлі `pyproject.toml`, приблизно так:
```toml
[tool.fastapi]
entrypoint = "main:app"
```
Цей `entrypoint` повідомить команді `fastapi`, що вона має імпортувати застосунок так:
```python
from main import app
```
Якщо структура вашого коду виглядала б так:
```
.
├── backend
│ ├── main.py
│ ├── __init__.py
```
Тоді ви б задали `entrypoint` як:
```toml
[tool.fastapi]
entrypoint = "backend.main:app"
```
що було б еквівалентно:
```python
from backend.main import app
```
### `fastapi dev` із шляхом { #fastapi-dev-with-path }
Ви також можете передати шлях до файлу в команду `fastapi dev`, і вона вгадає обʼєкт FastAPI app, який слід використовувати:
```console
$ fastapi dev main.py
```
Але вам доведеться щоразу памʼятати передавати правильний шлях під час виклику команди `fastapi`.
Крім того, інші інструменти можуть не знайти його, наприклад [Розширення VS Code](../editor-support.md) або [FastAPI Cloud](https://fastapicloud.com), тому рекомендується використовувати `entrypoint` у `pyproject.toml`.
### Розгорніть ваш застосунок (необовʼязково) { #deploy-your-app-optional }
За бажанням ви можете розгорнути ваш FastAPI-застосунок у [FastAPI Cloud](https://fastapicloud.com), перейдіть і приєднайтеся до списку очікування, якщо ви цього ще не зробили. 🚀
Якщо у вас вже є обліковий запис **FastAPI Cloud** (ми запросили вас зі списку очікування 😉), ви можете розгорнути ваш застосунок однією командою.
Перед розгортанням переконайтеся, що ви увійшли:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
Потім розгорніть ваш застосунок:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
Ось і все! Тепер ви можете отримати доступ до вашого застосунку за цим URL. ✨
## Підібʼємо підсумки, крок за кроком { #recap-step-by-step }
### Крок 1: імпортуємо `FastAPI` { #step-1-import-fastapi }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
`FastAPI` — це клас у Python, який надає всю функціональність для вашого API.
/// note | Технічні деталі
`FastAPI` — це клас, який успадковується безпосередньо від `Starlette`.
Ви також можете використовувати всю функціональність [Starlette](https://www.starlette.dev/) у `FastAPI`.
///
### Крок 2: створюємо «екземпляр» `FastAPI` { #step-2-create-a-fastapi-instance }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
Тут змінна `app` буде «екземпляром» класу `FastAPI`.
Це буде головна точка взаємодії для створення всього вашого API.
### Крок 3: створіть *операцію шляху* { #step-3-create-a-path-operation }
#### Шлях { #path }
«Шлях» тут означає останню частину URL, починаючи з першого `/`.
Отже, у такому URL, як:
```
https://example.com/items/foo
```
...шлях буде:
```
/items/foo
```
/// info
«Шлях» також зазвичай називають «ендпоінтом» або «маршрутом».
///
Під час створення API «шлях» є основним способом розділити «завдання» і «ресурси».
#### Операція { #operation }
«Операція» тут означає один з HTTP «методів».
Один з:
* `POST`
* `GET`
* `PUT`
* `DELETE`
...та більш екзотичних:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
У протоколі HTTP ви можете спілкуватися з кожним шляхом, використовуючи один (або кілька) з цих «методів».
---
Під час створення API зазвичай використовують ці конкретні HTTP методи, щоб виконати певну дію.
Зазвичай використовують:
* `POST`: щоб створити дані.
* `GET`: щоб читати дані.
* `PUT`: щоб оновити дані.
* `DELETE`: щоб видалити дані.
Отже, в OpenAPI кожен з HTTP методів називається «операцією».
Ми також будемо називати їх «**операціями**».
#### Визначте *декоратор операції шляху* { #define-a-path-operation-decorator }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
Декоратор `@app.get("/")` повідомляє **FastAPI**, що функція одразу нижче відповідає за обробку запитів, які надходять до:
* шляху `/`
* використовуючи get операція
/// info | `@decorator` Інформація
Синтаксис `@something` у Python називається «декоратором».
Ви розташовуєте його над функцією. Як гарний декоративний капелюх (мабуть, звідти походить термін).
«Декоратор» бере функцію нижче і виконує з нею якусь дію.
У нашому випадку, цей декоратор повідомляє **FastAPI**, що функція нижче відповідає **шляху** `/` і **операції** `get`.
Це і є «**декоратор операції шляху**».
///
Можна також використовувати інші операції:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
І більш екзотичні:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
/// tip
Ви можете використовувати кожну операцію (HTTP-метод) як забажаєте.
**FastAPI** не навʼязує жодного конкретного значення.
Наведена тут інформація подана як настанова, а не вимога.
Наприклад, під час використання GraphQL ви зазвичай виконуєте всі дії, використовуючи лише `POST` операції.
///
### Крок 4: визначте **функцію операції шляху** { #step-4-define-the-path-operation-function }
Ось наша «**функція операції шляху**»:
* **шлях**: це `/`.
* **операція**: це `get`.
* **функція**: це функція нижче «декоратора» (нижче `@app.get("/")`).
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
Це функція Python.
**FastAPI** викликатиме її щоразу, коли отримає запит до URL «`/`», використовуючи операцію `GET`.
У цьому випадку це `async` функція.
---
Ви також можете визначити її як звичайну функцію замість `async def`:
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
/// note
Якщо ви не знаєте різницю, подивіться [Асинхронність: *«Поспішаєте?»*](../async.md#in-a-hurry).
///
### Крок 5: поверніть вміст { #step-5-return-the-content }
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
Ви можете повернути `dict`, `list`, а також окремі значення `str`, `int` тощо.
Також можна повернути моделі Pydantic (про це ви дізнаєтесь пізніше).
Існує багато інших обʼєктів і моделей, які будуть автоматично конвертовані в JSON (зокрема ORM тощо). Спробуйте використати свої улюблені — велика ймовірність, що вони вже підтримуються.
### Крок 6: розгорніть його { #step-6-deploy-it }
Розгорніть ваш застосунок у **[FastAPI Cloud](https://fastapicloud.com)** однією командою: `fastapi deploy`. 🎉
#### Про FastAPI Cloud { #about-fastapi-cloud }
**[FastAPI Cloud](https://fastapicloud.com)** створено тим самим автором і командою, які стоять за **FastAPI**.
Він спрощує процес **створення**, **розгортання** та **доступу** до API з мінімальними зусиллями.
Він переносить той самий **досвід розробника** зі створення застосунків на FastAPI на **розгортання** їх у хмарі. 🎉
FastAPI Cloud — основний спонсор і джерело фінансування для open source проєктів *FastAPI and friends*. ✨
#### Розгортання в інших хмарних провайдерах { #deploy-to-other-cloud-providers }
FastAPI — це open source і базується на стандартах. Ви можете розгортати FastAPI-застосунки у будь-якого хмарного провайдера на ваш вибір.
Дотримуйтеся інструкцій вашого хмарного провайдера, щоб розгорнути FastAPI-застосунки з їхньою допомогою. 🤓
## Підібʼємо підсумки { #recap }
* Імпортуйте `FastAPI`.
* Створіть екземпляр `app`.
* Напишіть **декоратор операції шляху**, використовуючи декоратори на кшталт `@app.get("/")`.
* Визначте **функцію операції шляху**; наприклад, `def root(): ...`.
* Запустіть сервер розробки командою `fastapi dev`.
* За бажанням розгорніть ваш застосунок за допомогою `fastapi deploy`.
================================================
FILE: docs/uk/docs/tutorial/handling-errors.md
================================================
# Обробка помилок { #handling-errors }
Є багато ситуацій, коли вам потрібно повідомити про помилку клієнта, який використовує ваш API.
Цим клієнтом може бути браузер із фронтендом, код іншого розробника, IoT-пристрій тощо.
Можливо, вам потрібно повідомити клієнта, що:
* У нього недостатньо прав для виконання цієї операції.
* Він не має доступу до цього ресурсу.
* Елемент, до якого він намагається отримати доступ, не існує.
* тощо.
У таких випадках зазвичай повертається **код статусу HTTP** у діапазоні **400** (від 400 до 499).
Це схоже на коди статусу HTTP 200 (від 200 до 299). Ці «200» коди статусу означають, що якимось чином запит був «успішним».
Коди статусу в діапазоні 400 означають, що сталася помилка з боку клієнта.
Пам'ятаєте всі ці помилки **«404 Not Found»** (і жарти про них)?
## Використання `HTTPException` { #use-httpexception }
Щоб повернути HTTP-відповіді з помилками клієнту, використовуйте `HTTPException`.
### Імпорт `HTTPException` { #import-httpexception }
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
### Згенеруйте `HTTPException` у своєму коді { #raise-an-httpexception-in-your-code }
`HTTPException` - це звичайна помилка Python із додатковими даними, які стосуються API.
Оскільки це помилка Python, ви не `return` її, а `raise` її.
Це також означає, що якщо ви перебуваєте всередині допоміжної функції, яку викликаєте всередині своєї *функції операції шляху*, і там згенеруєте `HTTPException` всередині цієї допоміжної функції, то решта коду в *функції операції шляху* не буде виконана. Запит одразу завершиться, і HTTP-помилка з `HTTPException` буде надіслана клієнту.
Перевага генерації виключення замість повернення значення стане більш очевидною в розділі про залежності та безпеку.
У цьому прикладі, коли клієнт запитує елемент за ID, якого не існує, згенеруйте виключення з кодом статусу `404`:
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
### Отримана відповідь { #the-resulting-response }
Якщо клієнт робить запит за шляхом `http://example.com/items/foo` (де `item_id` `"foo"`), він отримає код статусу HTTP 200 і JSON відповідь:
```JSON
{
"item": "The Foo Wrestlers"
}
```
Але якщо клієнт робить запит на `http://example.com/items/bar` (де `item_id` має не існуюче значення `"bar"`), то отримає код статусу HTTP 404 (помилка «не знайдено») та JSON відповідь:
```JSON
{
"detail": "Item not found"
}
```
/// tip | Порада
Під час генерації `HTTPException` ви можете передати будь-яке значення, яке може бути перетворене в JSON, як параметр `detail`, а не лише `str`.
Ви можете передати `dict`, `list` тощо.
Вони обробляються автоматично за допомогою **FastAPI** та перетворюються в JSON.
///
## Додавання власних заголовків { #add-custom-headers }
Є деякі ситуації, коли корисно мати можливість додавати власні заголовки до HTTP-помилки. Наприклад, для деяких типів безпеки.
Ймовірно, вам не доведеться використовувати це безпосередньо у своєму коді.
Але якщо вам знадобиться це для складного сценарію, ви можете додати власні заголовки:
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
## Встановлення власних обробників виключень { #install-custom-exception-handlers }
Ви можете додати власні обробники виключень за допомогою [тих самих утиліт для виключень зі Starlette](https://www.starlette.dev/exceptions/).
Припустімо, у вас є власне виключення `UnicornException`, яке ви (або бібліотека, яку ви використовуєте) можете `raise`.
І ви хочете обробляти це виключення глобально за допомогою FastAPI.
Ви можете додати власний обробник виключень за допомогою `@app.exception_handler()`:
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
Тут, якщо ви звернетеся до `/unicorns/yolo`, *операція шляху* згенерує (`raise`) `UnicornException`.
Але вона буде оброблена функцією-обробником `unicorn_exception_handler`.
Отже, ви отримаєте зрозумілу помилку з кодом статусу HTTP `418` і JSON-вмістом:
```JSON
{"message": "Oops! yolo did something. There goes a rainbow..."}
```
/// note | Технічні деталі
Ви також можете використовувати `from starlette.requests import Request` і `from starlette.responses import JSONResponse`.
**FastAPI** надає ті самі `starlette.responses`, що й `fastapi.responses`, просто для зручності для вас, розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette. Те ж саме з `Request`.
///
## Перевизначення обробників виключень за замовчуванням { #override-the-default-exception-handlers }
**FastAPI** має кілька обробників виключень за замовчуванням.
Ці обробники відповідають за повернення стандартних JSON-відповідей, коли ви `raise` `HTTPException`, а також коли запит містить некоректні дані.
Ви можете перевизначити ці обробники виключень власними.
### Перевизначення виключень валідації запиту { #override-request-validation-exceptions }
Коли запит містить некоректні дані, **FastAPI** внутрішньо генерує `RequestValidationError`.
І також включає обробник виключень за замовчуванням для нього.
Щоб перевизначити його, імпортуйте `RequestValidationError` і використовуйте його з `@app.exception_handler(RequestValidationError)` для декорування обробника виключень.
Обробник виключень отримає `Request` і саме виключення.
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
Тепер, якщо ви перейдете за посиланням `/items/foo`, замість того, щоб отримати стандартну JSON-помилку:
```JSON
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
ви отримаєте текстову версію:
```
Validation errors:
Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
```
### Перевизначення обробника помилок `HTTPException` { #override-the-httpexception-error-handler }
Аналогічно, ви можете перевизначити обробник `HTTPException`.
Наприклад, ви можете захотіти повернути відповідь у вигляді простого тексту замість JSON для цих помилок:
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
/// note | Технічні деталі
Ви також можете використовувати `from starlette.responses import PlainTextResponse`.
**FastAPI** надає ті самі `starlette.responses`, що й `fastapi.responses`, просто для зручності для вас, розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette.
///
/// warning | Попередження
Пам’ятайте, що `RequestValidationError` містить інформацію про назву файлу та рядок, де сталася помилка валідації, щоб за потреби ви могли показати це у своїх логах із відповідною інформацією.
Але це означає, що якщо ви просто перетворите це на рядок і повернете цю інформацію напряму, ви можете розкрити трохи інформації про вашу систему, тому тут код витягає та показує кожну помилку незалежно.
///
### Використання тіла `RequestValidationError` { #use-the-requestvalidationerror-body }
`RequestValidationError` містить `body`, який він отримав із некоректними даними.
Ви можете використовувати це під час розробки свого додатка, щоб логувати тіло запиту та налагоджувати його, повертати користувачеві тощо.
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
Тепер спробуйте надіслати некоректний елемент, наприклад:
```JSON
{
"title": "towel",
"size": "XL"
}
```
Ви отримаєте відповідь, яка повідомить вам, що дані є некоректними, і міститиме отримане тіло запиту:
```JSON hl_lines="12-15"
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
```
#### `HTTPException` FastAPI проти `HTTPException` Starlette { #fastapis-httpexception-vs-starlettes-httpexception }
**FastAPI** має власний `HTTPException`.
І клас помилки `HTTPException` в **FastAPI** успадковується від класу помилки `HTTPException` у Starlette.
Єдина різниця полягає в тому, що `HTTPException` в **FastAPI** приймає будь-які дані, які можна перетворити на JSON, для поля `detail`, тоді як `HTTPException` у Starlette приймає тільки рядки.
Отже, ви можете продовжувати генерувати `HTTPException` **FastAPI** як зазвичай у своєму коді.
Але коли ви реєструєте обробник виключень, слід реєструвати його для `HTTPException` зі Starlette.
Таким чином, якщо будь-яка частина внутрішнього коду Starlette або розширення чи плагін Starlette згенерує Starlette `HTTPException`, ваш обробник зможе перехопити та обробити її.
У цьому прикладі, щоб мати можливість використовувати обидва `HTTPException` в одному коді, виключення Starlette перейменовується на `StarletteHTTPException`:
```Python
from starlette.exceptions import HTTPException as StarletteHTTPException
```
### Повторне використання обробників виключень **FastAPI** { #reuse-fastapis-exception-handlers }
Якщо ви хочете використовувати виключення разом із такими ж обробниками виключень за замовчуванням, як у **FastAPI**, ви можете імпортувати та повторно використати обробники виключень за замовчуванням із `fastapi.exception_handlers`:
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
У цьому прикладі ви просто друкуєте помилку з дуже виразним повідомленням, але ви зрозуміли основну ідею. Ви можете використовувати виключення, а потім просто повторно використати обробники виключень за замовчуванням.
================================================
FILE: docs/uk/docs/tutorial/header-param-models.md
================================================
# Моделі параметрів заголовків { #header-parameter-models }
Якщо у вас є група пов’язаних **параметрів заголовків**, ви можете створити **Pydantic модель** для їх оголошення.
Це дозволить вам повторно **використовувати модель** в **різних місцях**, а також оголосити валідації та метадані для всіх параметрів одночасно. 😎
/// note | Примітка
Ця можливість підтримується починаючи з версії FastAPI `0.115.0`. 🤓
///
## Параметри заголовків з використанням Pydantic моделі { #header-parameters-with-a-pydantic-model }
Оголосіть потрібні **параметри заголовків** у **Pydantic моделі**, а потім оголосіть параметр як `Header`:
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *}
**FastAPI** буде **витягувати** дані для **кожного поля** з **заголовків** у запиті та передавати їх у створену вами Pydantic модель.
## Перевірка в документації { #check-the-docs }
Ви можете побачити необхідні заголовки в інтерфейсі документації за адресою `/docs`:
## Заборонити додаткові заголовки { #forbid-extra-headers }
У деяких особливих випадках (ймовірно, не дуже поширених) ви можете захотіти **обмежити** заголовки, які хочете отримати.
Ви можете використати конфігурацію моделі Pydantic, щоб `заборонити` будь-які `додаткові` поля:
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *}
Якщо клієнт спробує надіслати **додаткові заголовки**, він отримає **помилку** у відповіді.
Наприклад, якщо клієнт спробує надіслати заголовок `tool` зі значенням `plumbus`, він отримає **помилку** у відповіді з повідомленням про те, що параметр заголовка `tool` не дозволений:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["header", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus",
}
]
}
```
## Вимкнути перетворення підкреслень { #disable-convert-underscores }
Так само, як і зі звичайними параметрами заголовків, коли у назвах параметрів є символи підкреслення, вони **автоматично перетворюються на дефіси**.
Наприклад, якщо у коді у вас є параметр заголовка `save_data`, очікуваний HTTP-заголовок буде `save-data`, і він так само відображатиметься в документації.
Якщо з якоїсь причини вам потрібно вимкнути це автоматичне перетворення, ви також можете зробити це для Pydantic моделей для параметрів заголовків.
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *}
/// warning | Попередження
Перш ніж встановлювати `convert_underscores` у значення `False`, пам’ятайте, що деякі HTTP проксі та сервери забороняють використання заголовків із підкресленнями.
///
## Підсумок { #summary }
Ви можете використовувати **Pydantic моделі** для оголошення **заголовків** у **FastAPI**. 😎
================================================
FILE: docs/uk/docs/tutorial/header-params.md
================================================
# Параметри заголовків { #header-parameters }
Ви можете визначати параметри заголовків так само, як визначаєте параметри `Query`, `Path` і `Cookie`.
## Імпорт `Header` { #import-header }
Спочатку імпортуйте `Header`:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *}
## Оголошення параметрів `Header` { #declare-header-parameters }
Потім оголосіть параметри заголовків, використовуючи ту ж структуру, що й для `Path`, `Query` та `Cookie`.
Ви можете визначити значення за замовчуванням, а також усі додаткові параметри валідації або анотації:
{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *}
/// note | Технічні деталі
`Header` є «сестринським» класом для `Path`, `Query` і `Cookie`. Він також успадковується від того ж спільного класу `Param`.
Але пам’ятайте, що коли ви імпортуєте `Query`, `Path`, `Header` та інші з `fastapi`, то насправді це функції, які повертають спеціальні класи.
///
/// info | Інформація
Щоб оголосити заголовки, потрібно використовувати `Header`, інакше параметри будуть інтерпретуватися як параметри запиту.
///
## Автоматичне перетворення { #automatic-conversion }
`Header` має трохи додаткової функціональності порівняно з тим, що надають `Path`, `Query` та `Cookie`.
Більшість стандартних заголовків розділяються символом «дефіс», також відомим як «символ мінуса» (`-`).
Але змінна, така як `user-agent`, є недійсною в Python.
Тому, за замовчуванням, `Header` перетворюватиме символи підкреслення (`_`) у назвах параметрів на дефіси (`-`), щоб отримувати та документувати заголовки.
Також заголовки HTTP не чутливі до регістру, тож ви можете оголошувати їх у стандартному стилі Python (також відомому як «snake_case»).
Тому ви можете використовувати `user_agent`, як зазвичай у коді Python, замість того щоб потрібно було писати з великої літери перші літери, як `User_Agent` або щось подібне.
Якщо з якоїсь причини вам потрібно вимкнути автоматичне перетворення підкреслень на дефіси, встановіть параметр `convert_underscores` у `Header` в `False`:
{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *}
/// warning | Попередження
Перед тим як встановити `convert_underscores` в `False`, пам’ятайте, що деякі HTTP-представники та сервери забороняють використання заголовків із підкресленнями.
///
## Дубльовані заголовки { #duplicate-headers }
Можливо отримати дубльовані заголовки. Тобто один і той самий заголовок із кількома значеннями.
Ви можете визначити такі випадки, використовуючи список у типізації.
Ви отримаєте всі значення дубльованого заголовка у вигляді Python-`list`.
Наприклад, щоб оголосити заголовок `X-Token`, який може з’являтися більше ніж один раз, ви можете написати:
{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *}
Якщо ви взаємодієте з цією *операцією шляху*, надсилаючи два HTTP-заголовки, наприклад:
```
X-Token: foo
X-Token: bar
```
Відповідь буде така:
```JSON
{
"X-Token values": [
"bar",
"foo"
]
}
```
## Підсумок { #recap }
Оголошуйте заголовки за допомогою `Header`, використовуючи той самий загальний підхід, що й для `Query`, `Path` та `Cookie`.
І не хвилюйтеся про підкреслення у ваших змінних — **FastAPI** подбає про їх перетворення.
================================================
FILE: docs/uk/docs/tutorial/index.md
================================================
# Навчальний посібник - Посібник користувача { #tutorial-user-guide }
У цьому посібнику показано, як користуватися **FastAPI** з більшістю його функцій, крок за кроком.
Кожен розділ поступово надбудовується на попередні, але він структурований на окремі теми, щоб ви могли перейти безпосередньо до будь-якої конкретної, щоб вирішити ваші конкретні потреби API.
Також він створений як довідник для роботи у майбутньому, тож ви можете повернутися і побачити саме те, що вам потрібно.
## Запустіть код { #run-the-code }
Усі блоки коду можна скопіювати та використовувати безпосередньо (це фактично перевірені файли Python).
Щоб запустити будь-який із прикладів, скопіюйте код у файл `main.py`, і запустіть `fastapi dev`:
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
**ДУЖЕ радимо** написати або скопіювати код, відредагувати його та запустити локально.
Використання його у своєму редакторі – це те, що дійсно показує вам переваги FastAPI, бачите, як мало коду вам потрібно написати, всі перевірки типів, автозаповнення тощо.
---
## Встановлення FastAPI { #install-fastapi }
Першим кроком є встановлення FastAPI.
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім **встановіть FastAPI**:
/// note | Примітка
Коли ви встановлюєте через `pip install "fastapi[standard]"`, він постачається з деякими типовими необов’язковими стандартними залежностями, включно з `fastapi-cloud-cli`, який дозволяє розгортати в [FastAPI Cloud](https://fastapicloud.com).
Якщо ви не хочете мати ці необов’язкові залежності, натомість можете встановити `pip install fastapi`.
Якщо ви хочете встановити стандартні залежності, але без `fastapi-cloud-cli`, ви можете встановити через `pip install "fastapi[standard-no-fastapi-cloud-cli]"`.
///
/// tip | Порада
FastAPI має [офіційне розширення для VS Code](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (та Cursor), яке надає багато можливостей, включно з переглядачем операцій шляху, пошуком операцій шляху, навігацією CodeLens у тестах (перехід до визначення з тестів), а також розгортанням і журналами FastAPI Cloud — усе безпосередньо з вашого редактора.
///
## Просунутий посібник користувача { #advanced-user-guide }
Існує також **Просунутий посібник користувача**, який ви зможете прочитати пізніше після цього **Навчальний посібник - Посібник користувача**.
**Просунутий посібник користувача** засновано на цьому, використовує ті самі концепції та навчає вас деяким додатковим функціям.
Але вам слід спочатку прочитати **Навчальний посібник - Посібник користувача** (те, що ви зараз читаєте).
Він розроблений таким чином, що ви можете створити повну програму лише за допомогою **Навчальний посібник - Посібник користувача**, а потім розширити її різними способами, залежно від ваших потреб, використовуючи деякі з додаткових ідей з **Просунутого посібника користувача**.
================================================
FILE: docs/uk/docs/tutorial/metadata.md
================================================
# Метадані та URL-адреси документації { #metadata-and-docs-urls }
Ви можете налаштувати кілька конфігурацій метаданих у Вашому додатку **FastAPI**.
## Метадані для API { #metadata-for-api }
Ви можете встановити такі поля, які використовуються в специфікації OpenAPI та в автоматично згенерованих інтерфейсах документації API:
| Параметр | Тип | Опис |
|------------|------|-------------|
| `title` | `str` | Назва API. |
| `summary` | `str` | Короткий підсумок API. Доступно з OpenAPI 3.1.0, FastAPI 0.99.0. |
| `description` | `str` | Короткий опис API. Може використовувати Markdown. |
| `version` | `string` | Версія API. Це версія Вашого додатка, а не OpenAPI. Наприклад, `2.5.0`. |
| `terms_of_service` | `str` | URL до умов використання API. Якщо вказано, має бути у форматі URL. |
| `contact` | `dict` | Інформація для контакту з опублікованим API. Може містити кілька полів. contact поля
Параметр
Тип
Опис
name
str
Ідентифікаційне ім'я контактної особи або організації.
url
str
URL, що вказує на контактну інформацію. МАЄ бути у форматі URL.
email
str
Адреса електронної пошти контактної особи або організації. МАЄ бути у форматі адреси електронної пошти.
|
| `license_info` | `dict` | Інформація про ліцензію для опублікованого API. Може містити кілька полів. license_info поля
Параметр
Тип
Опис
name
str
ОБОВ'ЯЗКОВО (якщо встановлено license_info). Назва ліцензії для API.
identifier
str
Ліцензійний вираз за [SPDX](https://spdx.org/licenses/) для API. Поле identifier взаємовиключне з полем url. Доступно з OpenAPI 3.1.0, FastAPI 0.99.0.
url
str
URL до ліцензії, яка використовується для API. МАЄ бути у форматі URL.
|
Ви можете налаштувати їх наступним чином:
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
/// tip | Порада
У полі `description` можна використовувати Markdown, і він буде відображатися у результаті.
///
З цією конфігурацією автоматична документація API виглядатиме так:
## Ідентифікатор ліцензії { #license-identifier }
З початку використання OpenAPI 3.1.0 та FastAPI 0.99.0 Ви також можете налаштувати `license_info` за допомогою `identifier` замість `url`.
Наприклад:
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
## Метадані для тегів { #metadata-for-tags }
Ви також можете додати додаткові метадані для різних тегів, які використовуються для групування операцій шляху, за допомогою параметра `openapi_tags`.
Він приймає список, який містить один словник для кожного тега.
Кожен словник може містити:
* `name` (**обов'язково**): `str` з тією ж назвою тегу, яку Ви використовуєте у параметрі `tags` у Ваших *операціях шляху* та `APIRouter`s.
* `description`: `str` з коротким описом тегу. Може містити Markdown і буде показано в інтерфейсі документації.
* `externalDocs`: `dict`, який описує зовнішню документацію з такими полями:
* `description`: `str` з коротким описом зовнішньої документації.
* `url` (**обов'язково**): `str` з URL-адресою зовнішньої документації.
### Створення метаданих для тегів { #create-metadata-for-tags }
Спробуймо це на прикладі з тегами для `users` та `items`.
Створіть метадані для своїх тегів і передайте їх у параметр `openapi_tags`:
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
Зверніть увагу, що в описах можна використовувати Markdown, наприклад, «login» буде показано жирним шрифтом (**login**), а «fancy» буде показано курсивом (_fancy_).
/// tip | Порада
Вам не потрібно додавати метадані для всіх тегів, які Ви використовуєте.
///
### Використовуйте свої теги { #use-your-tags }
Використовуйте параметр `tags` зі своїми *операціями шляху* (і `APIRouter`s), щоб призначити їх до різних тегів:
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
/// info | Інформація
Детальніше про теги читайте в розділі [Конфігурація операції шляху](path-operation-configuration.md#tags).
///
### Перевірте документацію { #check-the-docs }
Тепер, якщо Ви перевірите документацію, вона покаже всі додаткові метадані:
### Порядок тегів { #order-of-tags }
Порядок кожного словника метаданих тегу також визначає порядок відображення в інтерфейсі документації.
Наприклад, хоча `users` мав би йти після `items` в алфавітному порядку, він відображається перед ними, оскільки ми додали їхні метадані як перший словник у списку.
## URL для OpenAPI { #openapi-url }
За замовчуванням схема OpenAPI надається за адресою `/openapi.json`.
Але Ви можете налаштувати це за допомогою параметра `openapi_url`.
Наприклад, щоб налаштувати його на `/api/v1/openapi.json`:
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
Якщо Ви хочете повністю вимкнути схему OpenAPI, Ви можете встановити `openapi_url=None`, це також вимкне інтерфейси документації, які її використовують.
## URL-адреси документації { #docs-urls }
Ви можете налаштувати два інтерфейси користувача для документації, які включені:
* **Swagger UI**: доступний за адресою `/docs`.
* Ви можете змінити його URL за допомогою параметра `docs_url`.
* Ви можете вимкнути його, встановивши `docs_url=None`.
* **ReDoc**: доступний за адресою `/redoc`.
* Ви можете змінити його URL за допомогою параметра `redoc_url`.
* Ви можете вимкнути його, встановивши `redoc_url=None`.
Наприклад, щоб налаштувати Swagger UI на `/documentation` і вимкнути ReDoc:
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
================================================
FILE: docs/uk/docs/tutorial/middleware.md
================================================
# Middleware { #middleware }
У **FastAPI** можна додавати middleware.
«Middleware» — це функція, яка працює з кожним **запитом** перед його обробкою будь-якою конкретною *операцією шляху*. А також з кожною **відповіддю** перед її поверненням.
* Вона отримує кожен **запит**, що надходить до вашого застосунку.
* Потім вона може виконати певні дії із цим **запитом** або запустити необхідний код.
* Далі вона передає **запит** для обробки рештою застосунку (якоюсь *операцією шляху*).
* Потім вона отримує **відповідь**, сформовану застосунком (якоюсь *операцією шляху*).
* Вона може виконати певні дії із цією **відповіддю** або запустити необхідний код.
* Потім вона повертає **відповідь**.
/// note | Технічні деталі
Якщо у вас є залежності з `yield`, код виходу виконається *після* middleware.
Якщо були заплановані фонові задачі (розглянуто в розділі [Фонові задачі](background-tasks.md), ви побачите це пізніше), вони виконаються *після* всіх middleware.
///
## Створення middleware { #create-a-middleware }
Щоб створити middleware, ви використовуєте декоратор `@app.middleware("http")` над функцією.
Функція middleware отримує:
* `request`.
* Функцію `call_next`, яка отримає `request` як параметр.
* Ця функція передасть `request` відповідній *операції шляху*.
* Потім вона поверне `response`, згенеровану відповідною *операцією шляху*.
* Потім ви можете додатково змінити `response` перед тим, як повернути її.
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
/// tip
Пам’ятайте, що власні пропрієтарні заголовки можна додавати [використовуючи префікс `X-`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
Але якщо у вас є власні заголовки, які ви хочете, щоб клієнт у браузері міг побачити, потрібно додати їх до ваших конфігурацій CORS ([CORS (Спільне використання ресурсів між джерелами)](cors.md)) за допомогою параметра `expose_headers`, описаного в [документації Starlette по CORS](https://www.starlette.dev/middleware/#corsmiddleware).
///
/// note | Технічні деталі
Ви також можете використати `from starlette.requests import Request`.
**FastAPI** надає це для вашої зручності як розробника. Але воно походить безпосередньо зі Starlette.
///
### До і після `response` { #before-and-after-the-response }
Ви можете додати код, який буде виконуватися з `request`, до того, як його отримає будь-яка *операція шляху*.
Також ви можете додати код, який буде виконуватися після того, як `response` буде згенеровано, перед тим як її повернути.
Наприклад, ви можете додати власний заголовок `X-Process-Time`, який міститиме час у секундах, який витратився на обробку запиту та генерацію відповіді:
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
/// tip
Тут ми використовуємо [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) замість `time.time()` оскільки він може бути більш точним для таких випадків. 🤓
///
## Порядок виконання кількох middleware { #multiple-middleware-execution-order }
Коли ви додаєте кілька middleware, використовуючи або декоратор `@app.middleware()` або метод `app.add_middleware()`, кожен новий middleware обгортає застосунок, утворюючи стек. Останній доданий middleware є *зовнішнім*, а перший — *внутрішнім*.
На шляху запиту першим виконується *зовнішній* middleware.
На шляху відповіді він виконується останнім.
Наприклад:
```Python
app.add_middleware(MiddlewareA)
app.add_middleware(MiddlewareB)
```
Це призводить до такого порядку виконання:
* **Запит**: MiddlewareB → MiddlewareA → route
* **Відповідь**: route → MiddlewareA → MiddlewareB
Така поведінка стеку гарантує, що middleware виконуються у передбачуваному та керованому порядку.
## Інші middlewares { #other-middlewares }
Ви можете пізніше прочитати більше про інші middlewares у [просунутому посібнику користувача: просунуте middleware](../advanced/middleware.md).
Ви дізнаєтесь, як обробляти CORS за допомогою middleware в наступному розділі.
================================================
FILE: docs/uk/docs/tutorial/path-operation-configuration.md
================================================
# Налаштування операції шляху { #path-operation-configuration }
Є кілька параметрів, які ви можете передати вашому «декоратору операції шляху» для налаштування.
/// warning | Попередження
Зверніть увагу, що ці параметри передаються безпосередньо «декоратору операції шляху», а не вашій «функції операції шляху».
///
## Код статусу відповіді { #response-status-code }
Ви можете визначити (HTTP) `status_code`, який буде використано у відповіді вашої «операції шляху».
Можна передати безпосередньо цілий код, наприклад `404`.
Якщо ви не пам'ятаєте призначення числових кодів, скористайтеся скороченими константами в `status`:
{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *}
Цей код статусу буде використано у відповіді та додано до схеми OpenAPI.
/// note | Технічні деталі
Ви також можете використати `from starlette import status`.
FastAPI надає той самий `starlette.status` як `fastapi.status` для вашої зручності як розробника. Але він походить безпосередньо зі Starlette.
///
## Мітки { #tags }
Ви можете додати мітки до вашої «операції шляху», передайте параметр `tags` зі `list` із `str` (зазвичай лише один `str`):
{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *}
Вони будуть додані до схеми OpenAPI та використані інтерфейсами автоматичної документації:
### Мітки з переліками { #tags-with-enums }
У великому застосунку ви можете накопичити багато міток і захочете переконатися, що завжди використовуєте ту саму мітку для пов'язаних «операцій шляху».
У таких випадках має сенс зберігати мітки в `Enum`.
FastAPI підтримує це так само, як і зі звичайними строками:
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
## Короткий опис і опис { #summary-and-description }
Ви можете додати `summary` і `description`:
{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *}
## Опис зі строки документації { #description-from-docstring }
Оскільки описи зазвичай довгі та займають кілька рядків, ви можете оголосити опис «операції шляху» у строці документації функції, і FastAPI прочитає його звідти.
Ви можете писати [Markdown](https://en.wikipedia.org/wiki/Markdown) у строці документації, його буде інтерпретовано та показано коректно (з урахуванням відступів у строці документації).
{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *}
Його буде використано в інтерактивній документації:
## Опис відповіді { #response-description }
Ви можете вказати опис відповіді параметром `response_description`:
{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *}
/// info | Інформація
Зверніть увагу, що `response_description` стосується саме відповіді, а `description` стосується «операції шляху» загалом.
///
/// check | Перевірте
OpenAPI визначає, що кожна «операція шляху» потребує опису відповіді.
Тому, якщо ви його не надасте, FastAPI автоматично згенерує «Successful response».
///
## Позначити операцію шляху як застарілу { #deprecate-a-path-operation }
Якщо потрібно позначити «операцію шляху» як застарілу, але не видаляючи її, передайте параметр `deprecated`:
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
У інтерактивній документації вона буде чітко позначена як застаріла:
Подивіться, як виглядають застарілі та незастарілі «операції шляху»:
## Підсумок { #recap }
Ви можете легко налаштовувати та додавати метадані до ваших «операцій шляху», передаючи параметри «декораторам операцій шляху».
================================================
FILE: docs/uk/docs/tutorial/path-params-numeric-validations.md
================================================
# Параметри шляху та валідація числових даних { #path-parameters-and-numeric-validations }
Так само як ви можете оголошувати додаткові перевірки та метадані для query параметрів за допомогою `Query`, ви можете оголошувати той самий тип перевірок і метаданих для параметрів шляху за допомогою `Path`.
## Імпорт `Path` { #import-path }
Спочатку імпортуйте `Path` з `fastapi` і імпортуйте `Annotated`:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *}
/// info | Інформація
FastAPI додав підтримку `Annotated` (і почав рекомендувати його використання) у версії 0.95.0.
Якщо у вас стара версія, при спробі використати `Annotated` можуть виникати помилки.
Переконайтеся, що ви [оновили версію FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) принаймні до версії 0.95.1 перед використанням `Annotated`.
///
## Оголошення метаданих { #declare-metadata }
Ви можете оголошувати всі ті ж параметри, що і для `Query`.
Наприклад, щоб оголосити значення метаданих `title` для параметра шляху `item_id`, ви можете написати:
{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *}
/// note | Примітка
Параметр шляху завжди є обов’язковим, оскільки він має бути частиною шляху. Навіть якщо ви оголосите його зі значенням `None` або встановите значення за замовчуванням — це ні на що не вплине, він все одно завжди буде обов’язковим.
///
## Упорядковуйте параметри, як вам потрібно { #order-the-parameters-as-you-need }
/// tip | Порада
Це, мабуть, не настільки важливо або необхідно, якщо ви використовуєте `Annotated`.
///
Припустимо, ви хочете оголосити параметр запиту `q` як обов’язковий `str`.
І вам не потрібно оголошувати нічого іншого для цього параметра, тому вам насправді не потрібно використовувати `Query`.
Але вам все одно потрібно використовувати `Path` для параметра шляху `item_id`. І з певних причин ви не хочете використовувати `Annotated`.
Python видасть помилку, якщо розмістити значення з "default" перед значенням, яке не має "default".
Але ви можете змінити порядок і розмістити значення без значення за замовчуванням (параметр запиту `q`) першим.
Для **FastAPI** порядок не має значення. Він визначає параметри за їхніми іменами, типами та оголошеннями за замовчуванням (`Query`, `Path` тощо) і не звертає уваги на порядок.
Тому ви можете оголосити вашу функцію так:
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
Але майте на увазі, що якщо ви використовуєте `Annotated`, цієї проблеми не буде, це не матиме значення, оскільки ви не використовуєте значення за замовчуванням параметрів функції для `Query()` або `Path()`.
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
## Упорядковуйте параметри, як вам потрібно: хитрощі { #order-the-parameters-as-you-need-tricks }
/// tip | Порада
Це, мабуть, не настільки важливо або необхідно, якщо ви використовуєте `Annotated`.
///
Ось **невелика хитрість**, яка може стати в пригоді, хоча вона рідко знадобиться.
Якщо ви хочете:
* оголосити параметр запиту `q` без `Query` і без жодного значення за замовчуванням
* оголосити параметр шляху `item_id`, використовуючи `Path`
* розмістити їх у різному порядку
* не використовувати `Annotated`
...у Python є спеціальний синтаксис для цього.
Передайте `*` як перший параметр функції.
Python нічого не зробить із цією `*`, але розпізнає, що всі наступні параметри слід викликати як аргументи за ключовим словом (пари ключ-значення), також відомі як kwargs. Навіть якщо вони не мають значення за замовчуванням.
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
### Краще з `Annotated` { #better-with-annotated }
Майте на увазі, що якщо ви використовуєте `Annotated`, оскільки ви не використовуєте значення за замовчуванням параметрів функції, цієї проблеми не буде, і, ймовірно, вам не потрібно буде використовувати `*`.
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
## Валідація числових даних: більше або дорівнює { #number-validations-greater-than-or-equal }
За допомогою `Query` і `Path` (та інших, які ви побачите пізніше) можна оголошувати числові обмеження.
Тут, завдяки `ge=1`, `item_id` має бути цілим числом, яке "`g`reater than or `e`qual" (більше або дорівнює) `1`.
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
## Валідація числових даних: більше ніж і менше або дорівнює { #number-validations-greater-than-and-less-than-or-equal }
Те саме застосовується до:
* `gt`: `g`reater `t`han
* `le`: `l`ess than or `e`qual
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
## Валідація числових даних: float, більше ніж і менше ніж { #number-validations-floats-greater-than-and-less-than }
Валідація чисел також працює для значень типу `float`.
Ось де стає важливо мати можливість оголошувати gt, а не тільки ge. Це дозволяє, наприклад, вимагати, щоб значення було більше `0`, навіть якщо воно менше `1`.
Таким чином, значення `0.5` буде допустимим. Але `0.0` або `0` — ні.
Те саме стосується lt.
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
## Підсумок { #recap }
За допомогою `Query`, `Path` (і інших параметрів, які ви ще не бачили) можна оголошувати метадані та перевірки рядків так само як у [Query параметри та валідація рядків](query-params-str-validations.md).
Також можна оголошувати числові перевірки:
* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual
/// info | Інформація
`Query`, `Path` та інші класи, які ви побачите пізніше, є підкласами спільного класу `Param`.
Всі вони мають однакові параметри для додаткових перевірок і метаданих, які ви вже бачили.
///
/// note | Технічні деталі
Коли ви імпортуєте `Query`, `Path` та інші з `fastapi`, насправді це функції.
При виклику вони повертають екземпляри класів з такими ж іменами.
Тобто ви імпортуєте `Query`, яка є функцією. А коли ви її викликаєте, вона повертає екземпляр класу, який теж називається `Query`.
Ці функції створені таким чином (замість використання класів напряму), щоб ваш редактор не відзначав їхні типи як помилки.
Таким чином, ви можете користуватися своїм звичайним редактором і інструментами для програмування без додаткових налаштувань для ігнорування таких помилок.
///
================================================
FILE: docs/uk/docs/tutorial/path-params.md
================================================
# Параметри шляху { #path-parameters }
Ви можете оголосити «параметри» або «змінні» шляху, використовуючи той самий синтаксис, що й у форматованих рядках Python:
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
Значення параметра шляху `item_id` буде передано у вашу функцію як аргумент `item_id`.
Отже, якщо ви запустите цей приклад і перейдете за посиланням [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), то побачите відповідь:
```JSON
{"item_id":"foo"}
```
## Параметри шляху з типами { #path-parameters-with-types }
Ви можете оголосити тип параметра шляху у функції, використовуючи стандартні анотації типів Python:
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
У цьому випадку `item_id` оголошено як `int`.
/// check | Перевірте
Це дасть вам підтримку редактора всередині функції з перевірками помилок, автодоповненням тощо.
///
## Перетворення даних { #data-conversion }
Якщо ви запустите цей приклад і відкриєте у браузері [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3), то побачите відповідь:
```JSON
{"item_id":3}
```
/// check | Перевірте
Зверніть увагу, що значення, яке отримала (і повернула) ваша функція, — це `3`, як Python `int`, а не рядок `"3"`.
Отже, з таким оголошенням типу **FastAPI** надає вам автоматичний запит «парсинг».
///
## Валідація даних { #data-validation }
Але якщо ви перейдете у браузері за посиланням [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo), ви побачите гарну HTTP-помилку:
```JSON
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo"
}
]
}
```
тому що параметр шляху `item_id` мав значення `"foo"`, яке не є `int`.
Та сама помилка з’явиться, якщо ви передасте `float` замість `int`, як у: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2)
/// check | Перевірте
Отже, з тим самим оголошенням типу в Python **FastAPI** надає вам валідацію даних.
Зверніть увагу, що помилка також чітко вказує саме місце, де валідація не пройшла.
Це неймовірно корисно під час розробки та налагодження коду, що взаємодіє з вашим API.
///
## Документація { #documentation }
А коли ви відкриєте у браузері [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs), ви побачите автоматичну, інтерактивну, API-документацію на кшталт:
/// check | Перевірте
Знову ж таки, лише з тим самим оголошенням типу в Python **FastAPI** надає вам автоматичну, інтерактивну документацію (з інтеграцією Swagger UI).
Зверніть увагу, що параметр шляху оголошено як ціле число.
///
## Переваги стандартів, альтернативна документація { #standards-based-benefits-alternative-documentation }
І оскільки згенерована схема відповідає стандарту [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md), існує багато сумісних інструментів.
Через це **FastAPI** також надає альтернативну API-документацію (використовуючи ReDoc), до якої ви можете отримати доступ за посиланням [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc):
Так само, існує багато сумісних інструментів. Зокрема інструменти генерації коду для багатьох мов.
## Pydantic { #pydantic }
Уся валідація даних виконується за лаштунками за допомогою [Pydantic](https://docs.pydantic.dev/), тож ви отримуєте всі переваги від його використання. І ви знаєте, що ви в надійних руках.
Ви можете використовувати ті самі оголошення типів з `str`, `float`, `bool` та багатьма іншими складними типами даних.
Декілька з них розглядаються в наступних розділах посібника.
## Порядок має значення { #order-matters }
Під час створення *операцій шляху* можуть виникати ситуації, коли у вас є фіксований шлях.
Наприклад, `/users/me` — припустімо, це для отримання даних про поточного користувача.
І тоді у вас також може бути шлях `/users/{user_id}` для отримання даних про конкретного користувача за його ID.
Оскільки *операції шляху* оцінюються по черзі, вам потрібно переконатися, що шлях для `/users/me` оголошено перед шляхом для `/users/{user_id}`:
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
Інакше шлях для `/users/{user_id}` також відповідатиме `/users/me`, «вважаючи», що отримує параметр `user_id` зі значенням `"me"`.
Так само ви не можете перевизначити операцію шляху:
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
Завжди використовуватиметься перша, оскільки шлях збігається першим.
## Попередньо визначені значення { #predefined-values }
Якщо у вас є *операція шляху*, яка отримує *параметр шляху*, але ви хочете, щоб можливі коректні значення *параметра шляху* були попередньо визначені, ви можете використати стандартний Python `Enum`.
### Створіть клас `Enum` { #create-an-enum-class }
Імпортуйте `Enum` і створіть підклас, що наслідується від `str` та `Enum`.
Завдяки наслідуванню від `str` документація API зможе визначити, що значення повинні бути типу `string`, і зможе коректно їх відобразити.
Після цього створіть атрибути класу з фіксованими значеннями, які будуть доступними коректними значеннями:
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
/// tip | Порада
Якщо вам цікаво, «AlexNet», «ResNet» та «LeNet» — це просто назви моделей машинного навчання моделі.
///
### Оголосіть *параметр шляху* { #declare-a-path-parameter }
Потім створіть *параметр шляху* з анотацією типу, використовуючи створений вами клас enum (`ModelName`):
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
### Перевірте документацію { #check-the-docs }
Оскільки доступні значення для *параметра шляху* визначені заздалегідь, інтерактивна документація може красиво їх показати:
### Робота з Python *переліченнями* { #working-with-python-enumerations }
Значення *параметра шляху* буде *елементом перелічування*.
#### Порівняйте *елементи перелічування* { #compare-enumeration-members }
Ви можете порівнювати його з *елементом перелічування* у створеному вами enum `ModelName`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
#### Отримайте *значення перелічування* { #get-the-enumeration-value }
Ви можете отримати фактичне значення (у цьому випадку це `str`), використовуючи `model_name.value`, або загалом `your_enum_member.value`:
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
/// tip | Порада
Ви також можете отримати доступ до значення `"lenet"` через `ModelName.lenet.value`.
///
#### Поверніть *елементи перелічування* { #return-enumeration-members }
Ви можете повертати *елементи enum* з вашої *операції шляху*, навіть вкладені у JSON-тіло (наприклад, `dict`).
Вони будуть перетворені на відповідні значення (у цьому випадку рядки) перед поверненням клієнту:
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
На стороні клієнта ви отримаєте відповідь у форматі JSON, наприклад:
```JSON
{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}
```
## Параметри шляху, що містять шляхи { #path-parameters-containing-paths }
Припустімо, у вас є *операція шляху* зі шляхом `/files/{file_path}`.
Але вам потрібно, щоб `file_path` сам містив *шлях*, наприклад `home/johndoe/myfile.txt`.
Отже, URL для цього файлу виглядатиме приблизно так: `/files/home/johndoe/myfile.txt`.
### Підтримка OpenAPI { #openapi-support }
OpenAPI не підтримує спосіб оголошення *параметра шляху*, який має містити всередині *шлях*, оскільки це може призвести до сценаріїв, які складно тестувати та визначати.
Проте ви все одно можете зробити це в **FastAPI**, використовуючи один із внутрішніх інструментів Starlette.
І документація все ще працюватиме, хоча й не додаватиме жодної документації, яка б казала, що параметр має містити шлях.
### Конвертер шляху { #path-convertor }
Використовуючи опцію безпосередньо зі Starlette, ви можете оголосити *параметр шляху*, що містить *шлях*, використовуючи URL на кшталт:
```
/files/{file_path:path}
```
У цьому випадку ім’я параметра — `file_path`, а остання частина `:path` вказує, що параметр має відповідати будь-якому *шляху*.
Отже, ви можете використати його так:
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
/// tip | Порада
Вам може знадобитися, щоб параметр містив `/home/johndoe/myfile.txt` із початковою косою рискою (`/`).
У такому випадку URL виглядатиме так: `/files//home/johndoe/myfile.txt`, із подвійною косою рискою (`//`) між `files` і `home`.
///
## Підсумок { #recap }
З **FastAPI**, використовуючи короткі, інтуїтивно зрозумілі та стандартні оголошення типів Python, ви отримуєте:
* Підтримку редактора: перевірка помилок, автодоповнення тощо.
* Перетворення даних «парсинг»
* Валідацію даних
* Анотацію API та автоматичну документацію
І вам потрібно оголосити їх лише один раз.
Це, ймовірно, основна видима перевага **FastAPI** порівняно з альтернативними фреймворками (окрім сирої продуктивності).
================================================
FILE: docs/uk/docs/tutorial/query-param-models.md
================================================
# Моделі параметрів запиту { #query-parameter-models }
Якщо у Вас є група **параметрів запиту**, які пов’язані між собою, Ви можете створити **Pydantic-модель** для їх оголошення.
Це дозволить Вам **повторно використовувати модель** у **різних місцях**, а також оголошувати перевірки та метадані для всіх параметрів одночасно. 😎
/// note | Примітка
Ця можливість підтримується, починаючи з версії FastAPI `0.115.0`. 🤓
///
## Параметри запиту з Pydantic-моделлю { #query-parameters-with-a-pydantic-model }
Оголосіть **параметри запиту**, які Вам потрібні, у **Pydantic-моделі**, а потім оголосіть цей параметр як `Query`:
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *}
**FastAPI** буде **витягувати** дані для **кожного поля** з **параметрів запиту** у запиті та передавати їх у визначену вами Pydantic-модель.
## Перевірте документацію { #check-the-docs }
Ви можете побачити параметри запиту в UI документації за `/docs`:
## Заборона зайвих параметрів запиту { #forbid-extra-query-parameters }
У деяких особливих випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** параметри запиту, які дозволено отримувати.
Ви можете використати конфігурацію моделі Pydantic, щоб заборонити (`forbid`) будь-які зайві (`extra`) поля:
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *}
Якщо клієнт спробує надіслати **зайві** дані у **параметрах запиту**, він отримає **відповідь з помилкою**.
Наприклад, якщо клієнт спробує надіслати параметр запиту `tool` зі значенням `plumbus`, як у цьому запиті:
```http
https://example.com/items/?limit=10&tool=plumbus
```
Він отримає відповідь з **помилкою**, яка повідомить, що параметр запиту `tool` не дозволено:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
```
## Підсумок { #summary }
Ви можете використовувати **Pydantic-моделі** для оголошення **параметрів запиту** у **FastAPI**. 😎
/// tip | Порада
Спойлер: Ви також можете використовувати Pydantic-моделі для оголошення кукі та заголовків, але про це Ви дізнаєтеся пізніше в цьому посібнику. 🤫
///
================================================
FILE: docs/uk/docs/tutorial/query-params-str-validations.md
================================================
# Query параметри та валідація рядків { #query-parameters-and-string-validations }
**FastAPI** дозволяє оголошувати додаткову інформацію та виконувати валідацію для ваших параметрів.
Розглянемо цей додаток як приклад:
{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *}
Query параметр `q` має тип `str | None`, що означає, що він має тип `str`, але також може бути `None`, і справді, значення за замовчуванням — `None`, тож FastAPI знатиме, що він не є обов'язковим.
/// note | Примітка
FastAPI знатиме, що значення `q` не є обов’язковим, завдяки значенню за замовчуванням `= None`.
Використання `str | None` дозволить вашому редактору коду надавати кращу підтримку та виявляти помилки.
///
## Додаткова валідація { #additional-validation }
Ми хочемо, щоб навіть якщо `q` є необов’язковим, коли його передають, його довжина не перевищувала 50 символів.
### Імпорт `Query` та `Annotated` { #import-query-and-annotated }
Щоб це зробити, спочатку імпортуємо:
* `Query` з `fastapi`
* `Annotated` з `typing`
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *}
/// info | Інформація
FastAPI додав підтримку `Annotated` (і почав рекомендувати його) у версії 0.95.0.
Якщо у вас старіша версія, під час використання `Annotated` можуть виникати помилки.
Переконайтеся, що ви [оновили версію FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) до принаймні 0.95.1, перш ніж використовувати `Annotated`.
///
## Використання `Annotated` у типі параметра `q` { #use-annotated-in-the-type-for-the-q-parameter }
Пам’ятаєте, як я раніше розповідав, що `Annotated` можна використовувати для додавання метаданих до параметрів у [Вступі до типів Python](../python-types.md#type-hints-with-metadata-annotations)?
Зараз саме час використати його разом із FastAPI. 🚀
Раніше ми мали таку анотацію типу:
```Python
q: str | None = None
```
Тепер ми загорнемо її у `Annotated`, і отримаємо:
```Python
q: Annotated[str | None] = None
```
Обидві ці версії означають одне й те саме: `q` — це параметр, який може бути `str` або `None`, і за замовчуванням має значення `None`.
А тепер переходимо до цікавого! 🎉
## Додавання `Query` до `Annotated` у параметр `q` { #add-query-to-annotated-in-the-q-parameter }
Тепер, коли у нас є `Annotated`, де ми можемо додавати додаткову інформацію (у цьому випадку — додаткову валідацію), додамо `Query` всередину `Annotated` і встановимо параметр `max_length` у `50`:
{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *}
Зверніть увагу, що значення за замовчуванням усе ще `None`, тому параметр залишається необов'язковим.
Але тепер, додавши `Query(max_length=50)` всередину `Annotated`, ми повідомляємо FastAPI, що хочемо додаткову валідацію для цього значення: ми хочемо, щоб воно мало максимум 50 символів. 😎
/// tip | Порада
Тут ми використовуємо `Query()`, оскільки це query параметр. Далі ми розглянемо інші варіанти, як-от `Path()`, `Body()`, `Header()` та `Cookie()`, які приймають ті самі аргументи, що й `Query()`.
///
Тепер FastAPI:
* Перевірить дані, щоб переконатися, що їхня максимальна довжина — 50 символів
* Покажe чітку помилку клієнту, якщо дані недійсні
* Задокументує параметр в OpenAPI-схемі операції шляху (що відобразиться в автоматично згенерованій документації)
## Альтернативний (застарілий) метод: `Query` як значення за замовчуванням { #alternative-old-query-as-the-default-value }
У попередніх версіях FastAPI (до 0.95.0) потрібно було використовувати `Query` як значення за замовчуванням параметра, замість того, щоб додавати його в `Annotated`. Є висока ймовірність, що ви зустрінете код із таким підходом, тож я поясню його.
/// tip | Порада
Для нового коду та коли це можливо, використовуйте `Annotated`, як показано вище. Це має багато переваг (пояснених нижче) і не має недоліків. 🍰
///
Раніше ми писали `Query()` як значення за замовчуванням для параметра функції, встановлюючи `max_length` у 50:
{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *}
Оскільки в цьому випадку (без `Annotated`) нам потрібно замінити значення за замовчуванням `None` у функції на `Query()`, тепер ми повинні встановити значення за замовчуванням через параметр `Query(default=None)`. Це виконує ту саму роль визначення значення за замовчуванням (принаймні для FastAPI).
Таким чином:
```Python
q: str | None = Query(default=None)
```
...робить параметр необов’язковим зі значенням за замовчуванням `None`, що еквівалентно:
```Python
q: str | None = None
```
Але у версії з `Query` ми явно вказуємо, що це query параметр.
Далі ми можемо передавати `Query` додаткові параметри. У цьому випадку — параметр `max_length`, який застосовується до рядків:
```Python
q: str | None = Query(default=None, max_length=50)
```
Це забезпечить валідацію даних, виведе зрозумілу помилку у разі недійсних даних і задокументує параметр у схемі OpenAPI операції шляху.
### `Query` як значення за замовчуванням або всередині `Annotated` { #query-as-the-default-value-or-in-annotated }
Важливо пам’ятати, якщо використовувати `Query` всередині `Annotated`, не можна задавати параметр `default` у `Query`.
Замість цього використовуйте фактичне значення за замовчуванням параметра функції. Інакше це буде непослідовно.
Наприклад, цей варіант є некоректним:
```Python
q: Annotated[str, Query(default="rick")] = "morty"
```
...тому, що не зрозуміло, яке значення має бути значенням за замовчуванням: `"rick"` чи `"morty"`.
Тож ви будете використовувати (бажано):
```Python
q: Annotated[str, Query()] = "rick"
```
...або у старих кодових базах ви знайдете:
```Python
q: str = Query(default="rick")
```
### Переваги використання `Annotated` { #advantages-of-annotated }
Використання `Annotated` є рекомендованим замість задання значення за замовчуванням у параметрах функції, оскільки воно краще з кількох причин. 🤓
Значення за замовчуванням параметра функції є фактичним значенням за замовчуванням, що є більш інтуїтивним у Python загалом. 😌
Ви можете викликати ту саму функцію в інших місцях без FastAPI, і вона працюватиме очікувано. Якщо параметр є обов’язковим (без значення за замовчуванням), ваш редактор повідомить про помилку, а Python також видасть помилку, якщо ви виконаєте функцію без передавання цього параметра.
Якщо ви не використовуєте `Annotated`, а використовуєте (старий) стиль значень за замовчуванням, то при виклику цієї функції без FastAPI в інших місцях потрібно пам’ятати передати їй аргументи, щоб вона працювала коректно, інакше значення будуть відрізнятися від очікуваних (наприклад, ви отримаєте `QueryInfo` або щось подібне замість `str`). І ваш редактор не повідомить про помилку, і Python не скаржитиметься під час запуску цієї функції — лише коли операції всередині завершаться помилкою.
Оскільки `Annotated` може містити кілька анотацій метаданих, тепер ви навіть можете використовувати ту саму функцію з іншими інструментами, такими як [Typer](https://typer.tiangolo.com/). 🚀
## Додавання додаткових валідацій { #add-more-validations }
Ви також можете додати параметр `min_length`:
{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *}
## Додавання регулярних виразів { #add-regular-expressions }
Ви можете визначити регулярний вираз `pattern`, якому має відповідати параметр:
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
Цей конкретний шаблон регулярного виразу перевіряє, що отримане значення параметра:
* `^`: починається з наступних символів, перед якими немає інших символів.
* `fixedquery`: точно відповідає значенню `fixedquery`.
* `$`: закінчується тут, після `fixedquery` немає жодних символів.
Якщо ви почуваєтеся розгублено щодо **«regular expression»**, не хвилюйтеся. Це складна тема для багатьох людей. Ви все одно можете робити багато речей без використання регулярних виразів.
Тепер ви знаєте, що коли вони знадобляться, їх можна застосовувати у **FastAPI**.
## Значення за замовчуванням { #default-values }
Ви можете, звісно, використовувати значення за замовчуванням, відмінні від `None`.
Припустімо, що ви хочете оголосити query параметр `q` з `min_length` `3` і значенням за замовчуванням `"fixedquery"`:
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
/// note | Примітка
Наявність значення за замовчуванням будь-якого типу, включаючи `None`, робить параметр необов’язковим (not required).
///
## Обов’язкові параметри { #required-parameters }
Якщо нам не потрібно оголошувати додаткові валідації або метадані, ми можемо зробити query параметр `q` обов’язковим, просто не вказуючи значення за замовчуванням, наприклад:
```Python
q: str
```
замість:
```Python
q: str | None = None
```
Але тепер ми оголошуємо його з `Query`, наприклад так:
```Python
q: Annotated[str | None, Query(min_length=3)] = None
```
Тому, якщо вам потрібно оголосити значення як обов’язкове під час використання `Query`, просто не вказуйте значення за замовчуванням:
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
### Обов’язковий, може бути `None` { #required-can-be-none }
Ви можете вказати, що параметр може приймати `None`, але при цьому залишається обов’язковим. Це змусить клієнтів надіслати значення, навіть якщо значення дорівнює `None`.
Щоб зробити це, оголосіть, що `None` є допустимим типом, але просто не вкажіть значення за замовчуванням:
{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *}
## Список query параметрів / кілька значень { #query-parameter-list-multiple-values }
Коли ви явно визначаєте query параметр за допомогою `Query`, ви також можете оголосити, що він має приймати список значень, або, іншими словами, кілька значень.
Наприклад, щоб оголосити query параметр `q`, який може з’являтися в URL кілька разів, можна написати:
{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *}
Тоді, у випадку URL:
```
http://localhost:8000/items/?q=foo&q=bar
```
ви отримаєте кілька значень `q` query параметрів (`foo` і `bar`) у вигляді Python `list` у вашій функції операції шляху, у параметрі функції `q`.
Отже, відповідь на цей URL буде:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
/// tip | Порада
Щоб оголосити query параметр з типом `list`, як у наведеному вище прикладі, потрібно явно використовувати `Query`, інакше він буде інтерпретований як тіло запиту.
///
Інтерактивна API-документація оновиться відповідно, дозволяючи передавати кілька значень:
### Список query параметрів / кілька значень за замовчуванням { #query-parameter-list-multiple-values-with-defaults }
Ви також можете визначити значення за замовчуванням `list`, якщо жодне значення не було передане:
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
Якщо ви перейдете за посиланням:
```
http://localhost:8000/items/
```
то значення `q` за замовчуванням буде: `["foo", "bar"]`, і ваша відповідь виглядатиме так:
```JSON
{
"q": [
"foo",
"bar"
]
}
```
#### Використання тільки `list` { #using-just-list }
Ви також можете використовувати `list` напряму замість `list[str]`:
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
/// note | Примітка
Майте на увазі, що в цьому випадку FastAPI не перевірятиме вміст списку.
Наприклад, `list[int]` перевірятиме (і документуватиме), що вміст списку — цілі числа. Але `list` без уточнення цього не робитиме.
///
## Оголосити більше метаданих { #declare-more-metadata }
Ви можете додати більше інформації про параметр.
Ця інформація буде включена у згенерований OpenAPI та використана інтерфейсами документації та зовнішніми інструментами.
/// note | Примітка
Майте на увазі, що різні інструменти можуть мати різний рівень підтримки OpenAPI.
Деякі з них можуть ще не відображати всю додаткову інформацію, хоча в більшості випадків відсутню функцію вже заплановано до реалізації.
///
Ви можете додати `title`:
{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *}
А також `description`:
{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *}
## Аліаси параметрів { #alias-parameters }
Уявіть, що ви хочете, щоб параметр називався `item-query`.
Наприклад:
```
http://127.0.0.1:8000/items/?item-query=foobaritems
```
Але `item-query` — це некоректна назва змінної в Python.
Найближчий допустимий варіант — `item_query`.
Проте вам потрібно, щоб параметр залишався саме `item-query`...
У такому випадку можна оголосити `alias`, і саме він буде використовуватися для отримання значення параметра:
{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *}
## Позначення параметрів як застарілих { #deprecating-parameters }
Припустімо, що вам більше не подобається цей параметр.
Вам потрібно залишити його на деякий час, оскільки ним користуються клієнти, але ви хочете, щоб документація чітко показувала, що він є застарілий.
Тоді передайте параметр `deprecated=True` до `Query`:
{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *}
Документація буде показувати це таким чином:
## Виняток параметрів з OpenAPI { #exclude-parameters-from-openapi }
Щоб виключити query параметр зі згенерованої схеми OpenAPI (і, таким чином, з автоматичних систем документації), встановіть параметр `include_in_schema` для `Query` в `False`:
{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *}
## Кастомна валідація { #custom-validation }
Можуть бути випадки, коли вам потрібно провести кастомну валідацію, яку не можна реалізувати за допомогою параметрів, показаних вище.
У таких випадках ви можете використати кастомну функцію-валідатор, яка буде застосована після звичайної валідації (наприклад, після перевірки, що значення є типом `str`).
Це можна досягти за допомогою [Pydantic's `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) в середині `Annotated`.
/// tip | Порада
Pydantic також має [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) та інші. 🤓
///
Наприклад, цей кастомний валідатор перевіряє, чи починається ID елемента з `isbn-` для номера книги ISBN або з `imdb-` для ID URL фільму на IMDB:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
/// info | Інформація
Це доступно з версії Pydantic 2 або вище. 😎
///
/// tip | Порада
Якщо вам потрібно виконати будь-яку валідацію, яка вимагає взаємодії з будь-яким зовнішнім компонентом, таким як база даних чи інший API, замість цього слід використовувати FastAPI Dependencies — ви дізнаєтесь про них пізніше.
Ці кастомні валідатори використовуються для речей, які можна перевірити лише з тими самими даними, що надані в запиті.
///
### Зрозумійте цей код { #understand-that-code }
Головний момент — це використання `AfterValidator` з функцією всередині `Annotated`. Можете пропустити цю частину, якщо хочете. 🤸
---
Але якщо вам цікаво розібратися в цьому конкретному прикладі коду і вам ще не набридло, ось кілька додаткових деталей.
#### Рядок із `value.startswith()` { #string-with-value-startswith }
Звернули увагу? Рядок із `value.startswith()` може приймати кортеж, і тоді він перевірятиме кожне значення в кортежі:
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *}
#### Випадковий елемент { #a-random-item }
За допомогою `data.items()` ми отримуємо ітерабельний об'єкт із кортежами, що містять ключ і значення для кожного елемента словника.
Ми перетворюємо цей ітерабельний об'єкт у звичайний `list` за допомогою `list(data.items())`.
Потім, використовуючи `random.choice()`, ми можемо отримати випадкове значення зі списку, тобто отримуємо кортеж із `(id, name)`. Це може бути щось на зразок `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
Далі ми присвоюємо ці два значення кортежу змінним `id` і `name`.
Тож, якщо користувач не вказав ID елемента, він все одно отримає випадкову рекомендацію.
...ми робимо все це в одному простому рядку. 🤯 Хіба ви не любите Python? 🐍
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *}
## Підсумок { #recap }
Ви можете оголошувати додаткові валідації та метадані для ваших параметрів.
Загальні валідації та метадані:
* `alias`
* `title`
* `description`
* `deprecated`
Валідації, специфічні для рядків:
* `min_length`
* `max_length`
* `pattern`
Кастомні валідації за допомогою `AfterValidator`.
У цих прикладах ви побачили, як оголошувати валідації для значень `str`.
Дивіться наступні розділи, щоб дізнатися, як оголошувати валідації для інших типів, наприклад чисел.
================================================
FILE: docs/uk/docs/tutorial/query-params.md
================================================
# Query параметри { #query-parameters }
Коли ви оголошуєте інші параметри функції, які не є частиною параметрів шляху, вони автоматично інтерпретуються як параметри «query».
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
Query — це набір пар ключ-значення, що йдуть після символу `?` в URL, розділені символами `&`.
Наприклад, в URL:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
...параметрами query є:
* `skip`: зі значенням `0`
* `limit`: зі значенням `10`
Оскільки вони є частиною URL, вони «природно» є рядками.
Але коли ви оголошуєте їх із типами Python (у наведеному прикладі як `int`), вони перетворюються на цей тип і проходять перевірку відповідності.
Увесь той самий процес, який застосовується до параметрів шляху, також застосовується до параметрів query:
* Підтримка в редакторі (очевидно)
* «парсинг» даних
* Валідація даних
* Автоматична документація
## Значення за замовчуванням { #defaults }
Оскільки параметри query не є фіксованою частиною шляху, вони можуть бути необов’язковими та мати значення за замовчуванням.
У наведеному вище прикладі вони мають значення за замовчуванням: `skip=0` і `limit=10`.
Отже, перехід за URL:
```
http://127.0.0.1:8000/items/
```
буде таким самим, як і перехід за посиланням:
```
http://127.0.0.1:8000/items/?skip=0&limit=10
```
Але якщо ви перейдете, наприклад, за посиланням:
```
http://127.0.0.1:8000/items/?skip=20
```
Значення параметрів у вашій функції будуть такими:
* `skip=20`: оскільки ви вказали його в URL
* `limit=10`: оскільки це значення за замовчуванням
## Необов'язкові параметри { #optional-parameters }
Так само ви можете оголосити необов’язкові параметри query, встановивши для них значення за замовчуванням `None`:
{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *}
У цьому випадку параметр функції `q` буде необов’язковим і за замовчуванням матиме значення `None`.
/// check | Перевірте
Також зверніть увагу, що **FastAPI** достатньо розумний, щоб визначити, що параметр шляху `item_id` є параметром шляху, а `q` — ні, отже, це параметр query.
///
## Перетворення типу параметра query { #query-parameter-type-conversion }
Ви також можете оголошувати параметри типу `bool`, і вони будуть автоматично конвертовані:
{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *}
У цьому випадку, якщо ви перейдете за:
```
http://127.0.0.1:8000/items/foo?short=1
```
або
```
http://127.0.0.1:8000/items/foo?short=True
```
або
```
http://127.0.0.1:8000/items/foo?short=true
```
або
```
http://127.0.0.1:8000/items/foo?short=on
```
або
```
http://127.0.0.1:8000/items/foo?short=yes
```
або будь-який інший варіант написання (великі літери, перша літера велика тощо), ваша функція побачить параметр `short` зі значенням `True` типу `bool`. В іншому випадку — `False`.
## Кілька path і query параметрів { #multiple-path-and-query-parameters }
Ви можете одночасно оголошувати кілька параметрів шляху та параметрів query, **FastAPI** знає, який з них який.
І вам не потрібно оголошувати їх у якомусь конкретному порядку.
Вони визначаються за назвою:
{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *}
## Обов’язкові параметри query { #required-query-parameters }
Коли ви оголошуєте значення за замовчуванням для не-path-параметрів (поки що ми бачили лише параметри query), тоді вони не є обов’язковими.
Якщо ви не хочете задавати конкретне значення, а просто зробити параметр необов’язковим, задайте `None` як значення за замовчуванням.
Але якщо ви хочете зробити параметр query обов’язковим, просто не вказуйте для нього значення за замовчуванням:
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
Тут параметр query `needy` — обов’язковий параметр query типу `str`.
Якщо ви відкриєте у браузері URL-адресу:
```
http://127.0.0.1:8000/items/foo-item
```
...без додавання обов’язкового параметра `needy`, ви побачите помилку на кшталт:
```JSON
{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null
}
]
}
```
Оскільки `needy` є обов’язковим параметром, вам потрібно вказати його в URL:
```
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
```
...це спрацює:
```JSON
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
```
І звісно, ви можете визначити деякі параметри як обов’язкові, деякі — зі значенням за замовчуванням, а деякі — повністю необов’язкові:
{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *}
У цьому випадку є 3 параметри query:
* `needy`, обов’язковий `str`.
* `skip`, `int` зі значенням за замовчуванням `0`.
* `limit`, необов’язковий `int`.
/// tip | Порада
Ви також можете використовувати `Enum` так само, як і з [Параметри шляху](path-params.md#predefined-values).
///
================================================
FILE: docs/uk/docs/tutorial/request-files.md
================================================
# Запит файлів { #request-files }
Ви можете визначити файли, які будуть завантажуватися клієнтом, використовуючи `File`.
/// info | Інформація
Щоб отримувати завантажені файли, спочатку встановіть [`python-multipart`](https://github.com/Kludex/python-multipart).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім встановили його, наприклад:
```console
$ pip install python-multipart
```
Це необхідно, оскільки завантажені файли передаються у вигляді «form data».
///
## Імпорт `File` { #import-file }
Імпортуйте `File` та `UploadFile` з `fastapi`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
## Визначення параметрів `File` { #define-file-parameters }
Створіть параметри файлів так само як ви б створювали `Body` або `Form`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
/// info | Інформація
`File` — це клас, який безпосередньо успадковує `Form`.
Але пам’ятайте, що коли ви імпортуєте `Query`, `Path`, `File` та інші з `fastapi`, це насправді функції, які повертають спеціальні класи.
///
/// tip | Порада
Щоб оголосити тіла файлів, вам потрібно використовувати `File`, тому що інакше параметри будуть інтерпретовані як параметри запиту або параметри тіла (JSON).
///
Файли будуть завантажені у вигляді «form data».
Якщо ви оголосите тип параметра *функції операції шляху* як `bytes`, **FastAPI** прочитає файл за вас, і ви отримаєте його вміст у вигляді `bytes`.
Майте на увазі, що це означає, що весь вміст буде збережено в пам'яті. Це працюватиме добре для малих файлів.
Але є кілька випадків, у яких вам може бути корисно використовувати `UploadFile`.
## Параметри файлу з `UploadFile` { #file-parameters-with-uploadfile }
Визначте параметр файлу з типом `UploadFile`:
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
Використання `UploadFile` має кілька переваг перед `bytes`:
* Вам не потрібно використовувати `File()` у значенні за замовчуванням параметра.
* Використовується «spooled» файл:
* Файл зберігається в пам'яті до досягнення максимального обмеження розміру, після чого він буде збережений на диску.
* Це означає, що він добре працюватиме для великих файлів, таких як зображення, відео, великі двійкові файли тощо, не споживаючи всю пам'ять.
* Ви можете отримати метадані про завантажений файл.
* Він має [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` інтерфейс.
* Він надає фактичний об'єкт Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile), який можна передавати безпосередньо іншим бібліотекам, що очікують file-like об'єкт.
### `UploadFile` { #uploadfile }
`UploadFile` має такі атрибути:
* `filename`: Рядок `str` з оригінальною назвою файлу, який був завантажений (наприклад, `myimage.jpg`).
* `content_type`: Рядок `str` з типом вмісту (MIME type / media type) (наприклад, `image/jpeg`).
* `file`: [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) ([file-like](https://docs.python.org/3/glossary.html#term-file-like-object) об'єкт). Це фактичний файловий об'єкт Python, який ви можете передавати безпосередньо іншим функціям або бібліотекам, що очікують «file-like» об'єкт.
`UploadFile` має такі асинхронні `async` методи. Вони всі викликають відповідні методи файлу під капотом (використовуючи внутрішній `SpooledTemporaryFile`).
* `write(data)`: Записує `data` (`str` або `bytes`) у файл.
* `read(size)`: Читає `size` (`int`) байтів/символів з файлу.
* `seek(offset)`: Переходить до байтової позиції `offset` (`int`) у файлі.
* Наприклад, `await myfile.seek(0)` перейде на початок файлу.
* Це особливо корисно, якщо ви виконаєте `await myfile.read()` один раз, а потім потрібно знову прочитати вміст.
* `close()`: Закриває файл.
Оскільки всі ці методи є асинхронними `async` методами, вам потрібно їх «await»-ити.
Наприклад, всередині `async` *функції операції шляху* ви можете отримати вміст за допомогою:
```Python
contents = await myfile.read()
```
Якщо ви знаходитесь у звичайній `def` *функції операції шляху*, ви можете отримати доступ до `UploadFile.file` безпосередньо, наприклад:
```Python
contents = myfile.file.read()
```
/// note | Технічні деталі `async`
Коли ви використовуєте `async` методи, **FastAPI** виконує файлові методи у пулі потоків і очікує на них.
///
/// note | Технічні деталі Starlette
`UploadFile` у **FastAPI** успадковується безпосередньо від `UploadFile` у **Starlette**, але додає деякі необхідні частини, щоб зробити його сумісним із **Pydantic** та іншими частинами FastAPI.
///
## Що таке «Form Data» { #what-is-form-data }
Спосіб, у який HTML-форми (``) надсилають дані на сервер, зазвичай використовує «спеціальне» кодування для цих даних, відмінне від JSON.
**FastAPI** забезпечить зчитування цих даних з правильного місця, а не з JSON.
/// note | Технічні деталі
Дані з форм зазвичай кодуються за допомогою «media type» `application/x-www-form-urlencoded`, якщо вони не містять файлів.
Але якщо форма містить файли, вона кодується як `multipart/form-data`. Якщо ви використовуєте `File`, **FastAPI** знатиме, що потрібно отримати файли з правильної частини тіла.
Якщо ви хочете дізнатися більше про ці типи кодування та формові поля, ознайомтеся з [MDN web docs для `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Попередження
Ви можете оголосити кілька параметрів `File` і `Form` в *операції шляху*, але ви не можете одночасно оголошувати поля `Body`, які ви очікуєте отримати як JSON, оскільки запит матиме тіло, закодоване як `multipart/form-data`, а не `application/json`.
Це не обмеження **FastAPI**, а частина протоколу HTTP.
///
## Необов’язкове завантаження файлу { #optional-file-upload }
Ви можете зробити файл необов’язковим, використовуючи стандартні анотації типів і встановивши значення за замовчуванням `None`:
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
## `UploadFile` із додатковими метаданими { #uploadfile-with-additional-metadata }
Ви також можете використовувати `File()` разом із `UploadFile`, наприклад, щоб встановити додаткові метадані:
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
## Завантаження кількох файлів { #multiple-file-uploads }
Можна завантажувати кілька файлів одночасно.
Вони будуть пов’язані з одним і тим самим «form field», який передається у вигляді «form data».
Щоб це реалізувати, потрібно оголосити список `bytes` або `UploadFile`:
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
Ви отримаєте, як і було оголошено, `list` із `bytes` або `UploadFile`.
/// note | Технічні деталі
Ви також можете використати `from starlette.responses import HTMLResponse`.
**FastAPI** надає ті ж самі `starlette.responses`, що й `fastapi.responses`, просто для зручності для вас, розробника. Але більшість доступних відповідей надходять безпосередньо від Starlette.
///
### Завантаження кількох файлів із додатковими метаданими { #multiple-file-uploads-with-additional-metadata }
Так само як і раніше, ви можете використовувати `File()`, щоб встановити додаткові параметри навіть для `UploadFile`:
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
## Підсумок { #recap }
Використовуйте `File`, `bytes` та `UploadFile`, щоб оголошувати файли для завантаження в запиті, надіслані у вигляді form data.
================================================
FILE: docs/uk/docs/tutorial/request-form-models.md
================================================
# Моделі форм { #form-models }
У FastAPI ви можете використовувати **Pydantic-моделі** для оголошення **полів форми**.
/// info
Щоб використовувати форми, спочатку встановіть [`python-multipart`](https://github.com/Kludex/python-multipart).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім встановили його, наприклад:
```console
$ pip install python-multipart
```
///
/// note
Це підтримується, починаючи з FastAPI версії `0.113.0`. 🤓
///
## Pydantic-моделі для форм { #pydantic-models-for-forms }
Вам просто потрібно оголосити **Pydantic-модель** з полями, які ви хочете отримати як **поля форми**, а потім оголосити параметр як `Form`:
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
**FastAPI** **витягне** дані для **кожного поля** з **формових даних** у запиті та надасть вам Pydantic-модель, яку ви визначили.
## Перевірте документацію { #check-the-docs }
Ви можете перевірити це в UI документації за `/docs`:
## Забороніть додаткові поля форми { #forbid-extra-form-fields }
У деяких особливих випадках (ймовірно, не дуже поширених) ви можете **обмежити** поля форми лише тими, які були оголошені в Pydantic-моделі. І **заборонити** будь-які **додаткові** поля.
/// note
Це підтримується, починаючи з FastAPI версії `0.114.0`. 🤓
///
Ви можете використати конфігурацію Pydantic-моделі, щоб заборонити `forbid` будь-які додаткові `extra` поля:
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
Якщо клієнт спробує надіслати додаткові дані, він отримає **відповідь з помилкою**.
Наприклад, якщо клієнт спробує надіслати поля форми:
* `username`: `Rick`
* `password`: `Portal Gun`
* `extra`: `Mr. Poopybutthole`
Він отримає відповідь із помилкою, яка повідомляє, що поле `extra` не дозволено:
```json
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["body", "extra"],
"msg": "Extra inputs are not permitted",
"input": "Mr. Poopybutthole"
}
]
}
```
## Підсумок { #summary }
У FastAPI ви можете використовувати Pydantic-моделі для оголошення полів форми. 😎
================================================
FILE: docs/uk/docs/tutorial/request-forms-and-files.md
================================================
# Запити з формами та файлами { #request-forms-and-files }
Ви можете одночасно визначати файли та поля форми, використовуючи `File` і `Form`.
/// info | Інформація
Щоб отримувати завантажені файли та/або дані форми, спочатку встановіть [`python-multipart`](https://github.com/Kludex/python-multipart).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім встановили бібліотеку, наприклад:
```console
$ pip install python-multipart
```
///
## Імпорт `File` та `Form` { #import-file-and-form }
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
## Оголошення параметрів `File` та `Form` { #define-file-and-form-parameters }
Створіть параметри файлів та форми так само як і для `Body` або `Query`:
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
Файли та поля форми будуть завантажені як формові дані, і ви отримаєте файли та поля форми.
Ви також можете оголосити деякі файли як `bytes`, а деякі як `UploadFile`.
/// warning | Попередження
Ви можете оголосити кілька параметрів `File` і `Form` в операції *шляху*, але не можете одночасно оголошувати `Body`-поля, які очікуєте отримати у форматі JSON, оскільки запит матиме тіло, закодоване за допомогою `multipart/form-data`, а не `application/json`.
Це не обмеження **FastAPI**, а частина протоколу HTTP.
///
## Підсумок { #recap }
Використовуйте `File` та `Form` разом, коли вам потрібно отримувати дані та файли в одному запиті.
================================================
FILE: docs/uk/docs/tutorial/request-forms.md
================================================
# Дані форми { #form-data }
Коли вам потрібно отримувати поля форми замість JSON, ви можете використовувати `Form`.
/// info | Інформація
Щоб використовувати форми, спочатку встановіть [`python-multipart`](https://github.com/Kludex/python-multipart).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, і потім встановили бібліотеку, наприклад:
```console
$ pip install python-multipart
```
///
## Імпорт `Form` { #import-form }
Імпортуйте `Form` з `fastapi`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
## Оголошення параметрів `Form` { #define-form-parameters }
Створюйте параметри форми так само як ви б створювали `Body` або `Query`:
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
Наприклад, один зі способів використання специфікації OAuth2 (так званий «password flow») вимагає надсилати `username` та `password` як поля форми.
специфікація вимагає, щоб ці поля мали точні назви `username` і `password` та надсилалися у вигляді полів форми, а не JSON.
З `Form` ви можете оголошувати ті ж конфігурації, що і з `Body` (та `Query`, `Path`, `Cookie`), включаючи валідацію, приклади, псевдоніми (наприклад, `user-name` замість `username`) тощо.
/// info | Інформація
`Form` — це клас, який безпосередньо наслідується від `Body`.
///
/// tip | Порада
Щоб оголосити тіло форми, потрібно явно використовувати `Form`, оскільки без нього параметри будуть інтерпретуватися як параметри запиту або тіла (JSON).
///
## Про «поля форми» { #about-form-fields }
HTML-форми (``) надсилають дані на сервер у «спеціальному» кодуванні, яке відрізняється від JSON.
**FastAPI** подбає про те, щоб зчитати ці дані з правильного місця, а не з JSON.
/// note | Технічні деталі
Дані з форм зазвичай кодуються за допомогою «типу медіа» `application/x-www-form-urlencoded`.
Але якщо форма містить файли, вона кодується як `multipart/form-data`. Ви дізнаєтеся про обробку файлів у наступному розділі.
Якщо ви хочете дізнатися більше про ці кодування та поля форм, зверніться до [MDN вебдокументації для `POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
///
/// warning | Попередження
Ви можете оголосити кілька параметрів `Form` в *операції шляху*, але не можете одночасно оголосити поля `Body`, які ви очікуєте отримати у форматі JSON, оскільки запит матиме тіло, закодоване як `application/x-www-form-urlencoded`, а не `application/json`.
Це не обмеження **FastAPI**, а частина HTTP-протоколу.
///
## Підсумок { #recap }
Використовуйте `Form` для оголошення вхідних параметрів у вигляді даних форми.
================================================
FILE: docs/uk/docs/tutorial/response-model.md
================================================
# Модель відповіді — Тип, що повертається { #response-model-return-type }
Ви можете оголосити тип, який використовуватиметься у відповіді, анотувавши **тип повернення** *функції операції шляху*.
**Анотації типів** можна використовувати так само, як і для вхідних даних у **параметрах** функції: можна використовувати моделі Pydantic, списки, словники, скалярні значення, як-от цілі числа, булеві значення тощо.
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
FastAPI використовуватиме цей тип повернення, щоб:
* **Перевірити правильність** повернених даних.
* Якщо дані не валідні (наприклад, відсутнє поле), це означає, що *ваш* код застосунку зламаний, не повертає те, що повинен, і буде повернуто помилку сервера замість некоректних даних. Так ви та ваші клієнти можете бути впевнені, що отримаєте дані й очікувану структуру даних.
* Додати **JSON Schema** для відповіді в OpenAPI *операції шляху*.
* Це буде використано в **автоматичній документації**.
* Це також буде використано інструментами, які автоматично генерують клієнтський код.
* **Серіалізувати** повернені дані в JSON за допомогою Pydantic, який написаний мовою **Rust**, тому це буде **набагато швидше**.
Але найголовніше:
* Це **обмежить та відфільтрує** вихідні дані до того, що визначено в типі повернення.
* Це особливо важливо для **безпеки**, нижче ми побачимо про це більше.
## Параметр `response_model` { #response-model-parameter }
Є випадки, коли вам потрібно або ви хочете повертати дані, які не зовсім відповідають тому, що оголошено типом.
Наприклад, ви можете захотіти **повертати словник** або об’єкт бази даних, але **оголосити його як модель Pydantic**. Таким чином модель Pydantic виконуватиме всю документацію даних, валідацію тощо для об’єкта, який ви повернули (наприклад, словника або об’єкта бази даних).
Якщо ви додали анотацію типу повернення, інструменти та редактори скаржитимуться (коректною) помилкою, повідомляючи, що ваша функція повертає тип (наприклад, dict), який відрізняється від того, що ви оголосили (наприклад, модель Pydantic).
У таких випадках можна скористатися параметром *декоратора операції шляху* `response_model` замість типу повернення.
Ви можете використовувати параметр `response_model` у будь-якій з *операцій шляху*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* тощо.
{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *}
/// note | Примітка
Зверніть увагу, що `response_model` є параметром методу «декоратора» (`get`, `post` тощо). А не вашої *функції операції шляху*, як усі параметри та тіло.
///
`response_model` приймає такий самий тип, який ви б вказали для поля моделі Pydantic, тобто це може бути модель Pydantic, але також це може бути, наприклад, `list` моделей Pydantic, як-от `List[Item]`.
FastAPI використовуватиме цей `response_model` для виконання всієї документації даних, валідації тощо, а також для **перетворення та фільтрації вихідних даних** до оголошеного типу.
/// tip | Порада
Якщо у вас увімкнено сувору перевірку типів у редакторі, mypy тощо, ви можете оголосити тип повернення функції як `Any`.
Таким чином, ви повідомляєте редактору, що свідомо повертаєте будь-що. Але FastAPI усе одно виконуватиме документацію даних, валідацію, фільтрацію тощо за допомогою `response_model`.
///
### Пріоритет `response_model` { #response-model-priority }
Якщо ви оголошуєте і тип повернення, і `response_model`, то `response_model` матиме пріоритет і буде використаний FastAPI.
Таким чином ви можете додати правильні анотації типів до ваших функцій, навіть коли повертаєте тип, відмінний від моделі відповіді, щоб це використовували редактор і інструменти на кшталт mypy. І при цьому FastAPI все одно виконуватиме валідацію даних, документацію тощо, використовуючи `response_model`.
Ви також можете використати `response_model=None`, щоб вимкнути створення моделі відповіді для цієї *операції шляху*; це може знадобитися, якщо ви додаєте анотації типів для речей, які не є валідними полями Pydantic, приклад цього ви побачите в одному з розділів нижче.
## Повернути ті самі вхідні дані { #return-the-same-input-data }
Тут ми оголошуємо модель `UserIn`, вона міститиме пароль у відкритому вигляді:
{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *}
/// info | Інформація
Щоб використовувати `EmailStr`, спочатку встановіть [`email-validator`](https://github.com/JoshData/python-email-validator).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім встановили пакет, наприклад:
```console
$ pip install email-validator
```
або так:
```console
$ pip install "pydantic[email]"
```
///
І ми використовуємо цю модель, щоб оголосити наші вхідні дані, і цю ж модель, щоб оголосити наші вихідні дані:
{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *}
Тепер, щоразу коли браузер створює користувача з паролем, API поверне той самий пароль у відповіді.
У цьому випадку це може не бути проблемою, адже це той самий користувач надсилає пароль.
Але якщо ми використаємо цю ж модель для іншої *операції шляху*, ми можемо надсилати паролі наших користувачів кожному клієнту.
/// danger | Обережно
Ніколи не зберігайте пароль користувача у відкритому вигляді та не надсилайте його у відповіді таким чином, якщо тільки ви не знаєте всі застереження і точно розумієте, що робите.
///
## Додати вихідну модель { #add-an-output-model }
Замість цього ми можемо створити вхідну модель з паролем у відкритому вигляді і вихідну модель без нього:
{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *}
Тут, хоча наша *функція операції шляху* повертає того самого вхідного користувача, який містить пароль:
{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *}
...ми оголосили `response_model` як нашу модель `UserOut`, яка не містить пароля:
{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *}
Таким чином, **FastAPI** подбає про фільтрацію всіх даних, які не оголошені у вихідній моделі (використовуючи Pydantic).
### `response_model` або тип повернення { #response-model-or-return-type }
У цьому випадку, оскільки дві моделі різні, якщо ми анотуємо тип повернення функції як `UserOut`, редактор і інструменти скаржитимуться, що ми повертаємо невалідний тип, адже це різні класи.
Саме тому в цьому прикладі нам треба оголосити це через параметр `response_model`.
...але читайте далі нижче, щоб побачити, як це обійти.
## Тип повернення і фільтрація даних { #return-type-and-data-filtering }
Продовжимо з попереднього прикладу. Ми хотіли **анотувати функцію одним типом**, але хотіли мати змогу повертати з функції те, що насправді містить **більше даних**.
Ми хочемо, щоб FastAPI продовжував **фільтрувати** дані, використовуючи модель відповіді. Тобто навіть якщо функція повертає більше даних, відповідь міститиме лише поля, оголошені в моделі відповіді.
У попередньому прикладі, оскільки класи були різні, нам довелося використовувати параметр `response_model`. Але це також означає, що ми не отримуємо підтримки від редактора та інструментів, які перевіряють тип повернення функції.
Проте в більшості випадків, коли нам потрібно зробити щось подібне, ми просто хочемо, щоб модель **відфільтрувала/прибрала** частину даних, як у цьому прикладі.
І в таких випадках ми можемо використати класи та спадкування, щоб скористатися **анотаціями типів** функцій і отримати кращу підтримку в редакторі та інструментах, і при цьому зберегти **фільтрацію даних** у FastAPI.
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
Завдяки цьому ми отримуємо підтримку інструментів — від редакторів і mypy, адже цей код коректний з точки зору типів, — але ми також отримуємо фільтрацію даних від FastAPI.
Як це працює? Давайте розберемося. 🤓
### Анотації типів і підтримка інструментів { #type-annotations-and-tooling }
Спершу подивімося, як це бачать редактори, mypy та інші інструменти.
`BaseUser` має базові поля. Потім `UserIn` успадковує `BaseUser` і додає поле `password`, отже він включатиме всі поля з обох моделей.
Ми анотуємо тип повернення функції як `BaseUser`, але фактично повертаємо екземпляр `UserIn`.
Редактор, mypy та інші інструменти не скаржитимуться на це, тому що з точки зору типізації `UserIn` є підкласом `BaseUser`, а це означає, що він є *валідним* типом, коли очікується будь-що, що є `BaseUser`.
### Фільтрація даних у FastAPI { #fastapi-data-filtering }
Тепер для FastAPI він побачить тип повернення і переконається, що те, що ви повертаєте, містить **лише** поля, які оголошені у цьому типі.
FastAPI виконує кілька внутрішніх операцій з Pydantic, щоб гарантувати, що ті самі правила наслідування класів не застосовуються для фільтрації повернених даних, інакше ви могли б зрештою повертати значно більше даних, ніж очікували.
Таким чином ви можете отримати найкраще з двох світів: анотації типів із **підтримкою інструментів** і **фільтрацію даних**.
## Подивитися в документації { #see-it-in-the-docs }
Коли ви дивитеся автоматичну документацію, ви можете перевірити, що вхідна модель і вихідна модель матимуть власну JSON Schema:
І обидві моделі будуть використані для інтерактивної документації API:
## Інші анотації типів повернення { #other-return-type-annotations }
Можуть бути випадки, коли ви повертаєте щось, що не є валідним полем Pydantic, і анотуєте це у функції лише для того, щоб отримати підтримку від інструментів (редактора, mypy тощо).
### Повернути Response напряму { #return-a-response-directly }
Найпоширенішим випадком буде [повернення Response напряму, як пояснюється пізніше у розширеній документації](../advanced/response-directly.md).
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
Цей простий випадок автоматично обробляється FastAPI, тому що анотація типу повернення — це клас (або підклас) `Response`.
І інструменти також будуть задоволені, бо і `RedirectResponse`, і `JSONResponse` є підкласами `Response`, отже анотація типу коректна.
### Анотувати підклас Response { #annotate-a-response-subclass }
Ви також можете використати підклас `Response` в анотації типу:
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
Це теж працюватиме, бо `RedirectResponse` — підклас `Response`, і FastAPI автоматично обробить цей простий випадок.
### Некоректні анотації типу повернення { #invalid-return-type-annotations }
Але коли ви повертаєте якийсь інший довільний об’єкт, що не є валідним типом Pydantic (наприклад, об’єкт бази даних), і анотуєте його так у функції, FastAPI спробує створити модель відповіді Pydantic на основі цієї анотації типу і це завершиться помилкою.
Те саме станеться, якщо у вас буде об'єднання між різними типами, де один або більше не є валідними типами Pydantic, наприклад, це завершиться помилкою 💥:
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
...це не працює, тому що анотація типу не є типом Pydantic і не є просто одним класом `Response` або його підкласом, це union (будь-який із двох) між `Response` і `dict`.
### Вимкнути модель відповіді { #disable-response-model }
Продовжуючи приклад вище, можливо, ви не хочете мати стандартну валідацію даних, документацію, фільтрацію тощо, які виконує FastAPI.
Але ви можете все одно хотіти залишити анотацію типу повернення у функції, щоб отримати підтримку від інструментів, як-от редактори та перевірки типів (наприклад, mypy).
У такому випадку ви можете вимкнути генерацію моделі відповіді, встановивши `response_model=None`:
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
Це змусить FastAPI пропустити генерацію моделі відповіді, і таким чином ви зможете використовувати будь-які потрібні анотації типів повернення без впливу на ваш FastAPI застосунок. 🤓
## Параметри кодування моделі відповіді { #response-model-encoding-parameters }
Ваша модель відповіді може мати значення за замовчуванням, наприклад:
{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *}
* `description: Union[str, None] = None` (або `str | None = None` у Python 3.10) має значення за замовчуванням `None`.
* `tax: float = 10.5` має значення за замовчуванням `10.5`.
* `tags: List[str] = []` має значення за замовчуванням порожній список: `[]`.
але ви можете захотіти не включати їх у результат, якщо вони фактично не були збережені.
Наприклад, якщо у вас є моделі з багатьма необов’язковими атрибутами у NoSQL базі даних, але ви не хочете надсилати дуже довгі JSON-відповіді, повні значень за замовчуванням.
### Використовуйте параметр `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter }
Ви можете встановити параметр *декоратора операції шляху* `response_model_exclude_unset=True`:
{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *}
і ці значення за замовчуванням не будуть включені у відповідь, лише значення, які фактично встановлені.
Отже, якщо ви надішлете запит до цієї *операції шляху* для елемента з ID `foo`, відповідь (без включення значень за замовчуванням) буде:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
/// info | Інформація
Ви також можете використовувати:
* `response_model_exclude_defaults=True`
* `response_model_exclude_none=True`
як описано в [документації Pydantic](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) для `exclude_defaults` та `exclude_none`.
///
#### Дані зі значеннями для полів із типовими значеннями { #data-with-values-for-fields-with-defaults }
Але якщо ваші дані мають значення для полів моделі з типовими значеннями, як у елемента з ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
вони будуть включені у відповідь.
#### Дані з тими самими значеннями, що й типові { #data-with-the-same-values-as-the-defaults }
Якщо дані мають ті самі значення, що й типові, як у елемента з ID `baz`:
```Python hl_lines="3 5-6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI достатньо розумний (насправді, Pydantic достатньо розумний), щоб зрозуміти, що, хоча `description`, `tax` і `tags` мають ті самі значення, що й типові, їх було встановлено явно (а не взято як значення за замовчуванням).
Отже, вони будуть включені у JSON-відповідь.
/// tip | Порада
Зверніть увагу, що типові значення можуть бути будь-якими, не лише `None`.
Це може бути list (`[]`), `float` зі значенням `10.5` тощо.
///
### `response_model_include` та `response_model_exclude` { #response-model-include-and-response-model-exclude }
Ви також можете використовувати параметри *декоратора операції шляху* `response_model_include` та `response_model_exclude`.
Вони приймають `set` зі `str` з іменами атрибутів, які потрібно включити (пропускаючи решту) або виключити (включаючи решту).
Це можна використовувати як швидкий спосіб, якщо у вас є лише одна модель Pydantic і ви хочете видалити деякі дані з виводу.
/// tip | Порада
Але все ж рекомендується використовувати описані вище підходи, застосовуючи кілька класів, замість цих параметрів.
Це тому, що JSON Schema, який генерується в OpenAPI вашого застосунку (і в документації), все одно буде відповідати повній моделі, навіть якщо ви використовуєте `response_model_include` або `response_model_exclude`, щоб пропустити деякі атрибути.
Це також стосується `response_model_by_alias`, який працює подібним чином.
///
{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *}
/// tip | Порада
Синтаксис `{"name", "description"}` створює `set` з цими двома значеннями.
Він еквівалентний `set(["name", "description"])`.
///
#### Використання `list` замість `set` { #using-lists-instead-of-sets }
Якщо ви забудете використати `set` і натомість застосуєте `list` або `tuple`, FastAPI все одно перетворить це на `set`, і все працюватиме правильно:
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
## Підсумок { #recap }
Використовуйте параметр `response_model` *декоратора операції шляху*, щоб визначати моделі відповіді і особливо щоб гарантувати фільтрацію приватних даних.
Використовуйте `response_model_exclude_unset`, щоб повертати лише явно встановлені значення.
================================================
FILE: docs/uk/docs/tutorial/response-status-code.md
================================================
# Код статусу відповіді { #response-status-code }
Так само, як ви можете вказати модель відповіді, ви також можете оголосити HTTP код статусу, що використовується для відповіді, за допомогою параметра `status_code` в будь-якій з *операцій шляху*:
* `@app.get()`
* `@app.post()`
* `@app.put()`
* `@app.delete()`
* тощо.
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
/// note | Примітка
Зверніть увагу, що `status_code` є параметром методу «декоратора» (`get`, `post`, тощо). Не вашої *функції операції шляху*, як усі параметри та тіло.
///
Параметр `status_code` приймає число з HTTP кодом статусу.
/// info | Інформація
`status_code` також може, як альтернативу, приймати `IntEnum`, наприклад, Python [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus).
///
Він буде:
* Повертати цей код статусу у відповіді.
* Документувати його як такий у схемі OpenAPI (і, таким чином, в інтерфейсах користувача):
/// note | Примітка
Деякі коди відповіді (див. наступний розділ) вказують, що відповідь не має тіла.
FastAPI знає про це і створить документацію OpenAPI, яка вказує, що тіла відповіді немає.
///
## Про HTTP коди статусу { #about-http-status-codes }
/// note | Примітка
Якщо ви вже знаєте, що таке HTTP коди статусу, перейдіть до наступного розділу.
///
В HTTP ви надсилаєте числовий код статусу з 3 цифр як частину відповіді.
Ці коди статусу мають пов’язану назву для їх розпізнавання, але важливою частиною є число.
Коротко:
* `100 - 199` — для «Information». Ви рідко використовуєте їх напряму. Відповіді з такими кодами статусу не можуть мати тіла.
* **`200 - 299`** — для «Successful» відповідей. Це ті, які ви використовуватимете найчастіше.
* `200` — код статусу за замовчуванням, який означає, що все було «OK».
* Інший приклад — `201`, «Created». Його зазвичай використовують після створення нового запису в базі даних.
* Особливий випадок — `204`, «No Content». Цю відповідь використовують, коли немає вмісту для повернення клієнту, і тому відповідь не повинна мати тіла.
* **`300 - 399`** — для «Redirection». Відповіді з цими кодами статусу можуть мати або не мати тіла, за винятком `304`, «Not Modified», яка не повинна мати тіла.
* **`400 - 499`** — для відповідей «Client error». Це другий тип, який ви, ймовірно, будете використовувати найчастіше.
* Приклад — `404`, для відповіді «Not Found».
* Для загальних помилок з боку клієнта ви можете просто використовувати `400`.
* `500 - 599` — для помилок сервера. Ви майже ніколи не використовуєте їх напряму. Коли щось піде не так у якійсь частині коду вашого застосунку або на сервері, автоматично буде повернено один із цих кодів статусу.
/// tip | Порада
Щоб дізнатися більше про кожен код статусу і для чого призначений кожен із них, перегляньте [документацію MDN про HTTP коди статусу](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
///
## Скорочення, щоб запам’ятати назви { #shortcut-to-remember-the-names }
Розглянемо попередній приклад ще раз:
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
`201` — це код статусу для «Created».
Але вам не потрібно запам'ятовувати, що означає кожен із цих кодів.
Ви можете використовувати зручні змінні з `fastapi.status`.
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
Вони — просто для зручності, містять те саме число, але так ви можете скористатися автозаповненням редактора, щоб знайти їх:
/// note | Технічні деталі
Ви також можете використати `from starlette import status`.
**FastAPI** надає той самий `starlette.status` як `fastapi.status` просто як зручність для вас, розробника. Але він походить безпосередньо зі Starlette.
///
## Зміна значення за замовчуванням { #changing-the-default }
Пізніше, у [Просунутому посібнику користувача](../advanced/response-change-status-code.md), ви побачите, як повертати інший код статусу, ніж значення за замовчуванням, яке ви оголошуєте тут.
================================================
FILE: docs/uk/docs/tutorial/schema-extra-example.md
================================================
# Декларування прикладів вхідних даних { #declare-request-example-data }
Ви можете задати приклади даних, які ваш застосунок може отримувати.
Ось кілька способів, як це зробити.
## Додаткові дані Схеми JSON у моделях Pydantic { #extra-json-schema-data-in-pydantic-models }
Ви можете задати `examples` для моделі Pydantic, які буде додано до згенерованої Схеми JSON.
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
Ця додаткова інформація буде додана як є до **Схеми JSON** для цієї моделі, і вона буде використана в документації до API.
Ви можете використати атрибут `model_config`, який приймає `dict`, як описано в [документації Pydantic: Configuration](https://docs.pydantic.dev/latest/api/config/).
Ви можете встановити `"json_schema_extra"` як `dict`, що містить будь-які додаткові дані, які ви хочете відобразити у згенерованій Схемі JSON, включаючи `examples`.
/// tip | Порада
Ви можете використати ту ж техніку, щоб розширити Схему JSON і додати власну додаткову інформацію.
Наприклад, ви можете використати її для додавання метаданих для інтерфейсу користувача на фронтенді тощо.
///
/// info | Інформація
OpenAPI 3.1.0 (який використовується починаючи з FastAPI 0.99.0) додав підтримку `examples`, що є частиною стандарту **Схеми JSON**.
До цього підтримувався лише ключ `example` з одним прикладом. Він все ще підтримується в OpenAPI 3.1.0, але є застарілим і не входить до стандарту Схеми JSON. Тому рекомендується перейти з `example` на `examples`. 🤓
Більше про це можна прочитати в кінці цієї сторінки.
///
## Додаткові аргументи `Field` { #field-additional-arguments }
Коли ви використовуєте `Field()` у моделях Pydantic, ви також можете вказати додаткові `examples`:
{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *}
## `examples` у Схемі JSON - OpenAPI { #examples-in-json-schema-openapi }
При використанні будь-кого з наступного:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
ви також можете задати набір `examples` з додатковою інформацією, яка буде додана до їхніх **Схем JSON** у **OpenAPI**.
### `Body` з `examples` { #body-with-examples }
Тут ми передаємо `examples`, які містять один приклад очікуваних даних у `Body()`:
{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *}
### Приклад у UI документації { #example-in-the-docs-ui }
За допомогою будь-якого з наведених вище методів це виглядатиме так у `/docs`:
### `Body` з кількома `examples` { #body-with-multiple-examples }
Звичайно, ви також можете передати кілька `examples`:
{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *}
Коли ви це робите, приклади будуть частиною внутрішньої **Схеми JSON** для цих даних тіла.
Втім, на час написання цього, Swagger UI - інструмент, який відповідає за відображення UI документації - не підтримує показ кількох прикладів для даних у **Схемі JSON**. Але нижче можна прочитати про обхідний шлях.
### Специфічні для OpenAPI `examples` { #openapi-specific-examples }
Ще до того, як **Схема JSON** почала підтримувати `examples`, OpenAPI вже мала підтримку іншого поля, яке також називається `examples`.
Це **специфічне для OpenAPI** поле `examples` розміщується в іншому розділі специфікації OpenAPI. Воно розміщується в **деталях кожної *операції шляху***, а не всередині кожної Схеми JSON.
І Swagger UI вже давно підтримує це поле `examples`. Тому ви можете використовувати його, щоб **відображати** різні **приклади в UI документації**.
Форма цього специфічного для OpenAPI поля `examples` - це `dict` з **кількома прикладами** (а не `list`), кожен із яких має додаткову інформацію, яка також буде додана до **OpenAPI**.
Воно не включається всередину кожної Схеми JSON, що міститься в OpenAPI, воно розміщується зовні, безпосередньо в *операції шляху*.
### Використання параметра `openapi_examples` { #using-the-openapi-examples-parameter }
Ви можете оголосити специфічні для OpenAPI `examples` у FastAPI за допомогою параметра `openapi_examples` для:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* `Body()`
* `Form()`
* `File()`
Ключі `dict` ідентифікують кожен приклад, а кожне значення - це інший `dict`.
Кожен специфічний `dict` прикладу в `examples` може містити:
* `summary`: короткий опис прикладу.
* `description`: розгорнутий опис, який може містити Markdown.
* `value`: це сам приклад, який буде показано, наприклад `dict`.
* `externalValue`: альтернатива `value`, URL-адреса, що вказує на приклад. Проте це може не підтримуватися такою кількістю інструментів, як `value`.
Використання виглядає так:
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
### Приклади OpenAPI в UI документації { #openapi-examples-in-the-docs-ui }
З `openapi_examples`, доданим до `Body()`, `/docs` виглядатиме так:
## Технічні деталі { #technical-details }
/// tip | Порада
Якщо ви вже використовуєте **FastAPI** версії **0.99.0 або вище**, ви, ймовірно, можете **пропустити** ці технічні деталі.
Вони більш актуальні для старих версій, до появи OpenAPI 3.1.0.
Можна вважати це коротким **історичним екскурсом** у OpenAPI та Схему JSON. 🤓
///
/// warning | Попередження
Це дуже технічна інформація про стандарти **Схема JSON** і **OpenAPI**.
Якщо вищезгадані ідеї вже працюють у вас, цього може бути достатньо, і вам, ймовірно, не потрібні ці деталі - можете пропустити.
///
До OpenAPI 3.1.0 OpenAPI використовував стару та модифіковану версію **Схеми JSON**.
Схема JSON не мала `examples`, тож OpenAPI додала власне поле `example` до своєї модифікованої версії.
OpenAPI також додала поля `example` і `examples` до інших частин специфікації:
* [`Parameter Object` (у специфікації)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object), який використовувався утилітами FastAPI:
* `Path()`
* `Query()`
* `Header()`
* `Cookie()`
* [`Request Body Object`, у полі `content`, у `Media Type Object` (у специфікації)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object), який використовувався утилітами FastAPI:
* `Body()`
* `File()`
* `Form()`
/// info | Інформація
Цей старий специфічний для OpenAPI параметр `examples` тепер називається `openapi_examples`, починаючи з FastAPI `0.103.0`.
///
### Поле `examples` у Схемі JSON { #json-schemas-examples-field }
Пізніше Схема JSON додала поле [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) у нову версію специфікації.
А потім новий OpenAPI 3.1.0 базувався на найновішій версії (JSON Schema 2020-12), яка включала це нове поле `examples`.
І тепер це нове поле `examples` має вищий пріоритет за старе одиночне (і кастомне) поле `example`, яке тепер є застарілим.
Це нове поле `examples` у Схемі JSON - це **просто `list`** прикладів, а не `dict` з додатковими метаданими, як в інших місцях OpenAPI (описаних вище).
/// info | Інформація
Навіть після релізу OpenAPI 3.1.0 з цією новою простішою інтеграцією зі Схемою JSON, протягом певного часу Swagger UI, інструмент, який надає автоматичну документацію, не підтримував OpenAPI 3.1.0 (тепер підтримує, починаючи з версії 5.0.0 🎉).
Через це версії FastAPI до 0.99.0 все ще використовували версії OpenAPI нижчі за 3.1.0.
///
### `examples` у Pydantic і FastAPI { #pydantic-and-fastapi-examples }
Коли ви додаєте `examples` у модель Pydantic через `schema_extra` або `Field(examples=["something"])`, цей приклад додається до **Схеми JSON** для цієї моделі Pydantic.
І ця **Схема JSON** Pydantic-моделі включається до **OpenAPI** вашого API, а потім використовується в UI документації.
У версіях FastAPI до 0.99.0 (0.99.0 і вище використовують новіший OpenAPI 3.1.0), коли ви використовували `example` або `examples` з будь-якими іншими утилітами (`Query()`, `Body()` тощо), ці приклади не додавалися до Схеми JSON, що описує ці дані (навіть не до власної версії Схеми JSON в OpenAPI), натомість вони додавалися безпосередньо до декларації *операції шляху* в OpenAPI (поза межами частин OpenAPI, які використовують Схему JSON).
Але тепер, коли FastAPI 0.99.0 і вище використовує OpenAPI 3.1.0, який використовує JSON Schema 2020-12, і Swagger UI 5.0.0 і вище, все стало більш узгодженим, і приклади включаються до Схеми JSON.
### Swagger UI та специфічні для OpenAPI `examples` { #swagger-ui-and-openapi-specific-examples }
Оскільки Swagger UI не підтримував кілька прикладів Схеми JSON (станом на 2023-08-26), користувачі не мали можливості показати кілька прикладів у документації.
Щоб вирішити це, FastAPI `0.103.0` **додав підтримку** оголошення того самого старого **OpenAPI-специфічного** поля `examples` через новий параметр `openapi_examples`. 🤓
### Підсумок { #summary }
Раніше я казав, що не дуже люблю історію... а тепер подивіться на мене - читаю «технічні історичні» лекції. 😅
Коротко: **оновіться до FastAPI 0.99.0 або вище** - і все стане значно **простішим, узгодженим та інтуїтивно зрозумілим**, і вам не доведеться знати всі ці історичні деталі. 😎
================================================
FILE: docs/uk/docs/tutorial/security/first-steps.md
================================================
# Безпека - перші кроки { #security-first-steps }
Уявімо, що ваш **backend** API працює на певному домені.
А **frontend** - на іншому домені або в іншому шляху того ж домену (або у мобільному застосунку).
І ви хочете, щоб frontend міг автентифікуватися в backend, використовуючи ім'я користувача та пароль.
Ми можемо використати **OAuth2**, щоб збудувати це з **FastAPI**.
Але заощадимо вам час на читання всієї довгої специфікації, щоб знайти лише потрібні дрібниці.
Скористаймося інструментами, які надає **FastAPI**, щоб обробляти безпеку.
## Як це виглядає { #how-it-looks }
Спочатку просто запустімо код і подивімося, як він працює, а потім повернемося, щоб розібратися, що відбувається.
## Створіть `main.py` { #create-main-py }
Скопіюйте приклад у файл `main.py`:
{* ../../docs_src/security/tutorial001_an_py310.py *}
## Запустіть { #run-it }
/// info | Інформація
Пакет [`python-multipart`](https://github.com/Kludex/python-multipart) автоматично встановлюється з **FastAPI**, коли ви виконуєте команду `pip install "fastapi[standard]"`.
Однак, якщо ви використовуєте команду `pip install fastapi`, пакет `python-multipart` за замовчуванням не включено.
Щоб встановити його вручну, переконайтеся, що ви створили [віртуальне оточення](../../virtual-environments.md), активували його, а потім встановили:
```console
$ pip install python-multipart
```
Це тому, що **OAuth2** використовує «form data» для надсилання `username` та `password`.
///
Запустіть приклад:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Перевірте { #check-it }
Перейдіть до інтерактивної документації: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите щось подібне:
/// check | Кнопка Authorize!
У вас уже є нова блискуча кнопка «Authorize».
А ваша *операція шляху* має маленький замок у правому верхньому куті, на який можна натиснути.
///
Якщо натиснути її, з’явиться маленька форма авторизації, щоб ввести `username` і `password` (та інші необов’язкові поля):
/// note | Примітка
Неважливо, що ви введете у форму, поки що це не спрацює. Але ми скоро це доробимо.
///
Звісно, це не frontend для кінцевих користувачів, але це чудовий інструмент для інтерактивної документації всього вашого API.
Ним може користуватися команда frontend (якою можете бути і ви самі).
Ним можуть користуватися сторонні застосунки та системи.
І ним також можете користуватися ви самі, щоб налагоджувати, перевіряти та тестувати той самий застосунок.
## Потік паролю { #the-password-flow }
Тепер повернімося трохи назад і розберімося, що це все таке.
`password` «flow» - це один зі способів («flows»), визначених в OAuth2, для обробки безпеки та автентифікації.
OAuth2 був спроєктований так, щоб backend або API могли бути незалежними від сервера, який автентифікує користувача.
Але в нашому випадку той самий застосунок **FastAPI** оброблятиме і API, і автентифікацію.
Тож розгляньмо це у спрощеному вигляді:
- Користувач вводить `username` і `password` у frontend і натискає `Enter`.
- Frontend (у браузері користувача) надсилає ці `username` і `password` на специфічну URL-адресу нашого API (оголошену як `tokenUrl="token"`).
- API перевіряє ці `username` і `password` та повертає «токен» (ми ще нічого з цього не реалізували).
- «Токен» - це просто строка з деяким вмістом, який ми можемо пізніше використати, щоб перевірити цього користувача.
- Зазвичай токен налаштований на завершення строку дії через певний час.
- Тож користувачу доведеться знову увійти пізніше.
- І якщо токен викрадуть, ризик менший. Це не як постійний ключ, який працюватиме завжди (у більшості випадків).
- Frontend тимчасово зберігає цей токен десь.
- Користувач клікає у frontend, щоб перейти до іншого розділу вебзастосунку.
- Frontend має отримати ще дані з API.
- Але для того конкретного кінцевого пункту потрібна автентифікація.
- Тож, щоб автентифікуватися в нашому API, він надсилає заголовок `Authorization` зі значенням `Bearer ` плюс токен.
- Якщо токен містить `foobar`, вміст заголовка `Authorization` буде: `Bearer foobar`.
## `OAuth2PasswordBearer` у **FastAPI** { #fastapis-oauth2passwordbearer }
**FastAPI** надає кілька інструментів на різних рівнях абстракції, щоб реалізувати ці функції безпеки.
У цьому прикладі ми використаємо **OAuth2** з потоком **Password**, використовуючи токен **Bearer**. Це робиться за допомогою класу `OAuth2PasswordBearer`.
/// info | Інформація
«Bearer»-токен - не єдиний варіант.
Але це найкращий для нашого сценарію.
І, можливо, найкращий для більшості сценаріїв, якщо ви не експерт з OAuth2 і точно не знаєте, чому інша опція краще відповідає вашим потребам.
У такому разі **FastAPI** теж надає інструменти, щоб це збудувати.
///
Коли ми створюємо екземпляр класу `OAuth2PasswordBearer`, ми передаємо параметр `tokenUrl`. Цей параметр містить URL, який клієнт (frontend у браузері користувача) використовуватиме, щоб надіслати `username` і `password` для отримання токена.
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
/// tip | Порада
Тут `tokenUrl="token"` відноситься до відносної URL-адреси `token`, яку ми ще не створили. Оскільки це відносна URL-адреса, вона еквівалентна `./token`.
Тому, якщо ваш API розміщений на `https://example.com/`, це буде `https://example.com/token`. А якщо на `https://example.com/api/v1/`, тоді це буде `https://example.com/api/v1/token`.
Використання відносної URL-адреси важливе, щоб ваша програма продовжувала працювати навіть у просунутому сценарії, як-от [За представником](../../advanced/behind-a-proxy.md).
///
Цей параметр не створює той endpoint / *операцію шляху*, а декларує, що URL `/token` буде тим, який клієнт має використовувати, щоб отримати токен. Ця інформація використовується в OpenAPI, а потім - у системах інтерактивної документації API.
Незабаром ми також створимо фактичну операцію шляху.
/// info | Інформація
Якщо ви дуже строгий «Pythonista», вам може не подобатися стиль імені параметра `tokenUrl` замість `token_url`.
Це тому, що використано ту саму назву, що і в специфікації OpenAPI. Тож якщо вам потрібно буде дослідити ці схеми безпеки глибше, ви зможете просто скопіювати та вставити її, щоб знайти більше інформації.
///
Змінна `oauth2_scheme` - це екземпляр `OAuth2PasswordBearer`, але це також і «викликаємий» об’єкт.
Його можна викликати так:
```Python
oauth2_scheme(some, parameters)
```
Тож його можна використовувати з `Depends`.
### Використання { #use-it }
Тепер ви можете передати `oauth2_scheme` як залежність через `Depends`.
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Ця залежність надасть `str`, який буде присвоєний параметру `token` *функції операції шляху*.
**FastAPI** знатиме, що може використати цю залежність, щоб визначити «схему безпеки» в схемі OpenAPI (і в автоматичній документації API).
/// info | Технічні деталі
**FastAPI** знатиме, що може використати клас `OAuth2PasswordBearer` (оголошений у залежності), щоб визначити схему безпеки в OpenAPI, тому що він наслідує `fastapi.security.oauth2.OAuth2`, який своєю чергою наслідує `fastapi.security.base.SecurityBase`.
Усі утиліти безпеки, які інтегруються з OpenAPI (і автоматичною документацією API), наслідують `SecurityBase`. Так **FastAPI** розуміє, як інтегрувати їх в OpenAPI.
///
## Що відбувається { #what-it-does }
Вона шукатиме в запиті заголовок `Authorization`, перевірить, чи його значення - це `Bearer ` плюс деякий токен, і поверне токен як `str`.
Якщо заголовка `Authorization` немає або значення не містить токена `Bearer `, вона одразу відповість помилкою зі статус-кодом 401 (`UNAUTHORIZED`).
Вам навіть не потрібно перевіряти, чи існує токен, щоб повернути помилку. Ви можете бути певні: якщо ваша функція виконується, у параметрі токена буде `str`.
Ви вже можете спробувати це в інтерактивній документації:
Ми ще не перевіряємо дійсність токена, але це вже початок.
## Підсумок { #recap }
Отже, лише 3-4 додатковими рядками ви вже маєте деяку примітивну форму безпеки.
================================================
FILE: docs/uk/docs/tutorial/security/get-current-user.md
================================================
# Отримати поточного користувача { #get-current-user }
У попередньому розділі система безпеки (яка базується на системі впровадження залежностей) передавала функції операції шляху `token` як `str`:
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
Але це ще не дуже корисно.
Зробімо так, щоб вона повертала поточного користувача.
## Створити модель користувача { #create-a-user-model }
Спочатку створімо модель користувача в Pydantic.
Так само, як ми використовуємо Pydantic для оголошення тіл, ми можемо використовувати його будь-де:
{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:6] *}
## Створити залежність `get_current_user` { #create-a-get-current-user-dependency }
Створімо залежність `get_current_user`.
Пам'ятаєте, що залежності можуть мати підзалежності?
`get_current_user` матиме залежність із тим самим `oauth2_scheme`, який ми створили раніше.
Так само, як ми робили раніше безпосередньо в операції шляху, наша нова залежність `get_current_user` отримає `token` як `str` від підзалежності `oauth2_scheme`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *}
## Отримати користувача { #get-the-user }
`get_current_user` використає (фальшиву) утилітну функцію, яку ми створили, що приймає `token` як `str` і повертає нашу Pydantic-модель `User`:
{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *}
## Впровадити поточного користувача { #inject-the-current-user }
Тепер ми можемо використати той самий `Depends` з нашим `get_current_user` в операції шляху:
{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *}
Зверніть увагу, що ми оголошуємо тип `current_user` як Pydantic-модель `User`.
Це допоможе всередині функції з автодоповненням і перевірками типів.
/// tip | Порада
Можливо, ви пам'ятаєте, що тіла запиту також оголошуються моделями Pydantic.
Тут **FastAPI** не заплутається, тому що ви використовуєте `Depends`.
///
/// check | Перевірте
Те, як спроєктована ця система залежностей, дозволяє мати різні залежності (різні «залежні»), які всі повертають модель `User`.
Ми не обмежені наявністю лише однієї залежності, що може повертати такі дані.
///
## Інші моделі { #other-models }
Тепер ви можете отримувати поточного користувача безпосередньо у функціях операцій шляху та працювати з механізмами безпеки на рівні **впровадження залежностей**, використовуючи `Depends`.
І ви можете використовувати будь-яку модель або дані для вимог безпеки (у цьому випадку Pydantic-модель `User`).
Але ви не обмежені використанням якоїсь конкретної модели даних, класу чи типу.
Хочете мати id та email і не мати жодного username у вашій моделі? Без проблем. Ви можете використовувати ті самі інструменти.
Хочете мати просто `str`? Або лише `dict`? Або безпосередньо екземпляр класу моделі бази даних? Усе працює так само.
У вашій програмі насправді входять не користувачі, а роботи, боти чи інші системи, що мають лише токен доступу? Знову ж, усе працює так само.
Просто використовуйте будь-який тип моделі, будь-який клас, будь-яку базу даних, які потрібні вашій програмі. **FastAPI** подбає про це завдяки системі впровадження залежностей.
## Розмір коду { #code-size }
Цей приклад може здаватися багатослівним. Майте на увазі, що ми змішуємо безпеку, моделі даних, утилітні функції та операції шляху в одному файлі.
Але ось ключовий момент.
Речі, пов'язані з безпекою та впровадженням залежностей, пишуться один раз.
І ви можете зробити це настільки складним, наскільки потрібно. І все одно мати це написаним лише один раз, в одному місці. З усією гнучкістю.
Зате ви можете мати тисячі кінцевих точок (операцій шляху), що використовують одну й ту саму систему безпеки.
І всі вони (або будь-яка їхня частина, яку ви захочете) можуть скористатися повторним використанням цих залежностей або будь-яких інших, які ви створите.
І всі ці тисячі операцій шляху можуть бути всього у 3 рядки:
{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *}
## Підсумок { #recap }
Тепер ви можете отримувати поточного користувача безпосередньо у вашій функції операції шляху.
Ми вже на півдорозі.
Потрібно лише додати операцію шляху, щоб користувач/клієнт міг фактично надіслати `username` і `password`.
Далі саме це.
================================================
FILE: docs/uk/docs/tutorial/security/index.md
================================================
# Безпека { #security }
Існує багато способів реалізувати безпеку, автентифікацію та авторизацію.
І зазвичай це складна і «непроста» тема.
У багатьох фреймворках і системах лише обробка безпеки та автентифікації потребує великих зусиль і коду (у багатьох випадках це може бути 50% або більше від усього написаного коду).
**FastAPI** надає кілька інструментів, які допоможуть вам працювати з **безпекою** легко, швидко, стандартним способом, без необхідності вивчати всі специфікації безпеки.
Але спочатку розгляньмо кілька невеликих понять.
## Поспішаєте? { #in-a-hurry }
Якщо вам не цікаві всі ці терміни й вам просто потрібно додати безпеку з автентифікацією на основі імені користувача та пароля *прямо зараз*, переходьте до наступних розділів.
## OAuth2 { #oauth2 }
OAuth2 — це специфікація, що визначає кілька способів обробки автентифікації та авторизації.
Це досить об'ємна специфікація, яка охоплює кілька складних випадків використання.
Вона включає способи автентифікації через «третю сторону».
Саме це лежить в основі всіх систем із «увійти через Facebook, Google, X (Twitter), GitHub».
### OAuth 1 { #oauth-1 }
Раніше існував OAuth 1, який значно відрізняється від OAuth2 і є складнішим, оскільки містив прямі специфікації щодо того, як шифрувати комунікацію.
Зараз він не дуже популярний або використовується.
OAuth2 не вказує, як саме шифрувати комунікацію — він очікує, що ваш застосунок доступний через HTTPS.
/// tip | Порада
У розділі про **деплой** ви побачите, як налаштувати HTTPS безкоштовно з Traefik та Let's Encrypt.
///
## OpenID Connect { #openid-connect }
OpenID Connect — ще одна специфікація, побудована на основі **OAuth2**.
Вона просто розширює OAuth2, уточнюючи деякі відносно неоднозначні речі в OAuth2, щоб зробити його більш сумісним.
Наприклад, вхід через Google використовує OpenID Connect (який під капотом використовує OAuth2).
Але вхід через Facebook не підтримує OpenID Connect. Він має власний різновид OAuth2.
### OpenID (не «OpenID Connect») { #openid-not-openid-connect }
Існувала також специфікація «OpenID». Вона намагалася розвʼязати те саме, що й **OpenID Connect**, але не базувалась на OAuth2.
Тож це була повністю додаткова система.
Зараз вона не дуже популярна або використовується.
## OpenAPI { #openapi }
OpenAPI (раніше відомий як Swagger) — це відкрита специфікація для побудови API (тепер частина Linux Foundation).
**FastAPI** базується на **OpenAPI**.
Саме це робить можливими кілька автоматичних інтерактивних інтерфейсів документації, генерацію коду тощо.
OpenAPI має спосіб визначати різні «схеми» безпеки.
Використовуючи їх, ви можете скористатися всіма цими інструментами, що базуються на стандартах, зокрема цими інтерактивними системами документації.
OpenAPI визначає такі схеми безпеки:
* `apiKey`: специфічний для застосунку ключ, який може передаватися через:
* Параметр запиту.
* Заголовок.
* Кукі.
* `http`: стандартні системи HTTP-автентифікації, включаючи:
* `bearer`: заголовок `Authorization` зі значенням `Bearer ` та токеном. Це успадковано з OAuth2.
* базову автентифікацію HTTP.
* HTTP дайджест тощо.
* `oauth2`: усі способи обробки безпеки за допомогою OAuth2 (так звані «потоки»).
* Декілька з цих потоків підходять для створення провайдера автентифікації OAuth 2.0 (наприклад, Google, Facebook, X (Twitter), GitHub тощо):
* `implicit`
* `clientCredentials`
* `authorizationCode`
* Але є один окремий «потік», який можна ідеально використати для обробки автентифікації напряму в цьому ж застосунку:
* `password`: у кількох наступних розділах будуть приклади цього.
* `openIdConnect`: має спосіб визначити, як автоматично виявляти дані автентифікації OAuth2.
* Саме це автоматичне виявлення визначено у специфікації OpenID Connect.
/// tip | Порада
Інтеграція інших провайдерів автентифікації/авторизації, таких як Google, Facebook, X (Twitter), GitHub тощо, також можлива і відносно проста.
Найскладніше — це створити провайдера автентифікації/авторизації на кшталт таких, але **FastAPI** надає вам інструменти, щоб зробити це легко, виконуючи важку частину роботи за вас.
///
## Утиліти **FastAPI** { #fastapi-utilities }
FastAPI надає кілька інструментів для кожної з описаних схем безпеки в модулі `fastapi.security`, які спрощують використання цих механізмів безпеки.
У наступних розділах ви побачите, як додати безпеку до свого API за допомогою цих інструментів, які надає **FastAPI**.
А також побачите, як це автоматично інтегрується в інтерактивну систему документації.
================================================
FILE: docs/uk/docs/tutorial/security/oauth2-jwt.md
================================================
# OAuth2 з паролем (і хешуванням), Bearer з токенами JWT { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens }
Тепер, коли ми маємо весь потік безпеки, зробімо застосунок справді захищеним, використовуючи токени JWT і безпечне хешування паролів.
Цей код ви можете реально використовувати у своєму застосунку, зберігати хеші паролів у своїй базі даних тощо.
Ми почнемо з того місця, де зупинилися в попередньому розділі, і розширимо його.
## Про JWT { #about-jwt }
JWT означає «JSON Web Tokens».
Це стандарт кодування об'єкта JSON у довгий щільний рядок без пробілів. Він виглядає так:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
Він не зашифрований, тому кожен може відновити інформацію з вмісту.
Але він підписаний. Тож коли ви отримуєте токен, який ви видали, ви можете перевірити, що справді його видали ви.
Таким чином, ви можете створити токен із терміном дії, наприклад, 1 тиждень. І коли користувач повернеться наступного дня з токеном, ви знатимете, що користувач досі увійшов у вашу систему.
Через тиждень токен завершить термін дії, і користувач не буде авторизований та має знову увійти, щоб отримати новий токен. І якщо користувач (або третя сторона) намагатиметься змінити токен, щоб змінити термін дії, ви це виявите, бо підписи не співпадатимуть.
Якщо хочете «погратися» з токенами JWT і побачити, як вони працюють, перегляньте [https://jwt.io](https://jwt.io/).
## Встановіть `PyJWT` { #install-pyjwt }
Нам потрібно встановити `PyJWT`, щоб створювати та перевіряти токени JWT у Python.
Переконайтеся, що ви створили [віртуальне оточення](../../virtual-environments.md), активували його і тоді встановіть `pyjwt`:
```console
$ pip install pyjwt
---> 100%
```
/// info | Інформація
Якщо ви плануєте використовувати алгоритми цифрового підпису на кшталт RSA або ECDSA, слід встановити залежність криптобібліотеки `pyjwt[crypto]`.
Докладніше про це можна прочитати у [документації з встановлення PyJWT](https://pyjwt.readthedocs.io/en/latest/installation.html).
///
## Хешування паролів { #password-hashing }
«Хешування» означає перетворення деякого вмісту (у цьому випадку пароля) на послідовність байтів (просто строку), що виглядає як нісенітниця.
Кожного разу, коли ви передаєте точно той самий вміст (точно той самий пароль), ви отримуєте точно ту саму «нісенітницю».
Але не можна перетворити цю «нісенітницю» назад у пароль.
### Навіщо використовувати хешування паролів { #why-use-password-hashing }
Якщо вашу базу даних вкрадуть, зловмисник не матиме відкритих паролів ваших користувачів, а лише хеші.
Тож зловмисник не зможе спробувати використати цей пароль в іншій системі (оскільки багато користувачів використовують той самий пароль всюди, це було б небезпечно).
## Встановіть `pwdlib` { #install-pwdlib }
pwdlib - це чудовий пакет Python для роботи з хешами паролів.
Він підтримує багато безпечних алгоритмів хешування та утиліт для роботи з ними.
Рекомендований алгоритм - «Argon2».
Переконайтеся, що ви створили [віртуальне оточення](../../virtual-environments.md), активували його і тоді встановіть pwdlib з Argon2:
/// tip | Порада
З `pwdlib` ви навіть можете налаштувати його так, щоб він умів читати паролі, створені **Django**, плагіном безпеки **Flask** або багатьма іншими.
Тож ви зможете, наприклад, спільно використовувати ті самі дані з застосунку Django в базі даних із застосунком FastAPI. Або поступово мігрувати застосунок Django, використовуючи ту саму базу даних.
І ваші користувачі зможуть входити як із вашого застосунку Django, так і з вашого застосунку **FastAPI** одночасно.
///
## Хешування і перевірка паролів { #hash-and-verify-the-passwords }
Імпортуйте потрібні інструменти з `pwdlib`.
Створіть екземпляр PasswordHash з рекомендованими налаштуваннями - він буде використаний для хешування та перевірки паролів.
/// tip | Порада
pwdlib також підтримує алгоритм хешування bcrypt, але не включає застарілі алгоритми - для роботи із застарілими хешами рекомендується використовувати бібліотеку passlib.
Наприклад, ви можете використати її для читання і перевірки паролів, згенерованих іншою системою (наприклад, Django), але хешувати будь-які нові паролі іншим алгоритмом, таким як Argon2 або Bcrypt.
І бути сумісними з усіма ними одночасно.
///
Створіть утилітарну функцію для хешування пароля, що надходить від користувача.
І ще одну утиліту для перевірки, чи отриманий пароль відповідає збереженому хешу.
І ще одну - для автентифікації та повернення користувача.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
Коли `authenticate_user` викликається з ім'ям користувача, якого немає в базі даних, ми все одно запускаємо `verify_password` для «підставного» хешу.
Це забезпечує приблизно однаковий час відповіді кінцевої точки незалежно від того, чи є ім'я користувача дійсним, запобігаючи **атакам за часом**, які могли б бути використані для перелічення наявних імен користувачів.
/// note | Примітка
Якщо ви перевірите нову (фальшиву) базу даних `fake_users_db`, побачите, як зараз виглядає хеш пароля: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`.
///
## Опрацювання токенів JWT { #handle-jwt-tokens }
Імпортуйте встановлені модулі.
Створіть випадковий секретний ключ, який буде використано для підписання токенів JWT.
Щоб згенерувати безпечний випадковий секретний ключ, використайте команду:
І скопіюйте вивід у змінну `SECRET_KEY` (не використовуйте той, що в прикладі).
Створіть змінну `ALGORITHM` з алгоритмом для підписання токена JWT і встановіть її в `"HS256"`.
Створіть змінну для терміну дії токена.
Визначте модель Pydantic, яка буде використана в кінцевій точці токена для відповіді.
Створіть утилітарну функцію для генерації нового токена доступу.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Оновіть залежності { #update-the-dependencies }
Оновіть `get_current_user`, щоб отримувати той самий токен, що й раніше, але цього разу - токен JWT.
Декодуйте отриманий токен, перевірте його та поверніть поточного користувача.
Якщо токен недійсний, одразу поверніть помилку HTTP.
{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Оновіть операцію шляху `/token` { #update-the-token-path-operation }
Створіть `timedelta` з часом життя токена.
Створіть справжній токен доступу JWT і поверніть його.
{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Технічні деталі про «subject» `sub` у JWT { #technical-details-about-the-jwt-subject-sub }
Специфікація JWT каже, що існує ключ `sub` із суб'єктом токена.
Використовувати його не обов'язково, але саме туди зазвичай поміщають ідентифікатор користувача, тож ми використовуємо його тут.
JWT може використовуватися й для інших речей, окрім ідентифікації користувача та надання йому можливості безпосередньо виконувати операції з вашою API.
Наприклад, ви можете ідентифікувати «автомобіль» або «допис у блозі».
Тоді ви можете додати дозволи щодо цієї сутності, як-от «керувати» (для автомобіля) або «редагувати» (для допису).
І потім ви можете видати цей токен JWT користувачу (або боту), і він зможе виконувати ці дії (керувати автомобілем або редагувати допис), навіть не маючи облікового запису - лише з токеном JWT, який ваша API для цього згенерувала.
Використовуючи ці ідеї, JWT можна застосовувати у значно складніших сценаріях.
У таких випадках кілька сутностей можуть мати однакові ідентифікатори, скажімо `foo` (користувач `foo`, автомобіль `foo` і допис `foo`).
Щоб уникнути колізій ідентифікаторів, під час створення токена JWT для користувача ви можете додати префікс до значення ключа `sub`, наприклад `username:`. Отже, у цьому прикладі значення `sub` могло б бути: `username:johndoe`.
Важливо пам'ятати, що ключ `sub` має містити унікальний ідентифікатор у межах усього застосунку і має бути строкою.
## Перевірте { #check-it }
Запустіть сервер і перейдіть до документації: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
Ви побачите такий інтерфейс користувача:
Авторизуйте застосунок так само, як раніше.
Використайте облікові дані:
Username: `johndoe`
Password: `secret`
/// check | Перевірте
Зверніть увагу, що ніде в коді немає відкритого пароля "`secret`", ми маємо лише хешовану версію.
///
Викличте кінцеву точку `/users/me/`, ви отримаєте відповідь:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false
}
```
Якщо відкриєте інструменти розробника, ви побачите, що у відправлених даних є лише токен, пароль надсилається тільки в першому запиті для автентифікації користувача та отримання токена доступу, але не надсилається далі:
/// note | Примітка
Зверніть увагу на заголовок `Authorization` зі значенням, що починається з `Bearer `.
///
## Просунуте використання зі `scopes` { #advanced-usage-with-scopes }
OAuth2 має поняття «scopes».
Ви можете використовувати їх, щоб додати конкретний набір дозволів до токена JWT.
Потім ви можете видати цей токен користувачу напряму або третій стороні для взаємодії з вашою API із набором обмежень.
Ви можете дізнатися, як їх використовувати і як вони інтегровані з **FastAPI** пізніше у **просунутому посібнику користувача**.
## Підсумок { #recap }
Маючи все, що ви бачили досі, ви можете налаштувати захищений застосунок **FastAPI**, використовуючи стандарти на кшталт OAuth2 і JWT.
Майже в будь-якому фреймворку опрацювання безпеки дуже швидко стає досить складною темою.
Багато пакетів, що сильно це спрощують, змушені йти на численні компроміси з моделлю даних, базою даних і доступними можливостями. Дехто з цих пакетів, які надто все спрощують, насправді мають приховані вади безпеки.
---
**FastAPI** не йде на жодні компроміси з будь-якою базою даних, моделлю даних чи інструментом.
Він дає вам усю гнучкість, щоб обрати ті, які найкраще підходять вашому проєкту.
І ви можете напряму використовувати добре підтримувані та широко застосовувані пакети на кшталт `pwdlib` і `PyJWT`, адже **FastAPI** не вимагає жодних складних механізмів для інтеграції зовнішніх пакетів.
Водночас він надає інструменти, щоб максимально спростити процес без компромісів у гнучкості, надійності чи безпеці.
І ви можете використовувати та впроваджувати безпечні стандартні протоколи, як-от OAuth2, відносно простим способом.
У **просунутому посібнику користувача** ви можете дізнатися більше про те, як використовувати «scopes» в OAuth2 для більш детальної системи дозволів, дотримуючись тих самих стандартів. OAuth2 зі scopes - це механізм, який використовують багато великих провайдерів автентифікації, як-от Facebook, Google, GitHub, Microsoft, X (Twitter) тощо, щоб авторизувати сторонні застосунки на взаємодію з їхніми API від імені користувачів.
================================================
FILE: docs/uk/docs/tutorial/security/simple-oauth2.md
================================================
# Простий OAuth2 з паролем і Bearer { #simple-oauth2-with-password-and-bearer }
Тепер продовжимо з попереднього розділу і додамо відсутні частини, щоб отримати повний потік безпеки.
## Отримайте `username` і `password` { #get-the-username-and-password }
Ми використаємо утиліти безпеки **FastAPI**, щоб отримати `username` і `password`.
OAuth2 визначає, що під час використання «потоку паролю» (який ми використовуємо) клієнт/користувач має надіслати поля `username` і `password` як дані форми.
І специфікація каже, що поля мають називатися саме так. Тому `user-name` або `email` не підійдуть.
Але не хвилюйтеся, у фронтенді ви можете відображати це так, як забажаєте, для кінцевих користувачів.
І ваші моделі бази даних можуть використовувати будь-які інші назви.
Але для *операції шляху* входу ми маємо використовувати саме ці назви, щоб бути сумісними зі специфікацією (і мати змогу, наприклад, користуватися вбудованою системою документації API).
Специфікація також вказує, що `username` і `password` мають надсилатися як дані форми (тобто без JSON).
### `scope` { #scope }
Специфікація також каже, що клієнт може надіслати інше поле форми «`scope`».
Назва поля форми — `scope` (в однині), але насправді це довга строка зі «scopes», розділеними пробілами.
Кожен «scope» — це просто строка (без пробілів).
Їх зазвичай використовують для оголошення конкретних прав доступу, наприклад:
- `users:read` або `users:write` — поширені приклади.
- `instagram_basic` використовується Facebook / Instagram.
- `https://www.googleapis.com/auth/drive` використовується Google.
/// info | Інформація
У OAuth2 «scope» — це просто строка, що оголошує конкретний потрібний дозвіл.
Не має значення, чи містить вона інші символи на кшталт `:` або чи це URL.
Ці деталі залежать від реалізації.
Для OAuth2 це просто строки.
///
## Код для отримання `username` і `password` { #code-to-get-the-username-and-password }
Тепер скористаймося утилітами, наданими **FastAPI**, щоб обробити це.
### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform }
Спочатку імпортуйте `OAuth2PasswordRequestForm` і використайте його як залежність із `Depends` в *операції шляху* для `/token`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
`OAuth2PasswordRequestForm` — це клас залежності, що оголошує тіло форми з:
- `username`.
- `password`.
- Необов'язковим полем `scope` як великою строкою, складеною зі строк, розділених пробілами.
- Необов'язковим `grant_type`.
/// tip | Порада
Специфікація OAuth2 насправді вимагає поле `grant_type` із фіксованим значенням `password`, але `OAuth2PasswordRequestForm` цього не примушує.
Якщо вам потрібно це примусити, використовуйте `OAuth2PasswordRequestFormStrict` замість `OAuth2PasswordRequestForm`.
///
- Необов'язковим `client_id` (для нашого прикладу не потрібно).
- Необов'язковим `client_secret` (для нашого прикладу не потрібно).
/// info | Інформація
`OAuth2PasswordRequestForm` — не спеціальний клас для **FastAPI**, як `OAuth2PasswordBearer`.
`OAuth2PasswordBearer` робить так, що **FastAPI** знає, що це схема безпеки. Тому її додають таким чином до OpenAPI.
Але `OAuth2PasswordRequestForm` — це просто клас залежності, який ви могли б написати самі, або ви могли б напряму оголосити параметри `Form`.
Оскільки це типова задача, **FastAPI** надає його безпосередньо, щоб спростити роботу.
///
### Використовуйте дані форми { #use-the-form-data }
/// tip | Порада
Екземпляр класу залежності `OAuth2PasswordRequestForm` не матиме атрибута `scope` з довгою строкою, розділеною пробілами, натомість він матиме атрибут `scopes` зі справжнім списком строк для кожного надісланого scope.
У цьому прикладі ми не використовуємо `scopes`, але ця можливість є, якщо знадобиться.
///
Тепер отримайте дані користувача з (фальшивої) бази даних, використовуючи `username` з поля форми.
Якщо такого користувача немає, повертаємо помилку «Incorrect username or password».
Для помилки використовуємо виняток `HTTPException`:
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
### Перевірте пароль { #check-the-password }
На цьому етапі ми маємо дані користувача з нашої бази даних, але ще не перевірили пароль.
Спершу розмістимо ці дані в Pydantic-моделі `UserInDB`.
Ви ніколи не повинні зберігати паролі у відкритому вигляді, тож ми використаємо (фальшиву) систему хешування паролів.
Якщо паролі не збігаються, повертаємо ту саму помилку.
#### Хешування паролів { #password-hashing }
«Хешування» означає: перетворення деякого вмісту (у цьому випадку пароля) у послідовність байтів (просто строку), яка виглядає як нісенітниця.
Кожного разу, коли ви передаєте точно той самий вміст (точно той самий пароль), ви отримуєте точно ту саму «нісенітницю».
Але ви не можете перетворити цю «нісенітницю» назад у пароль.
##### Навіщо використовувати хешування паролів { #why-use-password-hashing }
Якщо вашу базу даних вкрадуть, зловмисник не матиме паролів користувачів у відкритому вигляді, лише хеші.
Тож зловмисник не зможе спробувати використати ті самі паролі в іншій системі (оскільки багато користувачів використовують той самий пароль всюди, це було б небезпечно).
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
#### Про `**user_dict` { #about-user-dict }
`UserInDB(**user_dict)` означає:
Передати ключі та значення з `user_dict` безпосередньо як аргументи ключ-значення, еквівалентно до:
```Python
UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)
```
/// info | Інформація
Для повнішого пояснення `**user_dict` перегляньте [документацію для **Додаткових моделей**](../extra-models.md#about-user-in-dict).
///
## Поверніть токен { #return-the-token }
Відповідь точки входу `token` має бути об'єктом JSON.
Вона повинна містити `token_type`. У нашому випадку, оскільки ми використовуємо токени «Bearer», тип токена має бути «`bearer`».
Також має бути `access_token` зі строкою, що містить наш токен доступу.
Для цього простого прикладу ми зробимо повністю небезпечно і повернемо той самий `username` як токен.
/// tip | Порада
У наступному розділі ви побачите справді безпечну реалізацію з хешуванням паролів і токенами JWT.
А поки зосередьмося на конкретних деталях, які нам потрібні.
///
{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}
/// tip | Порада
Згідно зі специфікацією, ви маєте повертати JSON з `access_token` і `token_type`, як у цьому прикладі.
Це те, що ви маєте зробити самостійно у своєму коді, і переконатися, що використовуєте саме ці ключі JSON.
Це майже єдине, що ви маєте зробити правильно самі, щоб відповідати специфікації.
Решту **FastAPI** зробить за вас.
///
## Оновіть залежності { #update-the-dependencies }
Тепер оновимо наші залежності.
Ми хочемо отримати `current_user` лише якщо цей користувач активний.
Тому створимо додаткову залежність `get_current_active_user`, яка своєю чергою використовує як залежність `get_current_user`.
Обидві ці залежності просто повернуть HTTP-помилку, якщо користувача не існує або він неактивний.
Отже, у нашій кінцевій точці ми отримаємо користувача лише якщо він існує, був правильно автентифікований і є активним:
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
/// info | Інформація
Додатковий заголовок `WWW-Authenticate` зі значенням `Bearer`, який ми тут повертаємо, також є частиною специфікації.
Будь-який HTTP (помилка) зі статус-кодом 401 «UNAUTHORIZED» також має повертати заголовок `WWW-Authenticate`.
У випадку токенів носія (наш випадок) значенням цього заголовка має бути `Bearer`.
Фактично ви можете пропустити цей додатковий заголовок, і все одно працюватиме.
Але він наданий тут, щоб відповідати специфікаціям.
Також можуть існувати інструменти, які очікують і використовують його (зараз або в майбутньому), і це може бути корисно вам або вашим користувачам, зараз або в майбутньому.
У цьому користь стандартів...
///
## Подивіться в дії { #see-it-in-action }
Відкрийте інтерактивну документацію: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
### Автентифікація { #authenticate }
Натисніть кнопку «Authorize».
Використайте облікові дані:
Користувач: `johndoe`
Пароль: `secret`
Після автентифікації в системі ви побачите таке:
### Отримайте власні дані користувача { #get-your-own-user-data }
Тепер використайте операцію `GET` зі шляхом `/users/me`.
Ви отримаєте дані свого користувача, наприклад:
```JSON
{
"username": "johndoe",
"email": "johndoe@example.com",
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}
```
Якщо натиснути значок замка й вийти з системи, а потім знову спробувати ту саму операцію, ви отримаєте помилку HTTP 401 такого вигляду:
```JSON
{
"detail": "Not authenticated"
}
```
### Неактивний користувач { #inactive-user }
Тепер спробуйте з неактивним користувачем, автентифікуйтеся з:
Користувач: `alice`
Пароль: `secret2`
І спробуйте використати операцію `GET` зі шляхом `/users/me`.
Ви отримаєте помилку «Inactive user», наприклад:
```JSON
{
"detail": "Inactive user"
}
```
## Підсумок { #recap }
Тепер у вас є інструменти для реалізації повної системи безпеки на основі `username` і `password` для вашого API.
Використовуючи ці інструменти, ви можете зробити систему безпеки сумісною з будь-якою базою даних і з будь-якою моделлю користувача чи даних.
Єдина відсутня деталь у тому, що наразі це ще не «безпечна» реалізація.
У наступному розділі ви побачите, як використовувати безпечну бібліотеку для хешування паролів і токени JWT.
================================================
FILE: docs/uk/docs/tutorial/server-sent-events.md
================================================
# Події, надіслані сервером (SSE) { #server-sent-events-sse }
Ви можете транслювати дані клієнту за допомогою **Server-Sent Events** (SSE).
Це подібно до [Потік JSON Lines](stream-json-lines.md), але використовує формат `text/event-stream`, який нативно підтримується браузерами через [API `EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource).
/// info | Інформація
Додано у FastAPI 0.135.0.
///
## Що таке Server-Sent Events { #what-are-server-sent-events }
SSE - це стандарт для трансляції даних із сервера до клієнта по HTTP.
Кожна подія - це невеликий текстовий блок із «полями» на кшталт `data`, `event`, `id` та `retry`, розділений порожніми рядками.
Виглядає так:
```
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
```
SSE часто використовують для стрімінгу чатів ШІ, живих сповіщень, логів і спостережуваності, а також інших випадків, коли сервер надсилає оновлення клієнту.
/// tip | Порада
Якщо ви хочете транслювати бінарні дані, наприклад відео чи аудіо, перегляньте просунутий посібник: [Потік даних](../advanced/stream-data.md).
///
## Стрімінг SSE у FastAPI { #stream-sse-with-fastapi }
Щоб транслювати SSE з FastAPI, використовуйте `yield` у вашій *функції операції шляху* і встановіть `response_class=EventSourceResponse`.
Імпортуйте `EventSourceResponse` з `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Кожен елемент, повернений через `yield`, кодується як JSON і надсилається в полі `data:` події SSE.
Якщо ви оголосите тип повернення як `AsyncIterable[Item]`, FastAPI використає його, щоб **перевіряти**, **документувати** і **серіалізувати** дані за допомогою Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Порада
Оскільки Pydantic серіалізує це на боці **Rust**, ви отримаєте значно вищу **продуктивність**, ніж якби не оголошували тип повернення.
///
### Не-async *функції операцій шляху* { #non-async-path-operation-functions }
Ви також можете використовувати звичайні функції `def` (без `async`) і використовувати `yield` так само.
FastAPI подбає про коректне виконання, щоб воно не блокувало цикл подій.
Оскільки в цьому випадку функція не async, коректним типом повернення буде `Iterable[Item]`:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
### Без типу повернення { #no-return-type }
Можна також опустити тип повернення. FastAPI використає [`jsonable_encoder`](./encoder.md), щоб конвертувати дані і надіслати їх.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
## `ServerSentEvent` { #serversentevent }
Якщо вам потрібно встановити поля SSE, такі як `event`, `id`, `retry` або `comment`, ви можете повертати через `yield` об'єкти `ServerSentEvent` замість звичайних даних.
Імпортуйте `ServerSentEvent` з `fastapi.sse`:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
Поле `data` завжди кодується як JSON. Ви можете передати будь-яке значення, яке можна серіалізувати в JSON, включно з моделями Pydantic.
## Сирі дані { #raw-data }
Якщо потрібно надіслати дані **без** кодування в JSON, використовуйте `raw_data` замість `data`.
Це корисно для надсилання попередньо відформатованого тексту, рядків логів або спеціальних значень «значення-сторож», як-от `[DONE]`.
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Примітка
`data` і `raw_data` взаємовиключні. У кожному `ServerSentEvent` ви можете встановити лише одне з них.
///
## Відновлення з `Last-Event-ID` { #resuming-with-last-event-id }
Коли браузер перепідключається після розриву з'єднання, він надсилає останній отриманий `id` у заголовку `Last-Event-ID`.
Ви можете прочитати його як параметр заголовка і використати, щоб відновити потік із місця, де клієнт зупинився:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
## SSE з POST { #sse-with-post }
SSE працює з **будь-яким HTTP-методом**, не лише з `GET`.
Це корисно для протоколів на кшталт [MCP](https://modelcontextprotocol.io), які транслюють SSE через `POST`:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
## Технічні деталі { #technical-details }
FastAPI реалізує деякі найкращі практики SSE «з коробки».
- Надсилати **коментар «keep alive» `ping`** кожні 15 секунд, коли не було жодного повідомлення, щоб запобігти закриттю з'єднання деякими проксі, як рекомендовано у [Специфікації HTML: Події, надіслані сервером](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes).
- Встановити заголовок `Cache-Control: no-cache`, щоб **запобігти кешуванню** потоку.
- Встановити спеціальний заголовок `X-Accel-Buffering: no`, щоб **запобігти буферизації** у деяких проксі, наприклад Nginx.
Вам не потрібно нічого з цим робити, воно працює «з коробки». 🤓
================================================
FILE: docs/uk/docs/tutorial/sql-databases.md
================================================
# SQL (реляційні) бази даних { #sql-relational-databases }
**FastAPI** не вимагає від вас використовувати SQL (реляційну) базу даних. Але ви можете скористатися будь-якою базою даних, яку забажаєте.
Тут ми розглянемо приклад з [SQLModel](https://sqlmodel.tiangolo.com/).
**SQLModel** побудовано поверх [SQLAlchemy](https://www.sqlalchemy.org/) та Pydantic. Її створив той самий автор, що і **FastAPI**, як ідеальну пару для застосунків FastAPI, яким потрібні **SQL бази даних**.
/// tip | Порада
Ви можете використовувати будь-яку іншу бібліотеку для SQL або NoSQL баз (інколи їх називають "ORMs"), FastAPI нічого не нав’язує. 😎
///
Оскільки SQLModel базується на SQLAlchemy, ви можете легко використовувати будь-яку базу даних, яку підтримує SQLAlchemy (а отже й SQLModel), наприклад:
* PostgreSQL
* MySQL
* SQLite
* Oracle
* Microsoft SQL Server тощо
У цьому прикладі ми використаємо **SQLite**, оскільки вона зберігається в одному файлі, а Python має вбудовану підтримку. Тож ви можете скопіювати цей приклад і запустити «як є».
Пізніше, для вашого продакшн-застосунку, ви можете перейти на сервер бази даних, наприклад **PostgreSQL**.
/// tip | Порада
Існує офіційний генератор проєкту з **FastAPI** та **PostgreSQL**, включно з фронтендом та іншими інструментами: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template)
///
Це дуже простий і короткий навчальний посібник. Якщо ви хочете вивчити бази даних загалом, SQL або більш просунуті можливості, зверніться до [документації SQLModel](https://sqlmodel.tiangolo.com/).
## Встановіть `SQLModel` { #install-sqlmodel }
Спочатку переконайтеся, що ви створили [віртуальне оточення](../virtual-environments.md), активували його та встановили `sqlmodel`:
```console
$ pip install sqlmodel
---> 100%
```
## Створіть застосунок з однією моделлю { #create-the-app-with-a-single-model }
Спершу створимо найпростішу версію застосунку з однією моделлю **SQLModel**.
Потім нижче покращимо безпеку і гнучкість за допомогою кількох моделей. 🤓
### Створіть моделі { #create-models }
Імпортуйте `SQLModel` і створіть модель бази даних:
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *}
Клас `Hero` дуже схожий на модель Pydantic (фактично, під капотом це і є модель Pydantic).
Є кілька відмінностей:
* `table=True` каже SQLModel, що це «таблична модель» - вона має представляти **таблицю** в SQL базі даних, а не просто «модель даних» (як будь-який інший звичайний клас Pydantic).
* `Field(primary_key=True)` каже SQLModel, що `id` - це **первинний ключ** у SQL базі даних (більше про первинні ключі в SQL див. у документації SQLModel).
Примітка: Ми використовуємо `int | None` для поля первинного ключа, щоб у Python-коді можна було створити об’єкт без `id` (`id=None`), припускаючи, що база даних згенерує його під час збереження. SQLModel розуміє, що `id` надасть база даних, і визначає стовпець як ненульовий `INTEGER` у схемі бази даних. Докладніше див. [документацію SQLModel про первинні ключі](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id).
* `Field(index=True)` каже SQLModel створити **SQL-індекс** для цього стовпця, що дозволить швидше виконувати пошук у базі даних під час читання даних, відфільтрованих за цим стовпцем.
SQLModel знатиме, що оголошене як `str` стане SQL-стовпцем типу `TEXT` (або `VARCHAR`, залежно від бази).
### Створіть рушій { #create-an-engine }
`engine` SQLModel (під капотом це `engine` SQLAlchemy) - це те, що **утримує з’єднання** з базою даних.
У вас має бути **єдиний об’єкт `engine`** для всього коду, що під’єднується до тієї самої бази.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *}
Використання `check_same_thread=False` дозволяє FastAPI використовувати ту саму базу SQLite у різних потоках. Це необхідно, адже **один запит** може використати **понад один потік** (наприклад, у залежностях).
Не переймайтеся, завдяки структурі коду ми далі забезпечимо **одну сесію SQLModel на запит**, власне цього і прагне `check_same_thread`.
### Створіть таблиці { #create-the-tables }
Додамо функцію, яка використовує `SQLModel.metadata.create_all(engine)`, щоб **створити таблиці** для всіх «табличних моделей».
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *}
### Створіть залежність сесії { #create-a-session-dependency }
**`Session`** зберігає **об’єкти в пам’яті** та відстежує зміни у даних, а потім **використовує `engine`** для взаємодії з базою даних.
Ми створимо **залежність** FastAPI з `yield`, яка надаватиме нову `Session` для кожного запиту. Це й гарантує, що ми використовуємо одну сесію на запит. 🤓
Далі створимо залежність `Annotated` - `SessionDep`, щоб спростити решту коду, який її використовуватиме.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *}
### Створюйте таблиці під час запуску { #create-database-tables-on-startup }
Ми створимо таблиці бази під час запуску застосунку.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *}
Тут ми створюємо таблиці на події запуску застосунку.
У продакшні ви, ймовірно, використаєте міграційний скрипт, який виконується перед стартом застосунку. 🤓
/// tip | Порада
SQLModel матиме утиліти міграцій-обгортки над Alembic, але поки що ви можете використовувати [Alembic](https://alembic.sqlalchemy.org/en/latest/) напряму.
///
### Створіть героя { #create-a-hero }
Оскільки кожна модель SQLModel також є моделлю Pydantic, ви можете використовувати її в тих самих **анотаціях типів**, що і моделі Pydantic.
Наприклад, якщо ви оголосите параметр типу `Hero`, він буде прочитаний з **JSON-тіла**.
Так само ви можете оголосити її як **тип, що повертається** функції, і форма даних з’явиться в автоматичному UI документації API.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *}
Тут ми використовуємо залежність `SessionDep` (`Session`), щоб додати нового `Hero` до екземпляра `Session`, закомітити зміни до бази, оновити дані в `hero`, а потім повернути його.
### Читання героїв { #read-heroes }
Ми можемо **читати** `Hero` з бази даних, використовуючи `select()`. Ми можемо додати `limit` і `offset` для пагінації результатів.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *}
### Читання одного героя { #read-one-hero }
Ми можемо **прочитати** одного `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *}
### Видалення героя { #delete-a-hero }
Ми також можемо **видалити** `Hero`.
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *}
### Запуск застосунку { #run-the-app }
Ви можете запустити застосунок:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Потім перейдіть до інтерфейсу `/docs`, ви побачите, що **FastAPI** використовує ці **моделі** для **документування** API, а також для **серіалізації** та **валідації** даних.
## Оновіть застосунок кількома моделями { #update-the-app-with-multiple-models }
Тепер трохи **відрефакторимо** застосунок, щоб підвищити **безпеку** та **гнучкість**.
Якщо подивитися на попередній варіант, у UI видно, що наразі клієнт може сам обирати `id` для створюваного `Hero`. 😱
Цього допускати не можна - вони можуть перезаписати `id`, який уже призначений у БД. Визначення `id` має виконувати **бекенд** або **база даних**, **а не клієнт**.
Крім того, ми створюємо для героя `secret_name`, але наразі повертаємо його всюди - це не дуже «секретно»... 😅
Виправимо це, додавши кілька **додаткових моделей**. Ось де SQLModel засяє. ✨
### Створіть кілька моделей { #create-multiple-models }
У **SQLModel** будь-який клас моделі з `table=True` - це **таблична модель**.
А будь-який клас моделі без `table=True` - це **модель даних**; це, по суті, просто моделі Pydantic (з парою невеликих доповнень). 🤓
З SQLModel ми можемо використовувати **успадкування**, щоб **уникати дублювання** полів у всіх випадках.
#### `HeroBase` - базовий клас { #herobase-the-base-class }
Почнімо з моделі `HeroBase`, яка містить усі **спільні поля** для всіх моделей:
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *}
#### `Hero` - «таблична модель» { #hero-the-table-model }
Потім створімо `Hero`, фактичну «табличну модель», з **додатковими полями**, яких немає в інших моделях завжди:
* `id`
* `secret_name`
Оскільки `Hero` успадковується від `HeroBase`, вона **також** містить **поля**, оголошені в `HeroBase`, тож усі поля `Hero` такі:
* `id`
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *}
#### `HeroPublic` - публічна «модель даних» { #heropublic-the-public-data-model }
Далі створимо модель `HeroPublic` - це та, що буде **повертатися** клієнтам API.
Вона має ті самі поля, що й `HeroBase`, отже не включатиме `secret_name`.
Нарешті, особистості наших героїв захищено! 🥷
Вона також перевизначає `id: int`. Роблячи це, ми укладаємо **контракт** з клієнтами API, що `id` завжди буде присутній і буде типу `int` (ніколи не `None`).
/// tip | Порада
Наявність у моделі відповіді гарантії, що значення завжди доступне і завжди `int` (не `None`), дуже корисна для клієнтів API - вони можуть писати значно простіший код, маючи цю визначеність.
Також **автоматично згенеровані клієнти** матимуть простіші інтерфейси, тож розробникам, які взаємодіють з вашим API, буде значно зручніше. 😎
///
Усі поля в `HeroPublic` такі самі, як у `HeroBase`, з `id`, оголошеним як `int` (не `None`):
* `id`
* `name`
* `age`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *}
#### `HeroCreate` - «модель даних» для створення героя { #herocreate-the-data-model-to-create-a-hero }
Тепер створимо модель `HeroCreate` - вона **валідуватиме** дані, отримані від клієнтів.
Вона має ті ж поля, що й `HeroBase`, а також `secret_name`.
Тепер, коли клієнти **створюють нового героя**, вони надсилають `secret_name`, він зберігається в базі даних, але ці секретні імена не повертаються клієнтам у API.
/// tip | Порада
Так слід поводитися з **паролями**. Приймайте їх, але не повертайте в API.
Також потрібно **хешувати** значення паролів перед збереженням, **ніколи не зберігайте їх у відкритому вигляді**.
///
Поля `HeroCreate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *}
#### `HeroUpdate` - «модель даних» для оновлення героя { #heroupdate-the-data-model-to-update-a-hero }
У попередній версії у нас не було способу **оновлювати героя**, але тепер, маючи **кілька моделей**, ми можемо це зробити. 🎉
«Модель даних» `HeroUpdate` дещо особлива: вона має **всі ті самі поля**, що й для створення нового героя, але всі поля **необов’язкові** (усі мають значення за замовчуванням). Таким чином, під час оновлення героя ви можете надіслати лише ті поля, які хочете змінити.
Оскільки **поля фактично змінюються** (тип тепер включає `None` і значення за замовчуванням - `None`), нам потрібно **оголосити їх заново**.
Насправді немає потреби успадковуватися від `HeroBase`, бо ми перевизначаємо всі поля. Я залишу успадкування для послідовності, але це не обов’язково. Це радше питання смаку. 🤷
Поля `HeroUpdate`:
* `name`
* `age`
* `secret_name`
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *}
### Створення з `HeroCreate` і повернення `HeroPublic` { #create-with-herocreate-and-return-a-heropublic }
Тепер, коли в нас **кілька моделей**, ми можемо оновити частини застосунку, що їх використовують.
У запиті ми отримуємо «модель даних» `HeroCreate`, і з неї створюємо «табличну модель» `Hero`.
Ця нова «таблична модель» `Hero` міститиме поля, надіслані клієнтом, а також матиме `id`, згенерований базою даних.
Далі ми повертаємо з функції цю ж «табличну модель» `Hero`. Але оскільки ми оголошуємо `response_model` як «модель даних» `HeroPublic`, **FastAPI** використає `HeroPublic` для валідації та серіалізації даних.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *}
/// tip | Порада
Тепер ми використовуємо `response_model=HeroPublic` замість **анотації типу, що повертається** `-> HeroPublic`, тому що значення, яке ми повертаємо, насправді - не `HeroPublic`.
Якби ми оголосили `-> HeroPublic`, ваш редактор і лінтер справедливо зауважили б, що ви повертаєте `Hero`, а не `HeroPublic`.
Оголошуючи це в `response_model`, ми кажемо **FastAPI** зробити свою справу, не втручаючись в анотації типів та підказки від вашого редактора й інших інструментів.
///
### Читання героїв з `HeroPublic` { #read-heroes-with-heropublic }
Можемо зробити те саме, що й раніше, щоб **читати** `Hero`, знову використовуємо `response_model=list[HeroPublic]`, щоб гарантувати коректну валідацію та серіалізацію даних.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *}
### Читання одного героя з `HeroPublic` { #read-one-hero-with-heropublic }
Ми можемо **прочитати** одного героя:
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *}
### Оновлення героя з `HeroUpdate` { #update-a-hero-with-heroupdate }
Ми можемо **оновити героя**. Для цього використовуємо HTTP-операцію `PATCH`.
У коді ми отримуємо `dict` з усіма даними, які надіслав клієнт, - лише з тими даними, які надіслав клієнт, виключаючи будь-які значення, присутні лише як значення за замовчуванням. Для цього використовуємо `exclude_unset=True`. Це головний трюк. 🪄
Потім використовуємо `hero_db.sqlmodel_update(hero_data)`, щоб оновити `hero_db` даними з `hero_data`.
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *}
### Знову видалення героя { #delete-a-hero-again }
**Видалення** героя майже не змінилося.
Ми не задовольнимо бажання відрефакторити все в цьому місці. 😅
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *}
### Знову запустіть застосунок { #run-the-app-again }
Ви можете знову запустити застосунок:
```console
$ fastapi dev
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Якщо ви перейдете до UI `/docs`, побачите, що він оновився і більше не очікуватиме отримати `id` від клієнта під час створення героя тощо.
## Підсумок { #recap }
Ви можете використовувати [**SQLModel**](https://sqlmodel.tiangolo.com/) для взаємодії з SQL базою даних і спростити код за допомогою «моделей даних» та «табличних моделей».
Багато чого ще можна дізнатися в документації **SQLModel**, там є розширений міні-[навчальний посібник з використання SQLModel з **FastAPI**](https://sqlmodel.tiangolo.com/tutorial/fastapi/). 🚀
================================================
FILE: docs/uk/docs/tutorial/static-files.md
================================================
# Статичні файли { #static-files }
Ви можете автоматично надавати статичні файли з каталогу, використовуючи `StaticFiles`.
## Використання `StaticFiles` { #use-staticfiles }
* Імпортуйте `StaticFiles`.
* «Під'єднати» екземпляр `StaticFiles()` з вказанням необхідного шляху.
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
/// note | Технічні деталі
Ви також можете використовувати `from starlette.staticfiles import StaticFiles`.
**FastAPI** надає той самий `starlette.staticfiles`, що й `fastapi.staticfiles` для зручності розробників. Але фактично він безпосередньо походить із Starlette.
///
### Що таке «Під'єднання» { #what-is-mounting }
«Під'єднання» означає додавання повноцінного «незалежного» застосунку за певним шляхом, який потім обробляє всі під шляхи.
Це відрізняється від використання `APIRouter`, оскільки під'єднаний застосунок є повністю незалежним. OpenAPI та документація вашого основного застосунку не будуть знати нічого про ваш під'єднаний застосунок тощо.
Ви можете дізнатися більше про це в [Посібнику для просунутих користувачів](../advanced/index.md).
## Деталі { #details }
Перше `"/static"` вказує на під шлях, за яким буде «під'єднано» цей новий «підзастосунок». Тому будь-який шлях, який починається з `"/static"`, буде оброблятися ним.
`directory="static"` визначає назву каталогу, що містить ваші статичні файли.
`name="static"` це ім'я, яке можна використовувати всередині **FastAPI**.
Усі ці параметри можуть бути іншими за "`static`", налаштуйте їх відповідно до потреб і особливостей вашого застосунку.
## Додаткова інформація { #more-info }
Детальніше про налаштування та можливості можна дізнатися в [документації Starlette про статичні файли](https://www.starlette.dev/staticfiles/).
================================================
FILE: docs/uk/docs/tutorial/stream-json-lines.md
================================================
# Стрімінг JSON Lines { #stream-json-lines }
У вас може бути послідовність даних, яку ви хочете надсилати у **«потоці»**, це можна зробити за допомогою **JSON Lines**.
/// info | Інформація
Додано в FastAPI 0.134.0.
///
## Що таке потік { #what-is-a-stream }
«**Стрімінг**» даних означає, що ваш застосунок почне надсилати елементи даних клієнту, не чекаючи, доки буде готова вся послідовність елементів.
Тобто він надішле перший елемент, клієнт його отримає і почне обробляти, а ви в цей час уже можете створювати наступний елемент.
```mermaid
sequenceDiagram
participant App
participant Client
App->>App: Produce Item 1
App->>Client: Send Item 1
App->>App: Produce Item 2
Client->>Client: Process Item 1
App->>Client: Send Item 2
App->>App: Produce Item 3
Client->>Client: Process Item 2
App->>Client: Send Item 3
Client->>Client: Process Item 3
Note over App: Keeps producing...
Note over Client: Keeps consuming...
```
Це може бути навіть нескінченний потік, у якому ви постійно надсилаєте дані.
## JSON Lines { #json-lines }
У таких випадках часто надсилають «**JSON Lines**» - формат, у якому ви надсилаєте по одному об’єкту JSON на рядок.
Відповідь матиме тип вмісту `application/jsonl` (замість `application/json`), а тіло буде приблизно таким:
```json
{"name": "Plumbus", "description": "A multi-purpose household device."}
{"name": "Portal Gun", "description": "A portal opening device."}
{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."}
```
Це дуже схоже на масив JSON (еквівалент списку Python), але замість того, щоб бути загорнутим у `[]` і мати `,` між елементами, тут є **по одному об’єкту JSON на рядок**, вони розділені символом нового рядка.
/// info | Інформація
Важливо те, що ваш застосунок зможе по черзі створювати кожен рядок, поки клієнт споживає попередні рядки.
///
/// note | Технічні деталі
Оскільки кожен об’єкт JSON буде розділено новим рядком, він не може містити буквальні символи нового рядка у своєму вмісті, але може містити екрановані нові рядки (`\n`), що є частиною стандарту JSON.
Зазвичай про це не треба турбуватися, усе робиться автоматично, читайте далі. 🤓
///
## Випадки використання { #use-cases }
Ви можете використовувати це, щоб стрімити дані зі служби **AI LLM**, із **логів** чи **телеметрії**, або з інших типів даних, які можна структурувати як елементи **JSON**.
/// tip | Порада
Якщо ви хочете стрімити бінарні дані, наприклад відео чи аудіо, перегляньте просунутий посібник: [Потокова передача даних](../advanced/stream-data.md).
///
## Стрімінг JSON Lines з FastAPI { #stream-json-lines-with-fastapi }
Щоб стрімити JSON Lines з FastAPI, замість використання `return` у вашій *функції операції шляху* використовуйте `yield`, щоб по черзі створювати кожен елемент.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *}
Якщо кожен елемент JSON, який ви хочете надіслати у відповідь, має тип `Item` (модель Pydantic) і це async-функція, ви можете оголосити тип повернення як `AsyncIterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *}
Якщо ви оголосите тип повернення, FastAPI використає його, щоб **перевіряти** дані, **документувати** їх в OpenAPI, **фільтрувати** їх і **серіалізувати** за допомогою Pydantic.
/// tip | Порада
Оскільки Pydantic серіалізуватиме це на боці **Rust**, ви отримаєте значно вищу **продуктивність**, ніж якби не оголошували тип повернення.
///
### Не-async *функції операцій шляху* { #non-async-path-operation-functions }
Ви також можете використовувати звичайні функції `def` (без `async`) і використовувати `yield` так само.
FastAPI подбає про коректне виконання, щоб це не блокувало цикл подій.
Оскільки в цьому випадку функція не є async, правильним типом повернення буде `Iterable[Item]`:
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *}
### Без типу повернення { #no-return-type }
Ви також можете опустити тип повернення. Тоді FastAPI використає [`jsonable_encoder`](./encoder.md), щоб перетворити дані на щось, що можна серіалізувати в JSON, і потім надішле їх як JSON Lines.
{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *}
## Події, надіслані сервером (SSE) { #server-sent-events-sse }
FastAPI також має повноцінну підтримку Server-Sent Events (SSE), які досить схожі, але мають кілька додаткових деталей. Ви можете дізнатися про них у наступному розділі: [Події, надіслані сервером (SSE)](server-sent-events.md). 🤓
================================================
FILE: docs/uk/docs/tutorial/testing.md
================================================
# Тестування { #testing }
Завдяки [Starlette](https://www.starlette.dev/testclient/), тестувати застосунки **FastAPI** просто й приємно.
Воно базується на [HTTPX](https://www.python-httpx.org), який, своєю чергою, спроєктований на основі Requests, тож він дуже знайомий та інтуїтивно зрозумілий.
З його допомогою ви можете використовувати [pytest](https://docs.pytest.org/) безпосередньо з **FastAPI**.
## Використання `TestClient` { #using-testclient }
/// info | Інформація
Щоб використовувати `TestClient`, спочатку встановіть [`httpx`](https://www.python-httpx.org).
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його, а потім встановили `httpx`, наприклад:
```console
$ pip install httpx
```
///
Імпортуйте `TestClient`.
Створіть `TestClient`, передавши йому ваш застосунок **FastAPI**.
Створюйте функції з іменами, що починаються з `test_` (це стандартна угода для `pytest`).
Використовуйте об'єкт `TestClient` так само як і `httpx`.
Записуйте прості `assert`-вирази зі стандартними виразами Python, які потрібно перевірити (це також стандарт для `pytest`).
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
/// tip | Порада
Зверніть увагу, що тестові функції — це звичайні `def`, а не `async def`.
Виклики клієнта також звичайні, без використання `await`.
Це дозволяє використовувати `pytest` без зайвих ускладнень.
///
/// note | Технічні деталі
Ви також можете використовувати `from starlette.testclient import TestClient`.
**FastAPI** надає той самий `starlette.testclient` під назвою `fastapi.testclient` просто для зручності для вас, розробника. Але він безпосередньо походить із Starlette.
///
/// tip | Порада
Якщо ви хочете викликати `async`-функції у ваших тестах, окрім відправлення запитів до вашого застосунку FastAPI (наприклад, асинхронні функції роботи з базою даних), перегляньте [Async Tests](../advanced/async-tests.md) у розширеному керівництві.
///
## Розділення тестів { #separating-tests }
У реальному застосунку ваші тести, ймовірно, будуть в окремому файлі.
Також ваш застосунок **FastAPI** може складатися з кількох файлів/модулів тощо.
### Файл застосунку **FastAPI** { #fastapi-app-file }
Припустимо, у вас є структура файлів, описана в розділі [Bigger Applications](bigger-applications.md):
```
.
├── app
│ ├── __init__.py
│ └── main.py
```
У файлі `main.py` знаходиться ваш застосунок **FastAPI**:
{* ../../docs_src/app_testing/app_a_py310/main.py *}
### Файл тестування { #testing-file }
Ви можете створити файл `test_main.py` з вашими тестами. Він може знаходитися в тому ж пакеті Python (у тій самій директорії з файлом `__init__.py`):
``` hl_lines="5"
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Оскільки цей файл знаходиться в тому ж пакеті, ви можете використовувати відносний імпорт, щоб імпортувати об'єкт `app` із модуля `main` (`main.py`):
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
...і написати код для тестів так само як і раніше.
## Тестування: розширений приклад { #testing-extended-example }
Тепер розширимо цей приклад і додамо більше деталей, щоб побачити, як тестувати різні частини.
### Розширений файл застосунку **FastAPI** { #extended-fastapi-app-file }
Залишимо ту саму структуру файлів:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Припустимо, що тепер файл `main.py` із вашим застосунком **FastAPI** містить інші **операції шляху**.
Він має `GET`-операцію, яка може повертати помилку.
Він має `POST`-операцію, яка може повертати кілька помилок.
Обидві *операції шляху* вимагають заголовок `X-Token`.
{* ../../docs_src/app_testing/app_b_an_py310/main.py *}
### Розширений тестовий файл { #extended-testing-file }
Потім ви можете оновити `test_main.py`, додавши розширені тести:
{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *}
Коли вам потрібно передати клієнту інформацію в запиті, але ви не знаєте, як це зробити, ви можете пошукати (Google), як це зробити в `httpx`, або навіть як це зробити з `requests`, оскільки дизайн HTTPX базується на дизайні Requests.
Далі ви просто повторюєте ці ж дії у ваших тестах.
Наприклад:
* Щоб передати *path* або *query* параметр, додайте його безпосередньо до URL.
* Щоб передати тіло JSON, передайте Python-об'єкт (наприклад, `dict`) у параметр `json`.
* Якщо потрібно надіслати *Form Data* замість JSON, використовуйте параметр `data`.
* Щоб передати заголовки *headers*, використовуйте `dict` у параметрі `headers`.
* Для *cookies* використовуйте `dict` у параметрі `cookies`.
Докладніше про передачу даних у бекенд (за допомогою `httpx` або `TestClient`) можна знайти в [документації HTTPX](https://www.python-httpx.org).
/// info | Інформація
Зверніть увагу, що `TestClient` отримує дані, які можна конвертувати в JSON, а не Pydantic-моделі.
Якщо у вас є Pydantic-модель у тесті, і ви хочете передати її дані в застосунок під час тестування, ви можете використати `jsonable_encoder`, описаний у розділі [JSON Compatible Encoder](encoder.md).
///
## Запуск { #run-it }
Після цього вам потрібно встановити `pytest`.
Переконайтеся, що ви створили [віртуальне середовище](../virtual-environments.md), активували його і встановили необхідні пакети, наприклад:
```console
$ pip install pytest
---> 100%
```
Він автоматично знайде файли та тести, виконає їх і повідомить вам результати.
Запустіть тести за допомогою:
================================================
FILE: docs/uk/docs/virtual-environments.md
================================================
# Віртуальні середовища { #virtual-environments }
Коли ви працюєте над проєктами Python, вам, імовірно, слід використовувати віртуальне середовище (або схожий механізм), щоб ізолювати пакети, які ви встановлюєте для кожного проєкту.
/// info | Інформація
Якщо ви вже знаєте про віртуальні середовища, як їх створювати та використовувати, можете пропустити цей розділ. 🤓
///
/// tip | Порада
Віртуальне середовище відрізняється від змінної оточення.
Змінна оточення - це змінна в системі, яку можуть використовувати програми.
Віртуальне середовище - це каталог із файлами в ньому.
///
/// info | Інформація
На цій сторінці ви дізнаєтеся, як використовувати віртуальні середовища і як вони працюють.
Якщо ви готові прийняти інструмент, що керує всім за вас (включно з установленням Python), спробуйте [uv](https://github.com/astral-sh/uv).
///
## Створіть проєкт { #create-a-project }
Спочатку створіть каталог для вашого проєкту.
Зазвичай я створюю каталог з назвою `code` у моєму домашньому каталозі користувача.
І всередині нього я створюю окремий каталог на кожен проєкт.
```console
// Перейдіть до домашнього каталогу
$ cd
// Створіть каталог для всіх ваших проєктів з кодом
$ mkdir code
// Перейдіть у цей каталог code
$ cd code
// Створіть каталог для цього проєкту
$ mkdir awesome-project
// Перейдіть до каталогу цього проєкту
$ cd awesome-project
```
## Створіть віртуальне середовище { #create-a-virtual-environment }
Коли ви починаєте працювати над проєктом Python уперше, створіть віртуальне середовище у вашому проєкті **у вашому проєкті**.
/// tip | Порада
Це потрібно робити лише один раз на проєкт, не щоразу, коли ви працюєте.
///
//// tab | `venv`
Щоб створити віртуальне середовище, ви можете використати модуль `venv`, який постачається разом із Python.
```console
$ python -m venv .venv
```
/// details | Що означає ця команда
* `python`: використати програму з назвою `python`
* `-m`: викликати модуль як скрипт, далі ми вкажемо, який модуль
* `venv`: використати модуль з назвою `venv`, який зазвичай уже встановлено з Python
* `.venv`: створити віртуальне середовище в новому каталозі `.venv`
///
////
//// tab | `uv`
Якщо у вас встановлено [`uv`](https://github.com/astral-sh/uv), ви можете використати його для створення віртуального середовища.
```console
$ uv venv
```
/// tip | Порада
Типово `uv` створить віртуальне середовище в каталозі з назвою `.venv`.
Але ви можете налаштувати це, передавши додатковий аргумент з назвою каталогу.
///
////
Ця команда створює нове віртуальне середовище в каталозі з назвою `.venv`.
/// details | `.venv` або інша назва
Ви можете створити віртуальне середовище в іншому каталозі, але існує усталена домовленість називати його `.venv`.
///
## Активуйте віртуальне середовище { #activate-the-virtual-environment }
Активуйте нове віртуальне середовище, щоб будь-яка команда Python, яку ви запускаєте, або пакет, який ви встановлюєте, використовували його.
/// tip | Порада
Робіть це щоразу, коли ви починаєте нову сесію термінала для роботи над проєктом.
///
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Або якщо ви використовуєте Bash для Windows (напр., [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
/// tip | Порада
Кожного разу, коли ви встановлюєте новий пакет у це середовище, активуйте середовище знову.
Це гарантує, що якщо ви використовуєте програму термінала (CLI), встановлену цим пакетом, ви використовуєте саме ту з вашого віртуального середовища, а не будь-яку іншу, яка може бути встановлена глобально, імовірно з іншою версією, ніж вам потрібно.
///
## Перевірте активність віртуального середовища { #check-the-virtual-environment-is-active }
Перевірте, що віртуальне середовище активне (попередня команда спрацювала).
/// tip | Порада
Це необов'язково, але це гарний спосіб перевірити, що все працює як очікується і ви використовуєте саме те віртуальне середовище, яке планували.
///
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
Якщо показано бінарний файл `python` за шляхом `.venv/bin/python` усередині вашого проєкту (у цьому випадку `awesome-project`), тоді все спрацювало. 🎉
////
//// tab | Windows PowerShell
Якщо показано бінарний файл `python` за шляхом `.venv\Scripts\python` усередині вашого проєкту (у цьому випадку `awesome-project`), тоді все спрацювало. 🎉
////
## Оновіть `pip` { #upgrade-pip }
/// tip | Порада
Якщо ви використовуєте [`uv`](https://github.com/astral-sh/uv), ви використовуватимете його для встановлення замість `pip`, тож вам не потрібно оновлювати `pip`. 😎
///
Якщо ви використовуєте `pip` для встановлення пакетів (він іде за замовчуванням із Python), вам слід оновити його до найновішої версії.
Багато дивних помилок під час встановлення пакета вирішуються тим, що спочатку оновлюють `pip`.
/// tip | Порада
Зазвичай це роблять один раз, відразу після створення віртуального середовища.
///
Переконайтеся, що віртуальне середовище активне (командою вище), а потім виконайте:
/// tip | Порада
Іноді ви можете отримати помилку **`No module named pip`** при спробі оновити pip.
Якщо це сталося, встановіть і оновіть pip за допомогою команди нижче:
Ця команда встановить pip, якщо він ще не встановлений, і також гарантує, що встановлена версія pip принаймні така ж нова, як доступна в `ensurepip`.
///
## Додайте `.gitignore` { #add-gitignore }
Якщо ви використовуєте Git (варто це робити), додайте файл `.gitignore`, щоб виключити з Git усе у вашому `.venv`.
/// tip | Порада
Якщо ви використали [`uv`](https://github.com/astral-sh/uv) для створення віртуального середовища, він уже зробив це за вас, можете пропустити цей крок. 😎
///
/// tip | Порада
Зробіть це один раз, відразу після створення віртуального середовища.
///
```console
$ echo "*" > .venv/.gitignore
```
/// details | Що означає ця команда
* `echo "*"`: «виведе» текст `*` у термінал (наступна частина трохи це змінює)
* `>`: усе, що команда ліворуч від `>` «виводить» у термінал, не слід друкувати, натомість записати у файл, вказаний праворуч від `>`
* `.gitignore`: назва файлу, куди слід записати текст
А `*` для Git означає «все». Тож він ігноруватиме все в каталозі `.venv`.
Ця команда створить файл `.gitignore` із вмістом:
```gitignore
*
```
///
## Встановіть пакети { #install-packages }
Після активації середовища ви можете встановлювати в нього пакети.
/// tip | Порада
Робіть це один раз під час встановлення або оновлення пакетів, потрібних вашому проєкту.
Якщо вам потрібно оновити версію або додати новий пакет, ви зробите це знову.
///
### Встановіть пакети безпосередньо { #install-packages-directly }
Якщо ви поспішаєте та не хочете використовувати файл для оголошення вимог вашого проєкту до пакетів, ви можете встановити їх безпосередньо.
/// tip | Порада
Дуже добра ідея - записати пакети та версії, потрібні вашій програмі, у файл (наприклад, `requirements.txt` або `pyproject.toml`).
///
//// tab | `pip`
////
### Встановіть з `requirements.txt` { #install-from-requirements-txt }
Якщо у вас є `requirements.txt`, ви можете використати його для встановлення перелічених там пакетів.
//// tab | `pip`
////
/// details | `requirements.txt`
`requirements.txt` із деякими пакетами може виглядати так:
```requirements.txt
fastapi[standard]==0.113.0
pydantic==2.8.0
```
///
## Запустіть вашу програму { #run-your-program }
Після активації віртуального середовища ви можете запустити вашу програму, і вона використовуватиме Python із вашого віртуального середовища з пакетами, які ви там встановили.
```console
$ python main.py
Hello World
```
## Налаштуйте ваш редактор { #configure-your-editor }
Ймовірно, ви використовуєте редактор коду, переконайтеся, що ви налаштували його на використання того самого віртуального середовища, яке ви створили (швидше за все, він визначить його автоматично), щоб отримувати автодоповнення та підсвічування помилок.
Наприклад:
* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment)
* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html)
/// tip | Порада
Зазвичай це потрібно робити лише один раз, коли ви створюєте віртуальне середовище.
///
## Деактивуйте віртуальне середовище { #deactivate-the-virtual-environment }
Коли ви завершили роботу над проєктом, ви можете деактивувати віртуальне середовище.
```console
$ deactivate
```
Таким чином, коли ви запустите `python`, він не намагатиметься запускатися з того віртуального середовища з установленими там пакетами.
## Готові до роботи { #ready-to-work }
Тепер ви готові почати працювати над вашим проєктом.
/// tip | Порада
Хочете зрозуміти, що це все було вище?
Продовжуйте читати. 👇🤓
///
## Навіщо віртуальні середовища { #why-virtual-environments }
Щоб працювати з FastAPI, вам потрібно встановити [Python](https://www.python.org/).
Після цього вам потрібно буде встановити FastAPI та інші пакети, які ви хочете використовувати.
Для встановлення пакетів зазвичай використовують команду `pip`, що постачається з Python (або схожі альтернативи).
Однак, якщо ви просто користуватиметеся `pip` напряму, пакети встановлюватимуться у ваше глобальне середовище Python (глобальну інсталяцію Python).
### Проблема { #the-problem }
То в чому ж проблема встановлення пакетів у глобальне середовище Python?
З часом ви, вірогідно, писатимете багато різних програм, які залежать від різних пакетів. І деякі з цих ваших проєктів залежатимуть від різних версій одного й того ж пакета. 😱
Наприклад, ви можете створити проєкт із назвою `philosophers-stone`, ця програма залежить від іншого пакета з назвою `harry`, використовуючи версію `1`. Тож вам потрібно встановити `harry`.
```mermaid
flowchart LR
stone(philosophers-stone) -->|requires| harry-1[harry v1]
```
Потім, трохи згодом, ви створюєте інший проєкт із назвою `prisoner-of-azkaban`, і цей проєкт також залежить від `harry`, але йому потрібна версія `harry` `3`.
```mermaid
flowchart LR
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3]
```
Але тепер проблема в тому, що якщо ви встановлюєте пакети глобально (у глобальне середовище), а не у локальне віртуальне середовище, вам доведеться вибирати, яку версію `harry` встановити.
Якщо ви хочете запустити `philosophers-stone`, вам спочатку потрібно встановити `harry` версії `1`, наприклад, так:
```console
$ pip install "harry==1"
```
У підсумку у вас буде встановлено `harry` версії `1` у глобальному середовищі Python.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -->|requires| harry-1
end
```
Але якщо ви захочете запустити `prisoner-of-azkaban`, вам доведеться видалити `harry` версії `1` і встановити `harry` версії `3` (або просто встановлення версії `3` автоматично видалить версію `1`).
```console
$ pip install "harry==3"
```
У підсумку у вас буде встановлено `harry` версії `3` у глобальному середовищі Python.
А якщо ви знову спробуєте запустити `philosophers-stone`, є шанс, що він не працюватиме, тому що йому потрібен `harry` версії `1`.
```mermaid
flowchart LR
subgraph global[global env]
harry-1[harry v1]
style harry-1 fill:#ccc,stroke-dasharray: 5 5
harry-3[harry v3]
end
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) -.-x|⛔️| harry-1
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --> |requires| harry-3
end
```
/// tip | Порада
У пакетах Python дуже поширена практика намагатися якнайкраще уникати несумісних змін у нових версіях, але краще підстрахуватися та встановлювати новіші версії свідомо і тоді, коли ви можете запустити тести, щоб перевірити, що все працює коректно.
///
Тепер уявіть те саме з багатьма іншими пакетами, від яких залежать усі ваші проєкти. Це дуже складно керувати. І ви, імовірно, запускатимете деякі проєкти з деякими несумісними версіями пакетів і не розумітимете, чому щось не працює.
Також, залежно від вашої операційної системи (напр., Linux, Windows, macOS), у ній може бути вже встановлений Python. І в такому разі, імовірно, уже будуть попередньо встановлені деякі пакети з певними версіями, потрібними вашій системі. Якщо ви встановлюєте пакети в глобальне середовище Python, ви можете зламати деякі програми, що постачаються з вашою операційною системою.
## Де встановлюються пакети { #where-are-packages-installed }
Коли ви встановлюєте Python, він створює на вашому комп'ютері кілька каталогів із деякими файлами.
Деякі з цих каталогів відповідають за зберігання всіх пакетів, які ви встановлюєте.
Коли ви запускаєте:
```console
// Не запускайте це зараз, це лише приклад 🤓
$ pip install "fastapi[standard]"
---> 100%
```
Це завантажить стиснений файл з кодом FastAPI, зазвичай із [PyPI](https://pypi.org/project/fastapi/).
Також будуть завантажені файли для інших пакетів, від яких залежить FastAPI.
Потім усе це буде розпаковано та покладено в каталог на вашому комп'ютері.
Типово ці завантажені та розпаковані файли будуть покладені в каталог, що постачається з вашою інсталяцією Python, це глобальне середовище.
## Що таке віртуальні середовища { #what-are-virtual-environments }
Рішенням проблеми з наявністю всіх пакетів у глобальному середовищі є використання віртуального середовища для кожного проєкту, над яким ви працюєте.
Віртуальне середовище - це каталог, дуже схожий на глобальний, у якому ви можете встановлювати пакети для конкретного проєкту.
Таким чином кожен проєкт матиме власне віртуальне середовище (каталог `.venv`) із власними пакетами.
```mermaid
flowchart TB
subgraph stone-project[philosophers-stone project]
stone(philosophers-stone) --->|requires| harry-1
subgraph venv1[.venv]
harry-1[harry v1]
end
end
subgraph azkaban-project[prisoner-of-azkaban project]
azkaban(prisoner-of-azkaban) --->|requires| harry-3
subgraph venv2[.venv]
harry-3[harry v3]
end
end
stone-project ~~~ azkaban-project
```
## Що означає активація віртуального середовища { #what-does-activating-a-virtual-environment-mean }
Коли ви активуєте віртуальне середовище, наприклад так:
//// tab | Linux, macOS
```console
$ source .venv/bin/activate
```
////
//// tab | Windows PowerShell
```console
$ .venv\Scripts\Activate.ps1
```
////
//// tab | Windows Bash
Або якщо ви використовуєте Bash для Windows (напр., [Git Bash](https://gitforwindows.org/)):
```console
$ source .venv/Scripts/activate
```
////
Ця команда створить або змінить деякі [Змінні оточення](environment-variables.md), які будуть доступні для наступних команд.
Однією з цих змінних є змінна `PATH`.
/// tip | Порада
Ви можете дізнатися більше про змінну оточення `PATH` у розділі [Змінні оточення](environment-variables.md#path-environment-variable).
///
Активація віртуального середовища додає його шлях `.venv/bin` (на Linux і macOS) або `.venv\Scripts` (на Windows) до змінної оточення `PATH`.
Скажімо, до активації середовища змінна `PATH` виглядала так:
//// tab | Linux, macOS
```plaintext
/usr/bin:/bin:/usr/sbin:/sbin
```
Це означає, що система шукатиме програми в:
* `/usr/bin`
* `/bin`
* `/usr/sbin`
* `/sbin`
////
//// tab | Windows
```plaintext
C:\Windows\System32
```
Це означає, що система шукатиме програми в:
* `C:\Windows\System32`
////
Після активації віртуального середовища змінна `PATH` виглядатиме приблизно так:
//// tab | Linux, macOS
```plaintext
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin
```
Це означає, що система тепер спочатку шукатиме програми в:
```plaintext
/home/user/code/awesome-project/.venv/bin
```
перед тим, як шукати в інших каталогах.
Тож коли ви введете `python` у терміналі, система знайде програму Python у
```plaintext
/home/user/code/awesome-project/.venv/bin/python
```
і використає саме її.
////
//// tab | Windows
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32
```
Це означає, що система тепер спочатку шукатиме програми в:
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts
```
перед тим, як шукати в інших каталогах.
Тож коли ви введете `python` у терміналі, система знайде програму Python у
```plaintext
C:\Users\user\code\awesome-project\.venv\Scripts\python
```
і використає саме її.
////
Важлива деталь: шлях до віртуального середовища буде додано на початок змінної `PATH`. Система знайде його раніше за будь-який інший доступний Python. Таким чином, коли ви запускаєте `python`, використовується саме Python із віртуального середовища, а не будь-який інший `python` (наприклад, з глобального середовища).
Активація віртуального середовища також змінює ще кілька речей, але це одна з найважливіших.
## Перевірка віртуального середовища { #checking-a-virtual-environment }
Коли ви перевіряєте, чи активне віртуальне середовище, наприклад так:
//// tab | Linux, macOS, Windows Bash
```console
$ which python
/home/user/code/awesome-project/.venv/bin/python
```
////
Це означає, що програма `python`, яка буде використана, знаходиться у віртуальному середовищі.
На Linux і macOS використовують `which`, а в Windows PowerShell - `Get-Command`.
Принцип роботи цієї команди в тому, що вона перевіряє змінну оточення `PATH`, проходячи по кожному шляху по порядку, шукаючи програму з назвою `python`. Щойно вона її знайде, вона покаже вам шлях до цієї програми.
Найважливіше, що коли ви викликаєте `python`, це рівно той «`python`», який буде виконаний.
Отже, ви можете підтвердити, чи перебуваєте в правильному віртуальному середовищі.
/// tip | Порада
Легко активувати одне віртуальне середовище, отримати один Python, а потім перейти до іншого проєкту.
І другий проєкт не працюватиме, бо ви використовуєте некоректний Python з віртуального середовища іншого проєкту.
Корисно вміти перевіряти, який саме `python` використовується. 🤓
///
## Навіщо деактивувати віртуальне середовище { #why-deactivate-a-virtual-environment }
Наприклад, ви працюєте над проєктом `philosophers-stone`, активували його віртуальне середовище, встановили пакети та працюєте з цим середовищем.
А потім ви хочете працювати над іншим проєктом `prisoner-of-azkaban`.
Ви переходите до цього проєкту:
```console
$ cd ~/code/prisoner-of-azkaban
```
Якщо ви не деактивуєте віртуальне середовище для `philosophers-stone`, коли ви запустите `python` у терміналі, він спробує використовувати Python із `philosophers-stone`.
```console
$ cd ~/code/prisoner-of-azkaban
$ python main.py
// Помилка імпорту sirius, його не встановлено 😱
Traceback (most recent call last):
File "main.py", line 1, in
import sirius
```
Але якщо ви деактивуєте віртуальне середовище і активуєте нове для `prisoner-of-askaban`, тоді при запуску `python` він використовуватиме Python із віртуального середовища в `prisoner-of-azkaban`.
```console
$ cd ~/code/prisoner-of-azkaban
// Вам не потрібно бути в старому каталозі, щоб деактивувати, це можна зробити будь-де, навіть після переходу до іншого проєкту 😎
$ deactivate
// Активуйте віртуальне середовище в prisoner-of-azkaban/.venv 🚀
$ source .venv/bin/activate
// Тепер, коли ви запускаєте python, він знайде пакет sirius, встановлений у цьому віртуальному середовищі ✨
$ python main.py
I solemnly swear 🐺
```
## Альтернативи { #alternatives }
Це простий посібник, щоб ви швидко стартували та зрозуміли, як усе працює «під капотом».
Існує багато альтернатив керування віртуальними середовищами, залежностями пакетів (вимогами), проєктами.
Коли будете готові й захочете використовувати інструмент для керування всім проєктом, залежностями пакетів, віртуальними середовищами тощо, я раджу спробувати [uv](https://github.com/astral-sh/uv).
`uv` уміє багато чого, зокрема:
* Встановлювати Python для вас, включно з різними версіями
* Керувати віртуальним середовищем ваших проєктів
* Встановлювати пакети
* Керувати залежностями пакетів і версіями у вашому проєкті
* Гарантувати, що у вас є точний набір пакетів і версій для встановлення, включно з їхніми залежностями, щоб ви були певні, що зможете запустити ваш проєкт у продакшені точно так само, як і на вашому комп'ютері під час розробки - це називається блокуванням
* І багато іншого
## Висновок { #conclusion }
Якщо ви все це прочитали й зрозуміли, тепер ви знаєте значно більше про віртуальні середовища, ніж багато розробників. 🤓
Знання цих деталей, найімовірніше, стане в пригоді в майбутньому, коли ви налагоджуватимете щось, що виглядає складним, але ви знатимете, як усе працює «під капотом». 😎
================================================
FILE: docs/uk/llm-prompt.md
================================================
### Target language
Translate to Ukrainian (українська).
Language code: uk.
### Grammar and tone
- Use polite/formal address consistent with existing Ukrainian docs (use “ви/ваш”).
- Keep the tone concise and technical.
- Use one style of dashes. For example, if text contains "-" then use only this symbol to represent a dash.
### Headings
- Follow existing Ukrainian heading style; keep headings short and instructional.
- Do not add trailing punctuation to headings.
### Quotes
- Prefer Ukrainian guillemets «…» for quoted terms in prose, matching existing Ukrainian docs.
- Never change quotes inside inline code, code blocks, URLs, or file paths.
### Ellipsis
- Keep ellipsis style consistent with existing Ukrainian docs.
- Never change `...` in code, URLs, or CLI examples.
### Preferred translations / glossary
Use the following preferred translations when they apply in documentation prose:
- request (HTTP): запит
- response (HTTP): відповідь
- path operation: операція шляху
- path operation function: функція операції шляху
- prompt: підсказка
- check: перевірка
- Parallel Server Gateway Interface: Інтерфейс Шлюзу Паралельного Сервера
- Mozilla Developer Network: Мережа Розробників Mozilla
- tutorial: навчальний посібник
- advanced user guide: просунутий посібник користувача
- deep learning: глибоке навчання
- machine learning: машинне навчання
- dependency injection: впровадження залежностей
- digest (HTTP): дайджест
- basic authentication (HTTP): базова автентифікація
- JSON schema: Схема JSON
- password flow: потік паролю
- mobile: мобільний
- body: тіло
- form: форма
- path: шлях
- query: запит
- cookie: кукі
- header: заголовок
- startup: запуск
- shutdown: вимкнення
- lifespan: тривалість життя
- authorization: авторизація
- forwarded header: направлений заголовок
- dependable: залежний
- dependent: залежний
- bound: межа
- concurrency: рівночасність
- parallelism: паралелізм
- multiprocessing: багатопроцесорність
- env var: змінна оточення
- dict: словник
- enum: перелік
- issue: проблема
- server worker: серверний працівник
- worker: працівник
- software development kit: набір для розробки програмного забезпечення
- bearer token: токен носія
- breaking change: несумісна зміна
- bug: помилка
- button: кнопка
- callable: викликаємий
- code: код
- commit: фіксація
- context manager: менеджер контексту
- coroutine: співпрограма
- engine: рушій
- fake X: фальшивий X
- item: предмет
- lock: блокування
- middleware: проміжне програмне забезпечення
- mounting: монтування
- origin: джерело
- override: переписування
- payload: корисне навантаження
- processor: процесор
- property: властивість
- proxy: представник
- pull request: запит на витяг
- random-access memory: пам'ять з довільним доступом
- status code: код статусу
- string: строка
- tag: мітка
- wildcard: дика карта
### `///` admonitions
- Keep the admonition keyword in English (do not translate `note`, `tip`, etc.).
- If a title is present, prefer these canonical titles (choose one canonical form where variants exist):
- `/// note | Примітка`
- `/// note | Технічні деталі`
- `/// tip | Порада`
- `/// warning | Попередження`
- `/// info | Інформація`
- `/// danger | Обережно`
- `/// check | Перевірте`
================================================
FILE: docs/uk/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs/zh/docs/_llm-test.md
================================================
# LLM 测试文件 { #llm-test-file }
本文用于测试用于翻译文档的 LLM 是否理解 `scripts/translate.py` 中的 `general_prompt` 以及 `docs/{language code}/llm-prompt.md` 中的语言特定提示。语言特定提示会追加到 `general_prompt` 之后。
这里添加的测试会被所有语言特定提示的设计者看到。
用法如下:
* 准备语言特定提示——`docs/{language code}/llm-prompt.md`。
* 将本文重新翻译为你的目标语言(例如使用 `translate.py` 的 `translate-page` 命令)。这会在 `docs/{language code}/docs/_llm-test.md` 下创建翻译。
* 检查翻译是否正确。
* 如有需要,改进你的语言特定提示、通用提示,或英文文档。
* 然后手动修正翻译中剩余的问题,确保这是一个优秀的译文。
* 重新翻译,在已有的优秀译文基础上进行。理想情况是 LLM 不再对译文做任何更改。这意味着通用提示和你的语言特定提示已经尽可能完善(有时它仍会做一些看似随机的改动,原因是[LLM 不是确定性算法](https://doublespeak.chat/#/handbook#deterministic-output))。
测试如下:
## 代码片段 { #code-snippets }
//// tab | 测试
这是一个代码片段:`foo`。这是另一个代码片段:`bar`。还有一个:`baz quux`。
////
//// tab | 信息
代码片段的内容应保持不变。
参见 `scripts/translate.py` 中通用提示的 `### Content of code snippets` 部分。
////
## 引号 { #quotes }
//// tab | 测试
昨天,我的朋友写道:"如果你把 incorrectly 拼对了,你就把它拼错了"。我回答:"没错,但 'incorrectly' 错的不是 '"incorrectly"'"。
/// note | 注意
LLM 很可能会把这段翻错。我们只关心在重新翻译时它是否能保持修正后的译文。
///
////
//// tab | 信息
提示词设计者可以选择是否将中性引号转换为排版引号。也可以保持不变。
例如参见 `docs/de/llm-prompt.md` 中的 `### Quotes` 部分。
////
## 代码片段中的引号 { #quotes-in-code-snippets }
//// tab | 测试
`pip install "foo[bar]"`
代码片段中的字符串字面量示例:`"this"`,`'that'`。
一个较难的字符串字面量示例:`f"I like {'oranges' if orange else "apples"}"`
硬核:`Yesterday, my friend wrote: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'"`
////
//// tab | 信息
... 但是,代码片段内的引号必须保持不变。
////
## 代码块 { #code-blocks }
//// tab | 测试
一个 Bash 代码示例...
```bash
# 向宇宙打印问候
echo "Hello universe"
```
...以及一个控制台代码示例...
```console
$ fastapi run main.py FastAPI Starting server
Searching for package file structure
```
...以及另一个控制台代码示例...
```console
// 创建目录 "code"
$ mkdir code
// 切换到该目录
$ cd code
```
...以及一个 Python 代码示例...
```Python
wont_work() # 这不会起作用 😱
works(foo="bar") # 这可行 🎉
```
...就这些。
////
//// tab | 信息
代码块中的代码不应被修改,注释除外。
参见 `scripts/translate.py` 中通用提示的 `### Content of code blocks` 部分。
////
## 选项卡与彩色提示框 { #tabs-and-colored-boxes }
//// tab | 测试
/// info | 信息
Some text
///
/// note | 注意
Some text
///
/// note | 技术细节
Some text
///
/// check | 检查
Some text
///
/// tip | 提示
Some text
///
/// warning | 警告
Some text
///
/// danger | 危险
Some text
///
////
//// tab | 信息
选项卡以及 `Info`/`Note`/`Warning`/等提示块,应在竖线(`|`)后添加其标题的翻译。
参见 `scripts/translate.py` 中通用提示的 `### Special blocks` 与 `### Tab blocks` 部分。
////
## Web 与内部链接 { #web-and-internal-links }
//// tab | 测试
链接文本应被翻译,链接地址应保持不变:
* [链接到上面的标题](#code-snippets)
* [内部链接](index.md#installation)
* [外部链接](https://sqlmodel.tiangolo.com/)
* [样式链接](https://fastapi.tiangolo.com/css/styles.css)
* [脚本链接](https://fastapi.tiangolo.com/js/logic.js)
* [图片链接](https://fastapi.tiangolo.com/img/foo.jpg)
链接文本应被翻译,且链接地址应指向对应的译文页面:
* [FastAPI 链接](https://fastapi.tiangolo.com/zh/)
////
//// tab | 信息
链接的文本应被翻译,但地址保持不变。唯一的例外是指向 FastAPI 文档页面的绝对链接,此时应指向对应语言的译文。
参见 `scripts/translate.py` 中通用提示的 `### Links` 部分。
////
## HTML "abbr" 元素 { #html-abbr-elements }
//// tab | 测试
这里有一些包裹在 HTML "abbr" 元素中的内容(有些是虚构的):
### abbr 提供了完整短语 { #the-abbr-gives-a-full-phrase }
* GTD
* lt
* XWT
* PSGI
### abbr 提供了完整短语与解释 { #the-abbr-gives-a-full-phrase-and-an-explanation }
* MDN
* I/O.
////
//// tab | 信息
"abbr" 元素的 "title" 属性需要按照特定规则进行翻译。
译文可以自行添加 "abbr" 元素以解释英语单词,LLM 不应删除这些元素。
参见 `scripts/translate.py` 中通用提示的 `### HTML abbr elements` 部分。
////
## HTML "dfn" 元素 { #html-dfn-elements }
* 集群
* 深度学习
## 标题 { #headings }
//// tab | 测试
### 开发 Web 应用——教程 { #develop-a-webapp-a-tutorial }
Hello.
### 类型提示与注解 { #type-hints-and-annotations }
Hello again.
### 超类与子类 { #super-and-subclasses }
Hello again.
////
//// tab | 信息
关于标题的唯一硬性规则是:LLM 必须保持花括号内的哈希部分不变,以确保链接不会失效。
参见 `scripts/translate.py` 中通用提示的 `### Headings` 部分。
语言特定的说明可参见例如 `docs/de/llm-prompt.md` 中的 `### Headings` 部分。
////
## 文档中使用的术语 { #terms-used-in-the-docs }
//// tab | 测试
* you
* your
* e.g.
* etc.
* `foo` as an `int`
* `bar` as a `str`
* `baz` as a `list`
* the Tutorial - User guide
* the Advanced User Guide
* the SQLModel docs
* the API docs
* the automatic docs
* Data Science
* Deep Learning
* Machine Learning
* Dependency Injection
* HTTP Basic authentication
* HTTP Digest
* ISO format
* the JSON Schema standard
* the JSON schema
* the schema definition
* Password Flow
* Mobile
* deprecated
* designed
* invalid
* on the fly
* standard
* default
* case-sensitive
* case-insensitive
* to serve the application
* to serve the page
* the app
* the application
* the request
* the response
* the error response
* the path operation
* the path operation decorator
* the path operation function
* the body
* the request body
* the response body
* the JSON body
* the form body
* the file body
* the function body
* the parameter
* the body parameter
* the path parameter
* the query parameter
* the cookie parameter
* the header parameter
* the form parameter
* the function parameter
* the event
* the startup event
* the startup of the server
* the shutdown event
* the lifespan event
* the handler
* the event handler
* the exception handler
* to handle
* the model
* the Pydantic model
* the data model
* the database model
* the form model
* the model object
* the class
* the base class
* the parent class
* the subclass
* the child class
* the sibling class
* the class method
* the header
* the headers
* the authorization header
* the `Authorization` header
* the forwarded header
* the dependency injection system
* the dependency
* the dependable
* the dependant
* I/O bound
* CPU bound
* concurrency
* parallelism
* multiprocessing
* the env var
* the environment variable
* the `PATH`
* the `PATH` variable
* the authentication
* the authentication provider
* the authorization
* the authorization form
* the authorization provider
* the user authenticates
* the system authenticates the user
* the CLI
* the command line interface
* the server
* the client
* the cloud provider
* the cloud service
* the development
* the development stages
* the dict
* the dictionary
* the enumeration
* the enum
* the enum member
* the encoder
* the decoder
* to encode
* to decode
* the exception
* to raise
* the expression
* the statement
* the frontend
* the backend
* the GitHub discussion
* the GitHub issue
* the performance
* the performance optimization
* the return type
* the return value
* the security
* the security scheme
* the task
* the background task
* the task function
* the template
* the template engine
* the type annotation
* the type hint
* the server worker
* the Uvicorn worker
* the Gunicorn Worker
* the worker process
* the worker class
* the workload
* the deployment
* to deploy
* the SDK
* the software development kit
* the `APIRouter`
* the `requirements.txt`
* the Bearer Token
* the breaking change
* the bug
* the button
* the callable
* the code
* the commit
* the context manager
* the coroutine
* the database session
* the disk
* the domain
* the engine
* the fake X
* the HTTP GET method
* the item
* the library
* the lifespan
* the lock
* the middleware
* the mobile application
* the module
* the mounting
* the network
* the origin
* the override
* the payload
* the processor
* the property
* the proxy
* the pull request
* the query
* the RAM
* the remote machine
* the status code
* the string
* the tag
* the web framework
* the wildcard
* to return
* to validate
////
//// tab | 信息
这是一份不完整且非规范性的(主要是)技术术语清单,取自文档中常见的词汇。它可能有助于提示词设计者判断哪些术语需要对 LLM 提供额外指引。例如当它总是把一个好的译法改回次优译法,或在你的语言中对某个术语的词形变化有困难时。
参见例如 `docs/de/llm-prompt.md` 中的 `### List of English terms and their preferred German translations` 部分。
////
================================================
FILE: docs/zh/docs/about/index.md
================================================
# 关于 { #about }
关于 FastAPI、其设计、灵感等。🤓
================================================
FILE: docs/zh/docs/advanced/additional-responses.md
================================================
# OpenAPI 中的附加响应 { #additional-responses-in-openapi }
/// warning | 警告
这是一个相对高级的话题。
如果你刚开始使用 **FastAPI**,可能暂时用不到。
///
你可以声明附加响应,包括额外的状态码、媒体类型、描述等。
这些附加响应会被包含在 OpenAPI 模式中,因此它们也会出现在 API 文档中。
但是对于这些附加响应,你必须确保直接返回一个 `Response`(例如 `JSONResponse`),并携带你的状态码和内容。
## 带有 `model` 的附加响应 { #additional-response-with-model }
你可以向你的*路径操作装饰器*传入参数 `responses`。
它接收一个 `dict`:键是每个响应的状态码(例如 `200`),值是包含该响应信息的另一个 `dict`。
这些响应的每个 `dict` 都可以有一个键 `model`,包含一个 Pydantic 模型,就像 `response_model` 一样。
**FastAPI** 会获取该模型,生成它的 JSON Schema,并将其放在 OpenAPI 中的正确位置。
例如,要声明另一个状态码为 `404` 且具有 Pydantic 模型 `Message` 的响应,你可以这样写:
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
/// note | 注意
记住你需要直接返回 `JSONResponse`。
///
/// info | 信息
`model` 键不是 OpenAPI 的一部分。
**FastAPI** 会从这里获取 Pydantic 模型,生成 JSON Schema,并把它放到正确的位置。
正确的位置是:
* 在键 `content` 中,它的值是另一个 JSON 对象(`dict`),该对象包含:
* 一个媒体类型作为键,例如 `application/json`,它的值是另一个 JSON 对象,该对象包含:
* 一个键 `schema`,它的值是来自该模型的 JSON Schema,这里就是正确的位置。
* **FastAPI** 会在这里添加一个引用,指向你 OpenAPI 中另一个位置的全局 JSON Schemas,而不是直接内联。这样,其他应用和客户端可以直接使用这些 JSON Schemas,提供更好的代码生成工具等。
///
为该*路径操作*在 OpenAPI 中生成的响应将是:
```JSON hl_lines="3-12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
这些模式在 OpenAPI 模式中被引用到另一个位置:
```JSON hl_lines="4-16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## 主响应的其他媒体类型 { #additional-media-types-for-the-main-response }
你可以使用同一个 `responses` 参数为同一个主响应添加不同的媒体类型。
例如,你可以添加一个额外的媒体类型 `image/png`,声明你的*路径操作*可以返回 JSON 对象(媒体类型为 `application/json`)或 PNG 图片:
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
/// note | 注意
请注意,你必须直接使用 `FileResponse` 返回图片。
///
/// info | 信息
除非你在 `responses` 参数中明确指定不同的媒体类型,否则 FastAPI 会假设响应与主响应类具有相同的媒体类型(默认是 `application/json`)。
但是如果你指定了一个媒体类型为 `None` 的自定义响应类,FastAPI 会对任何具有关联模型的附加响应使用 `application/json`。
///
## 组合信息 { #combining-information }
你也可以把来自多个位置的响应信息组合在一起,包括 `response_model`、`status_code` 和 `responses` 参数。
你可以声明一个 `response_model`,使用默认状态码 `200`(或根据需要使用自定义状态码),然后在 `responses` 中直接在 OpenAPI 模式里为同一个响应声明附加信息。
**FastAPI** 会保留来自 `responses` 的附加信息,并把它与你的模型生成的 JSON Schema 合并。
例如,你可以声明一个状态码为 `404` 的响应,它使用一个 Pydantic 模型并带有自定义的 `description`。
以及一个状态码为 `200` 的响应,它使用你的 `response_model`,但包含自定义的 `example`:
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
所有这些都会被合并并包含到你的 OpenAPI 中,并显示在 API 文档里:
## 组合预定义响应和自定义响应 { #combine-predefined-responses-and-custom-ones }
你可能希望有一些适用于许多*路径操作*的预定义响应,但同时又想把它们与每个*路径操作*所需的自定义响应组合在一起。
在这些情况下,你可以使用 Python 的“解包”`dict` 的技巧 `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
这里,`new_dict` 将包含来自 `old_dict` 的所有键值对,再加上新的键值对:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
你可以使用该技巧在*路径操作*中重用一些预定义响应,并把它们与额外的自定义响应组合在一起。
例如:
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
## 关于 OpenAPI 响应的更多信息 { #more-information-about-openapi-responses }
要查看响应中究竟可以包含什么,你可以查看 OpenAPI 规范中的以下部分:
* [OpenAPI Responses 对象](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object),它包含 `Response Object`。
* [OpenAPI Response 对象](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object),你可以把这里的任何内容直接包含到 `responses` 参数中的每个响应里。包括 `description`、`headers`、`content`(在这里声明不同的媒体类型和 JSON Schemas),以及 `links`。
================================================
FILE: docs/zh/docs/advanced/additional-status-codes.md
================================================
# 额外的状态码 { #additional-status-codes }
**FastAPI** 默认使用 `JSONResponse` 返回一个响应,将你的 *路径操作* 中的返回内容放到该 `JSONResponse` 中。
**FastAPI** 会自动使用默认的状态码或者使用你在 *路径操作* 中设置的状态码。
## 额外的状态码 { #additional-status-codes_1 }
如果你想要返回主要状态码之外的状态码,你可以通过直接返回一个 `Response` 来实现,比如 `JSONResponse`,然后直接设置额外的状态码。
例如,假设你想有一个 *路径操作* 能够更新条目,并且更新成功时返回 200 「成功」 的 HTTP 状态码。
但是你也希望它能够接受新的条目。并且当这些条目不存在时,会自动创建并返回 201 「创建」的 HTTP 状态码。
要实现它,导入 `JSONResponse`,然后在其中直接返回你的内容,并将 `status_code` 设置为你要的值。
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
/// warning
当你直接返回一个像上面例子中的 `Response` 对象时,它会直接返回。
它不会用模型等进行序列化。
确保其中有你想要的数据,且返回的值为合法的 JSON(如果你使用 `JSONResponse` 的话)。
///
/// note | 技术细节
你也可以使用 `from starlette.responses import JSONResponse`。
出于方便,**FastAPI** 为开发者提供同 `starlette.responses` 一样的 `fastapi.responses`。但是大多数可用的响应都是直接来自 Starlette。`status` 也是一样。
///
## OpenAPI 和 API 文档 { #openapi-and-api-docs }
如果你直接返回额外的状态码和响应,它们不会包含在 OpenAPI 方案(API 文档)中,因为 FastAPI 没办法预先知道你要返回什么。
但是你可以使用 [额外的响应](additional-responses.md) 在代码中记录这些内容。
================================================
FILE: docs/zh/docs/advanced/advanced-dependencies.md
================================================
# 高级依赖项 { #advanced-dependencies }
## 参数化的依赖项 { #parameterized-dependencies }
目前我们看到的依赖项都是固定的函数或类。
但有时你可能希望为依赖项设置参数,而不必声明许多不同的函数或类。
假设我们要有一个依赖项,用来检查查询参数 `q` 是否包含某个固定内容。
但我们希望能够把这个固定内容参数化。
## “可调用”的实例 { #a-callable-instance }
在 Python 中,可以让某个类的实例变成“可调用对象”(callable)。
这里指的是类的实例(类本身已经是可调用的),而不是类本身。
为此,声明一个 `__call__` 方法:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
在这种情况下,**FastAPI** 会使用这个 `__call__` 来检查附加参数和子依赖,并且稍后会调用它,把返回值传递给你的*路径操作函数*中的参数。
## 参数化实例 { #parameterize-the-instance }
现在,我们可以用 `__init__` 声明实例的参数,用来“参数化”这个依赖项:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
在本例中,**FastAPI** 不会接触或关心 `__init__`,我们会在自己的代码中直接使用它。
## 创建实例 { #create-an-instance }
我们可以这样创建该类的实例:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
这样就把依赖项“参数化”了,现在它内部带有属性 `checker.fixed_content` 的值 `"bar"`。
## 把实例作为依赖项 { #use-the-instance-as-a-dependency }
然后,我们可以在 `Depends(checker)` 中使用这个 `checker`,而不是 `Depends(FixedContentQueryChecker)`,因为依赖项是实例 `checker`,不是类本身。
解析依赖项时,**FastAPI** 会像这样调用 `checker`:
```Python
checker(q="somequery")
```
...并将其返回值作为依赖项的值,传给我们的*路径操作函数*中的参数 `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
/// tip | 提示
这些看起来可能有些牵强,目前它的用处也许还不太明显。
这些示例刻意保持简单,但展示了整体的工作方式。
在安全相关的章节里,有一些工具函数就是以相同的方式实现的。
如果你理解了这里的内容,你就已经知道那些安全工具在底层是如何工作的。
///
## 带 `yield` 的依赖项、`HTTPException`、`except` 与后台任务 { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | 警告
你很可能不需要了解这些技术细节。
这些细节主要在你的 FastAPI 应用版本低于 0.121.0 且你正遇到带 `yield` 的依赖项问题时才有用。
///
带 `yield` 的依赖项随着时间演进以覆盖不同用例并修复一些问题,下面是变更摘要。
### 带 `yield` 的依赖项与 `scope` { #dependencies-with-yield-and-scope }
在 0.121.0 版本中,FastAPI 为带 `yield` 的依赖项新增了 `Depends(scope="function")` 的支持。
使用 `Depends(scope="function")` 时,`yield` 之后的退出代码会在*路径操作函数*结束后、响应发送给客户端之前立即执行。
而当使用默认的 `Depends(scope="request")` 时,`yield` 之后的退出代码会在响应发送之后执行。
你可以在文档 [带 `yield` 的依赖项 - 提前退出与 `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 中了解更多。
### 带 `yield` 的依赖项与 `StreamingResponse`(技术细节) { #dependencies-with-yield-and-streamingresponse-technical-details }
在 FastAPI 0.118.0 之前,如果你使用带 `yield` 的依赖项,它会在*路径操作函数*返回后、发送响应之前运行 `yield` 之后的退出代码。
这样做的目的是避免在等待响应通过网络传输期间不必要地占用资源。
这也意味着,如果你返回的是 `StreamingResponse`,那么该带 `yield` 的依赖项的退出代码会在开始发送响应前就已经执行完毕。
例如,如果你在带 `yield` 的依赖项中持有一个数据库会话,那么 `StreamingResponse` 在流式发送数据时将无法使用该会话,因为会话已经在 `yield` 之后的退出代码里被关闭了。
在 0.118.0 中,这一行为被回退为:让 `yield` 之后的退出代码在响应发送之后再执行。
/// info | 信息
如你在下文所见,这与 0.106.0 之前的行为非常相似,但对若干边界情况做了改进和修复。
///
#### 需要提前执行退出代码的用例 { #use-cases-with-early-exit-code }
在某些特定条件下,旧的行为(在发送响应之前执行带 `yield` 依赖项的退出代码)会更有利。
例如,设想你在带 `yield` 的依赖项中仅用数据库会话来校验用户,而在*路径操作函数*中并不会再次使用该会话;同时,响应需要很长时间才能发送完,比如一个缓慢发送数据的 `StreamingResponse`,且它出于某种原因并不使用数据库。
这种情况下,会一直持有数据库会话直到响应发送完毕;但如果并不再使用它,就没有必要一直占用。
代码可能如下:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
退出代码(自动关闭 `Session`)位于:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
...会在响应把慢速数据发送完之后才运行:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
但由于 `generate_stream()` 并不使用数据库会话,因此在发送响应期间保持会话打开并非必要。
如果你使用的是 SQLModel(或 SQLAlchemy)并碰到这种特定用例,你可以在不再需要时显式关闭会话:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
这样会话会释放数据库连接,让其他请求可以使用。
如果你还有其他需要在 `yield` 依赖项中提前退出的用例,请创建一个 [GitHub 讨论问题](https://github.com/fastapi/fastapi/discussions/new?category=questions),说明你的具体用例以及为何提前关闭会对你有帮助。
如果确有有力的用例需要提前关闭,我会考虑新增一种选择性启用提前关闭的方式。
### 带 `yield` 的依赖项与 `except`(技术细节) { #dependencies-with-yield-and-except-technical-details }
在 FastAPI 0.110.0 之前,如果你在带 `yield` 的依赖项中用 `except` 捕获了一个异常,并且没有再次抛出它,那么该异常会被自动抛出/转发给任意异常处理器或内部服务器错误处理器。
在 0.110.0 中对此作出了变更,以修复将异常转发为未处理(内部服务器错误)时造成的内存消耗问题,并使其与常规 Python 代码的行为保持一致。
### 后台任务与带 `yield` 的依赖项(技术细节) { #background-tasks-and-dependencies-with-yield-technical-details }
在 FastAPI 0.106.0 之前,`yield` 之后抛出异常是不可行的,因为带 `yield` 的依赖项中的退出代码会在响应发送之后才执行,此时[异常处理器](../tutorial/handling-errors.md#install-custom-exception-handlers)已经运行完毕。
之所以这样设计,主要是为了允许在后台任务中继续使用依赖项通过 `yield`“产出”的对象,因为退出代码会在后台任务完成之后才执行。
在 FastAPI 0.106.0 中,这一行为被修改,目的是避免在等待响应通过网络传输时一直占用资源。
/// tip | 提示
另外,后台任务通常是一段独立的逻辑,应该单独处理,并使用它自己的资源(例如它自己的数据库连接)。
因此,这样做你的代码通常会更清晰。
///
如果你过去依赖于旧行为,现在应在后台任务内部自行创建所需资源,并且只在内部使用不依赖于带 `yield` 依赖项资源的数据。
例如,不要复用相同的数据库会话,而是在后台任务内部创建一个新的会话,并用这个新会话从数据库获取对象。然后,不是把数据库对象本身作为参数传给后台任务函数,而是传递该对象的 ID,并在后台任务函数内部再次获取该对象。
================================================
FILE: docs/zh/docs/advanced/advanced-python-types.md
================================================
# 高级 Python 类型 { #advanced-python-types }
这里有一些在使用 Python 类型时可能有用的额外想法。
## 使用 `Union` 或 `Optional` { #using-union-or-optional }
如果你的代码因为某些原因不能使用 `|`,例如它不是在类型注解里,而是在 `response_model=` 之类的参数中,那么你可以使用 `typing` 中的 `Union` 来代替竖线(`|`)。
例如,你可以声明某个值可以是 `str` 或 `None`:
```python
from typing import Union
def say_hi(name: Union[str, None]):
print(f"Hi {name}!")
```
`typing` 也提供了一个声明“可能为 `None`”的快捷方式:`Optional`。
从我非常主观的角度给个小建议:
- 🚨 避免使用 `Optional[SomeType]`
- 改用 ✨`Union[SomeType, None]`✨。
两者是等价的,底层其实也是一样的。但我更推荐使用 `Union` 而不是 `Optional`,因为单词“optional”(可选)看起来会暗示该值是可选的,而它真正的含义是“它可以是 `None`”,即使它并不是可选的,仍然是必填的。
我认为 `Union[SomeType, None]` 更能明确表达其含义。
这只是关于词语和命名的问题,但这些词语会影响你和你的队友如何看待代码。
举个例子,看这段函数:
```python
from typing import Optional
def say_hi(name: Optional[str]):
print(f"Hey {name}!")
```
参数 `name` 被定义为 `Optional[str]`,但它并不是“可选”的,你不能不传这个参数就调用函数:
```Python
say_hi() # 哎呀,这会报错!😱
```
参数 `name` 仍然是必填的(不是“可选”),因为它没有默认值。不过,`name` 接受 `None` 作为取值:
```Python
say_hi(name=None) # 这样可以,None 是有效的 🎉
```
好消息是,在大多数情况下,你可以直接使用 `|` 来定义类型联合:
```python
def say_hi(name: str | None):
print(f"Hey {name}!")
```
因此,通常你不必为像 `Optional` 和 `Union` 这样的名字而操心。😎
================================================
FILE: docs/zh/docs/advanced/async-tests.md
================================================
# 异步测试 { #async-tests }
您已经了解了如何使用 `TestClient` 测试 **FastAPI** 应用程序。但是到目前为止,您只了解了如何编写同步测试,而没有使用 `async` 异步函数。
在测试中能够使用异步函数可能会很有用,比如当您需要异步查询数据库的时候。想象一下,您想要测试向 FastAPI 应用程序发送请求,然后验证您的后端是否成功在数据库中写入了正确的数据,与此同时您使用了异步的数据库的库。
让我们看看如何才能实现这一点。
## pytest.mark.anyio { #pytest-mark-anyio }
如果我们想在测试中调用异步函数,那么我们的测试函数必须是异步的。 AnyIO 为此提供了一个简洁的插件,它允许我们指定一些测试函数要异步调用。
## HTTPX { #httpx }
即使您的 **FastAPI** 应用程序使用普通的 `def` 函数而不是 `async def` ,它本质上仍是一个 `async` 异步应用程序。
`TestClient` 在内部通过一些“魔法”操作,使得您可以在普通的 `def` 测试函数中调用异步的 FastAPI 应用程序,并使用标准的 pytest。但当我们在异步函数中使用它时,这种“魔法”就不再生效了。由于测试以异步方式运行,我们无法在测试函数中继续使用 `TestClient`。
`TestClient` 是基于 [HTTPX](https://www.python-httpx.org) 的。幸运的是,我们可以直接使用它来测试 API。
## 示例 { #example }
举个简单的例子,让我们来看一个与[更大的应用](../tutorial/bigger-applications.md)和[测试](../tutorial/testing.md)中描述的类似文件结构:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
文件 `main.py` 将包含:
{* ../../docs_src/async_tests/app_a_py310/main.py *}
文件 `test_main.py` 将包含针对 `main.py` 的测试,现在它可能看起来如下:
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
## 运行测试 { #run-it }
您可以通过以下方式照常运行测试:
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## 部署 { #deploy }
现在用**一条命令**部署你的应用:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
然后部署你的应用:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
## 部署 { #deploy }
現在用「一行指令」部署你的應用:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi run main.py FastAPI Starting production server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Started server process [2306215] INFO Waiting for application startup.
INFO Application startup complete.
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C
to quit)
```
```console
$ fastapi run --workers 4 main.py FastAPI Starting production server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://0.0.0.0:8000 server Documentation at http://0.0.0.0:8000/docs
Logs:
INFO Uvicorn running on http://0.0.0.0:8000(Press CTRL+C to
quit) INFO Started parent process [27365] INFO Started server process [27368] INFO Started server process [27369] INFO Started server process [27370] INFO Started server process [27367] INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Waiting for application startup.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
INFO Application startup complete.
```
////
//// tab | `uvicorn`
如果你偏好直接使用 `uvicorn` 指令:
```console
$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
INFO: Started parent process [27365]
INFO: Started server process [27368]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27369]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27370]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Started server process [27367]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories with
__init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with the
following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C to
quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi dev
╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯
INFO: Will watch for changes in these directories: ['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2248755] using WatchFiles
INFO: Started server process [2248757]
INFO: Waiting for application startup.
INFO: Application startup complete.
```
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
接著部署你的應用程式:
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
```console
$ fastapi dev
FastAPI Starting development server 🚀
Searching for package file structure from directories
with __init__.py files
Importing from /home/user/code/awesomeapp
module 🐍 main.py
code Importing the FastAPI app object from the module with
the following code:
from main import app app Using import string: main:app
server Server started at http://127.0.0.1:8000 server Documentation at http://127.0.0.1:8000/docs tip Running in development mode, for production use:
fastapi run
Logs:
INFO Will watch for changes in these directories:
['/home/user/code/awesomeapp'] INFO Uvicorn running on http://127.0.0.1:8000(Press CTRL+C
to quit) INFO Started reloader process [383138] using WatchFiles
INFO Started server process [383153] INFO Waiting for application startup.
INFO Application startup complete.
```
## 替代方案 { #alternatives }
這是一個簡單的指南,幫助你入門並教會你如何理解一切**底層**的原理。
有許多**替代方案**來管理虛擬環境、套件依賴(requirements)、專案。
當你準備好並想要使用一個工具來**管理整個專案**、套件依賴、虛擬環境等,建議你嘗試 [uv](https://github.com/astral-sh/uv)。
`uv` 可以執行許多操作,它可以:
* 為你**安裝 Python**,包括不同的版本
* 為你的專案管理**虛擬環境**
* 安裝**套件**
* 為你的專案管理套件的**依賴和版本**
* 確保你有一個**精確**的套件和版本集合來安裝,包括它們的依賴項,這樣你可以確保專案在生產環境中運行的狀態與開發時在你的電腦上運行的狀態完全相同,這被稱為**鎖定**
* 還有很多其他功能
## 結論 { #conclusion }
如果你讀過並理解了所有這些,現在**你對虛擬環境的了解已超過許多開發者**。🤓
未來當你為看起來複雜的問題除錯時,了解這些細節很可能會有所幫助,你會知道**它是如何在底層運作的**。😎
================================================
FILE: docs/zh-hant/llm-prompt.md
================================================
### Target language
Translate to Traditional Chinese (繁體中文).
Language code: zh-hant.
### Grammar and tone
- Use clear, concise technical Traditional Chinese consistent with existing docs.
- Address the reader naturally (commonly using “你/你的”).
### Headings
- Follow existing Traditional Chinese heading style (short and descriptive).
- Do not add trailing punctuation to headings.
### Quotes and punctuation
- Keep punctuation style consistent with existing Traditional Chinese docs (they often mix English terms like “FastAPI” with Chinese text).
- Never change punctuation inside inline code, code blocks, URLs, or file paths.
- For more details, please follow the [Chinese Copywriting Guidelines](https://github.com/sparanoid/chinese-copywriting-guidelines).
### Ellipsis
- Keep ellipsis style consistent within each document, prefer `...` over `……`.
- Never change ellipsis in code, URLs, or CLI examples.
### Preferred translations / glossary
- Should avoid using simplified Chinese characters and terms. Always examine if the translation can be easily comprehended by the Traditional Chinese readers.
- For some Python-specific terms like "pickle", "list", "dict" etc, we don't have to translate them.
- Use the following preferred translations when they apply in documentation prose:
- request (HTTP): 請求
- response (HTTP): 回應
- path operation: 路徑操作
- path operation function: 路徑操作函式
The translation can optionally include the original English text only in the first occurrence of each page (e.g. "路徑操作 (path operation)") if the translation is hard to be comprehended by most of the Chinese readers.
### `///` admonitions
1) Keep the admonition keyword in English (do not translate `note`, `tip`, etc.).
2) Many Traditional Chinese docs currently omit titles in `///` blocks; that is OK.
3) If a generic title is present, prefer these canonical titles:
- `/// note | 注意`
Notes:
- `details` blocks exist; keep `/// details` as-is and translate only the title after `|`.
- Example canonical titles used in existing docs:
```
/// details | 上述指令的含義
```
```
/// details | 關於 `requirements.txt`
```
================================================
FILE: docs/zh-hant/mkdocs.yml
================================================
INHERIT: ../en/mkdocs.yml
================================================
FILE: docs_src/additional_responses/__init__.py
================================================
================================================
FILE: docs_src/additional_responses/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
return JSONResponse(status_code=404, content={"message": "Item not found"})
================================================
FILE: docs_src/additional_responses/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
200: {
"content": {"image/png": {}},
"description": "Return the JSON item or an image.",
}
},
)
async def read_item(item_id: str, img: bool | None = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
================================================
FILE: docs_src/additional_responses/tutorial003_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
404: {"model": Message, "description": "The item was not found"},
200: {
"description": "Item requested by ID",
"content": {
"application/json": {
"example": {"id": "bar", "value": "The bar tenders"}
}
},
},
},
)
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})
================================================
FILE: docs_src/additional_responses/tutorial004_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
class Item(BaseModel):
id: str
value: str
responses = {
404: {"description": "Item not found"},
302: {"description": "The item was moved"},
403: {"description": "Not enough privileges"},
}
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: bool | None = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}
================================================
FILE: docs_src/additional_status_codes/__init__.py
================================================
================================================
FILE: docs_src/additional_status_codes/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: Annotated[str | None, Body()] = None,
size: Annotated[int | None, Body()] = None,
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
================================================
FILE: docs_src/additional_status_codes/tutorial001_py310.py
================================================
from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(
item_id: str,
name: str | None = Body(default=None),
size: int | None = Body(default=None),
):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
================================================
FILE: docs_src/advanced_middleware/__init__.py
================================================
================================================
FILE: docs_src/advanced_middleware/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
@app.get("/")
async def main():
return {"message": "Hello World"}
================================================
FILE: docs_src/advanced_middleware/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
)
@app.get("/")
async def main():
return {"message": "Hello World"}
================================================
FILE: docs_src/advanced_middleware/tutorial003_py310.py
================================================
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
@app.get("/")
async def main():
return "somebigcontent"
================================================
FILE: docs_src/app_testing/__init__.py
================================================
================================================
FILE: docs_src/app_testing/app_a_py310/__init__.py
================================================
================================================
FILE: docs_src/app_testing/app_a_py310/main.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
================================================
FILE: docs_src/app_testing/app_a_py310/test_main.py
================================================
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
================================================
FILE: docs_src/app_testing/app_b_an_py310/__init__.py
================================================
================================================
FILE: docs_src/app_testing/app_b_an_py310/main.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: str | None = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/")
async def create_item(item: Item, x_token: Annotated[str, Header()]) -> Item:
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=409, detail="Item already exists")
fake_db[item.id] = item.model_dump()
return item
================================================
FILE: docs_src/app_testing/app_b_an_py310/test_main.py
================================================
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_nonexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 409
assert response.json() == {"detail": "Item already exists"}
================================================
FILE: docs_src/app_testing/app_b_py310/__init__.py
================================================
================================================
FILE: docs_src/app_testing/app_b_py310/main.py
================================================
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
app = FastAPI()
class Item(BaseModel):
id: str
title: str
description: str | None = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: str = Header()):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/")
async def create_item(item: Item, x_token: str = Header()) -> Item:
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=409, detail="Item already exists")
fake_db[item.id] = item.model_dump()
return item
================================================
FILE: docs_src/app_testing/app_b_py310/test_main.py
================================================
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_nonexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 409
assert response.json() == {"detail": "Item already exists"}
================================================
FILE: docs_src/app_testing/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
================================================
FILE: docs_src/app_testing/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocket
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
@app.websocket("/ws")
async def websocket(websocket: WebSocket):
await websocket.accept()
await websocket.send_json({"msg": "Hello WebSocket"})
await websocket.close()
def test_read_main():
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
def test_websocket():
client = TestClient(app)
with client.websocket_connect("/ws") as websocket:
data = websocket.receive_json()
assert data == {"msg": "Hello WebSocket"}
================================================
FILE: docs_src/app_testing/tutorial003_py310.py
================================================
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
def test_read_items():
with TestClient(app) as client:
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Fighters"}
================================================
FILE: docs_src/app_testing/tutorial004_py310.py
================================================
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.testclient import TestClient
items = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
yield
# clean up items
items.clear()
app = FastAPI(lifespan=lifespan)
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
def test_read_items():
# Before the lifespan starts, "items" is still empty
assert items == {}
with TestClient(app) as client:
# Inside the "with TestClient" block, the lifespan starts and items added
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Fighters"}
# After the requests is done, the items are still there
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
# The end of the "with TestClient" block simulates terminating the app, so
# the lifespan ends and items are cleaned up
assert items == {}
================================================
FILE: docs_src/async_tests/__init__.py
================================================
================================================
FILE: docs_src/async_tests/app_a_py310/__init__.py
================================================
================================================
FILE: docs_src/async_tests/app_a_py310/main.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Tomato"}
================================================
FILE: docs_src/async_tests/app_a_py310/test_main.py
================================================
import pytest
from httpx import ASGITransport, AsyncClient
from .main import app
@pytest.mark.anyio
async def test_root():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
response = await ac.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Tomato"}
================================================
FILE: docs_src/authentication_error_status_code/__init__.py
================================================
================================================
FILE: docs_src/authentication_error_status_code/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
app = FastAPI()
class HTTPBearer403(HTTPBearer):
def make_not_authenticated_error(self) -> HTTPException:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated"
)
CredentialsDep = Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer403())]
@app.get("/me")
def read_me(credentials: CredentialsDep):
return {"message": "You are authenticated", "token": credentials.credentials}
================================================
FILE: docs_src/background_tasks/__init__.py
================================================
================================================
FILE: docs_src/background_tasks/tutorial001_py310.py
================================================
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification(email: str, message=""):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
================================================
FILE: docs_src/background_tasks/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}
================================================
FILE: docs_src/background_tasks/tutorial002_py310.py
================================================
from fastapi import BackgroundTasks, Depends, FastAPI
app = FastAPI()
def write_log(message: str):
with open("log.txt", mode="a") as log:
log.write(message)
def get_query(background_tasks: BackgroundTasks, q: str | None = None):
if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}
================================================
FILE: docs_src/behind_a_proxy/__init__.py
================================================
================================================
FILE: docs_src/behind_a_proxy/tutorial001_01_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
def read_items():
return ["plumbus", "portal gun"]
================================================
FILE: docs_src/behind_a_proxy/tutorial001_py310.py
================================================
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
================================================
FILE: docs_src/behind_a_proxy/tutorial002_py310.py
================================================
from fastapi import FastAPI, Request
app = FastAPI(root_path="/api/v1")
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
================================================
FILE: docs_src/behind_a_proxy/tutorial003_py310.py
================================================
from fastapi import FastAPI, Request
app = FastAPI(
servers=[
{"url": "https://stag.example.com", "description": "Staging environment"},
{"url": "https://prod.example.com", "description": "Production environment"},
],
root_path="/api/v1",
)
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
================================================
FILE: docs_src/behind_a_proxy/tutorial004_py310.py
================================================
from fastapi import FastAPI, Request
app = FastAPI(
servers=[
{"url": "https://stag.example.com", "description": "Staging environment"},
{"url": "https://prod.example.com", "description": "Production environment"},
],
root_path="/api/v1",
root_path_in_servers=False,
)
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
================================================
FILE: docs_src/bigger_applications/__init__.py
================================================
================================================
FILE: docs_src/bigger_applications/app_an_py310/__init__.py
================================================
================================================
FILE: docs_src/bigger_applications/app_an_py310/dependencies.py
================================================
from typing import Annotated
from fastapi import Header, HTTPException
async def get_token_header(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def get_query_token(token: str):
if token != "jessica":
raise HTTPException(status_code=400, detail="No Jessica token provided")
================================================
FILE: docs_src/bigger_applications/app_an_py310/internal/__init__.py
================================================
================================================
FILE: docs_src/bigger_applications/app_an_py310/internal/admin.py
================================================
from fastapi import APIRouter
router = APIRouter()
@router.post("/")
async def update_admin():
return {"message": "Admin getting schwifty"}
================================================
FILE: docs_src/bigger_applications/app_an_py310/main.py
================================================
from fastapi import Depends, FastAPI
from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users
app = FastAPI(dependencies=[Depends(get_query_token)])
app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}
================================================
FILE: docs_src/bigger_applications/app_an_py310/routers/__init__.py
================================================
================================================
FILE: docs_src/bigger_applications/app_an_py310/routers/items.py
================================================
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_token_header
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
@router.get("/")
async def read_items():
return fake_items_db
@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}
================================================
FILE: docs_src/bigger_applications/app_an_py310/routers/users.py
================================================
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}
================================================
FILE: docs_src/body/__init__.py
================================================
================================================
FILE: docs_src/body/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
================================================
FILE: docs_src/body/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.model_dump()
if item.tax is not None:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
================================================
FILE: docs_src/body/tutorial003_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.model_dump()}
================================================
FILE: docs_src/body/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.model_dump()}
if q:
result.update({"q": q})
return result
================================================
FILE: docs_src/body_fields/__init__.py
================================================
================================================
FILE: docs_src/body_fields/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_fields/tutorial001_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_multiple_params/__init__.py
================================================
================================================
FILE: docs_src/body_multiple_params/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str | None = None,
item: Item | None = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results
================================================
FILE: docs_src/body_multiple_params/tutorial001_py310.py
================================================
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
q: str | None = None,
item: Item | None = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results
================================================
FILE: docs_src/body_multiple_params/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
results = {"item_id": item_id, "item": item, "user": user}
return results
================================================
FILE: docs_src/body_multiple_params/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
================================================
FILE: docs_src/body_multiple_params/tutorial003_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: int = Body()):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
================================================
FILE: docs_src/body_multiple_params/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: str | None = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/body_multiple_params/tutorial004_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: int = Body(gt=0),
q: str | None = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/body_multiple_params/tutorial005_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_multiple_params/tutorial005_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/__init__.py
================================================
================================================
FILE: docs_src/body_nested_models/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial003_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial005_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial006_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/body_nested_models/tutorial007_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
class Offer(BaseModel):
name: str
description: str | None = None
price: float
items: list[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
================================================
FILE: docs_src/body_nested_models/tutorial008_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
return images
================================================
FILE: docs_src/body_nested_models/tutorial009_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
return weights
================================================
FILE: docs_src/body_updates/__init__.py
================================================
================================================
FILE: docs_src/body_updates/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded
return update_item_encoded
================================================
FILE: docs_src/body_updates/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.patch("/items/{item_id}")
async def update_item(item_id: str, item: Item) -> Item:
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.model_dump(exclude_unset=True)
updated_item = stored_item_model.model_copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item
================================================
FILE: docs_src/conditional_openapi/__init__.py
================================================
================================================
FILE: docs_src/conditional_openapi/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
openapi_url: str = "/openapi.json"
settings = Settings()
app = FastAPI(openapi_url=settings.openapi_url)
@app.get("/")
def root():
return {"message": "Hello World"}
================================================
FILE: docs_src/configure_swagger_ui/__init__.py
================================================
================================================
FILE: docs_src/configure_swagger_ui/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI(swagger_ui_parameters={"syntaxHighlight": False})
@app.get("/users/{username}")
async def read_user(username: str):
return {"message": f"Hello {username}"}
================================================
FILE: docs_src/configure_swagger_ui/tutorial002_py310.py
================================================
from fastapi import FastAPI
app = FastAPI(swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}})
@app.get("/users/{username}")
async def read_user(username: str):
return {"message": f"Hello {username}"}
================================================
FILE: docs_src/configure_swagger_ui/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI(swagger_ui_parameters={"deepLinking": False})
@app.get("/users/{username}")
async def read_user(username: str):
return {"message": f"Hello {username}"}
================================================
FILE: docs_src/cookie_param_models/__init__.py
================================================
================================================
FILE: docs_src/cookie_param_models/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
return cookies
================================================
FILE: docs_src/cookie_param_models/tutorial001_py310.py
================================================
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Cookies = Cookie()):
return cookies
================================================
FILE: docs_src/cookie_param_models/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
model_config = {"extra": "forbid"}
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
return cookies
================================================
FILE: docs_src/cookie_param_models/tutorial002_py310.py
================================================
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
model_config = {"extra": "forbid"}
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Cookies = Cookie()):
return cookies
================================================
FILE: docs_src/cookie_params/__init__.py
================================================
================================================
FILE: docs_src/cookie_params/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}
================================================
FILE: docs_src/cookie_params/tutorial001_py310.py
================================================
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: str | None = Cookie(default=None)):
return {"ads_id": ads_id}
================================================
FILE: docs_src/cors/__init__.py
================================================
================================================
FILE: docs_src/cors/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
async def main():
return {"message": "Hello World"}
================================================
FILE: docs_src/custom_docs_ui/__init__.py
================================================
================================================
FILE: docs_src/custom_docs_ui/tutorial001_py310.py
================================================
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@2/bundles/redoc.standalone.js",
)
@app.get("/users/{username}")
async def read_user(username: str):
return {"message": f"Hello {username}"}
================================================
FILE: docs_src/custom_docs_ui/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles
app = FastAPI(docs_url=None, redoc_url=None)
app.mount("/static", StaticFiles(directory="static"), name="static")
@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="/static/swagger-ui-bundle.js",
swagger_css_url="/static/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="/static/redoc.standalone.js",
)
@app.get("/users/{username}")
async def read_user(username: str):
return {"message": f"Hello {username}"}
================================================
FILE: docs_src/custom_request_and_route/__init__.py
================================================
================================================
FILE: docs_src/custom_request_and_route/tutorial001_an_py310.py
================================================
import gzip
from collections.abc import Callable
from typing import Annotated
from fastapi import Body, FastAPI, Request, Response
from fastapi.routing import APIRoute
class GzipRequest(Request):
async def body(self) -> bytes:
if not hasattr(self, "_body"):
body = await super().body()
if "gzip" in self.headers.getlist("Content-Encoding"):
body = gzip.decompress(body)
self._body = body
return self._body
class GzipRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
request = GzipRequest(request.scope, request.receive)
return await original_route_handler(request)
return custom_route_handler
app = FastAPI()
app.router.route_class = GzipRoute
@app.post("/sum")
async def sum_numbers(numbers: Annotated[list[int], Body()]):
return {"sum": sum(numbers)}
================================================
FILE: docs_src/custom_request_and_route/tutorial001_py310.py
================================================
import gzip
from collections.abc import Callable
from fastapi import Body, FastAPI, Request, Response
from fastapi.routing import APIRoute
class GzipRequest(Request):
async def body(self) -> bytes:
if not hasattr(self, "_body"):
body = await super().body()
if "gzip" in self.headers.getlist("Content-Encoding"):
body = gzip.decompress(body)
self._body = body
return self._body
class GzipRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
request = GzipRequest(request.scope, request.receive)
return await original_route_handler(request)
return custom_route_handler
app = FastAPI()
app.router.route_class = GzipRoute
@app.post("/sum")
async def sum_numbers(numbers: list[int] = Body()):
return {"sum": sum(numbers)}
================================================
FILE: docs_src/custom_request_and_route/tutorial002_an_py310.py
================================================
from collections.abc import Callable
from typing import Annotated
from fastapi import Body, FastAPI, HTTPException, Request, Response
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
class ValidationErrorLoggingRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
try:
return await original_route_handler(request)
except RequestValidationError as exc:
body = await request.body()
detail = {"errors": exc.errors(), "body": body.decode()}
raise HTTPException(status_code=422, detail=detail)
return custom_route_handler
app = FastAPI()
app.router.route_class = ValidationErrorLoggingRoute
@app.post("/")
async def sum_numbers(numbers: Annotated[list[int], Body()]):
return sum(numbers)
================================================
FILE: docs_src/custom_request_and_route/tutorial002_py310.py
================================================
from collections.abc import Callable
from fastapi import Body, FastAPI, HTTPException, Request, Response
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
class ValidationErrorLoggingRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
try:
return await original_route_handler(request)
except RequestValidationError as exc:
body = await request.body()
detail = {"errors": exc.errors(), "body": body.decode()}
raise HTTPException(status_code=422, detail=detail)
return custom_route_handler
app = FastAPI()
app.router.route_class = ValidationErrorLoggingRoute
@app.post("/")
async def sum_numbers(numbers: list[int] = Body()):
return sum(numbers)
================================================
FILE: docs_src/custom_request_and_route/tutorial003_py310.py
================================================
import time
from collections.abc import Callable
from fastapi import APIRouter, FastAPI, Request, Response
from fastapi.routing import APIRoute
class TimedRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
before = time.time()
response: Response = await original_route_handler(request)
duration = time.time() - before
response.headers["X-Response-Time"] = str(duration)
print(f"route duration: {duration}")
print(f"route response: {response}")
print(f"route response headers: {response.headers}")
return response
return custom_route_handler
app = FastAPI()
router = APIRouter(route_class=TimedRoute)
@app.get("/")
async def not_timed():
return {"message": "Not timed"}
@router.get("/timed")
async def timed():
return {"message": "It's the time of my life"}
app.include_router(router)
================================================
FILE: docs_src/custom_response/__init__.py
================================================
================================================
FILE: docs_src/custom_response/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]
================================================
FILE: docs_src/custom_response/tutorial001b_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
app = FastAPI()
@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
return ORJSONResponse([{"item_id": "Foo"}])
================================================
FILE: docs_src/custom_response/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
Some HTML in here
Look ma! HTML!
"""
================================================
FILE: docs_src/custom_response/tutorial003_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/")
async def read_items():
html_content = """
Some HTML in here
Look ma! HTML!
"""
return HTMLResponse(content=html_content, status_code=200)
================================================
FILE: docs_src/custom_response/tutorial004_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
def generate_html_response():
html_content = """
Some HTML in here
Look ma! HTML!
"""
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()
================================================
FILE: docs_src/custom_response/tutorial005_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
================================================
FILE: docs_src/custom_response/tutorial006_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/typer")
async def redirect_typer():
return RedirectResponse("https://typer.tiangolo.com")
================================================
FILE: docs_src/custom_response/tutorial006b_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
return "https://fastapi.tiangolo.com"
================================================
FILE: docs_src/custom_response/tutorial006c_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
return "https://docs.pydantic.dev/"
================================================
FILE: docs_src/custom_response/tutorial007_py310.py
================================================
import anyio
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
await anyio.sleep(0)
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
================================================
FILE: docs_src/custom_response/tutorial008_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
def main():
def iterfile(): # (1)
with open(some_file_path, mode="rb") as file_like: # (2)
yield from file_like # (3)
return StreamingResponse(iterfile(), media_type="video/mp4")
================================================
FILE: docs_src/custom_response/tutorial009_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(some_file_path)
================================================
FILE: docs_src/custom_response/tutorial009b_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/", response_class=FileResponse)
async def main():
return some_file_path
================================================
FILE: docs_src/custom_response/tutorial009c_py310.py
================================================
from typing import Any
import orjson
from fastapi import FastAPI, Response
app = FastAPI()
class CustomORJSONResponse(Response):
media_type = "application/json"
def render(self, content: Any) -> bytes:
assert orjson is not None, "orjson must be installed"
return orjson.dumps(content, option=orjson.OPT_INDENT_2)
@app.get("/", response_class=CustomORJSONResponse)
async def main():
return {"message": "Hello World"}
================================================
FILE: docs_src/custom_response/tutorial010_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI(default_response_class=HTMLResponse)
@app.get("/items/")
async def read_items():
return "
Items
This is a list of items.
"
================================================
FILE: docs_src/dataclasses_/__init__.py
================================================
================================================
FILE: docs_src/dataclasses_/tutorial001_py310.py
================================================
from dataclasses import dataclass
from fastapi import FastAPI
@dataclass
class Item:
name: str
price: float
description: str | None = None
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
================================================
FILE: docs_src/dataclasses_/tutorial002_py310.py
================================================
from dataclasses import dataclass, field
from fastapi import FastAPI
@dataclass
class Item:
name: str
price: float
tags: list[str] = field(default_factory=list)
description: str | None = None
tax: float | None = None
app = FastAPI()
@app.get("/items/next", response_model=Item)
async def read_next_item():
return {
"name": "Island In The Moon",
"price": 12.99,
"description": "A place to be playin' and havin' fun",
"tags": ["breater"],
}
================================================
FILE: docs_src/dataclasses_/tutorial003_py310.py
================================================
from dataclasses import field # (1)
from fastapi import FastAPI
from pydantic.dataclasses import dataclass # (2)
@dataclass
class Item:
name: str
description: str | None = None
@dataclass
class Author:
name: str
items: list[Item] = field(default_factory=list) # (3)
app = FastAPI()
@app.post("/authors/{author_id}/items/", response_model=Author) # (4)
async def create_author_items(author_id: str, items: list[Item]): # (5)
return {"name": author_id, "items": items} # (6)
@app.get("/authors/", response_model=list[Author]) # (7)
def get_authors(): # (8)
return [ # (9)
{
"name": "Breaters",
"items": [
{
"name": "Island In The Moon",
"description": "A place to be playin' and havin' fun",
},
{"name": "Holy Buddies"},
],
},
{
"name": "System of an Up",
"items": [
{
"name": "Salt",
"description": "The kombucha mushroom people's favorite",
},
{"name": "Pad Thai"},
{
"name": "Lonely Night",
"description": "The mostests lonliest nightiest of allest",
},
],
},
]
================================================
FILE: docs_src/debugging/__init__.py
================================================
================================================
FILE: docs_src/debugging/tutorial001_py310.py
================================================
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
a = "a"
b = "b" + a
return {"hello world": b}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
================================================
FILE: docs_src/dependencies/__init__.py
================================================
================================================
FILE: docs_src/dependencies/tutorial001_02_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonsDep):
return commons
@app.get("/users/")
async def read_users(commons: CommonsDep):
return commons
================================================
FILE: docs_src/dependencies/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons
================================================
FILE: docs_src/dependencies/tutorial001_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return commons
================================================
FILE: docs_src/dependencies/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial002_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial003_an_py310.py
================================================
from typing import Annotated, Any
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial003_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons=Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial004_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends()):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
================================================
FILE: docs_src/dependencies/tutorial005_an_py310.py
================================================
from typing import Annotated
from fastapi import Cookie, Depends, FastAPI
app = FastAPI()
def query_extractor(q: str | None = None):
return q
def query_or_cookie_extractor(
q: Annotated[str, Depends(query_extractor)],
last_query: Annotated[str | None, Cookie()] = None,
):
if not q:
return last_query
return q
@app.get("/items/")
async def read_query(
query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
return {"q_or_cookie": query_or_default}
================================================
FILE: docs_src/dependencies/tutorial005_py310.py
================================================
from fastapi import Cookie, Depends, FastAPI
app = FastAPI()
def query_extractor(q: str | None = None):
return q
def query_or_cookie_extractor(
q: str = Depends(query_extractor), last_query: str | None = Cookie(default=None)
):
if not q:
return last_query
return q
@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
return {"q_or_cookie": query_or_default}
================================================
FILE: docs_src/dependencies/tutorial006_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]
================================================
FILE: docs_src/dependencies/tutorial006_py310.py
================================================
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
async def verify_token(x_token: str = Header()):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: str = Header()):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]
================================================
FILE: docs_src/dependencies/tutorial007_py310.py
================================================
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
================================================
FILE: docs_src/dependencies/tutorial008_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
================================================
FILE: docs_src/dependencies/tutorial008_py310.py
================================================
from fastapi import Depends
async def dependency_a():
dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()
async def dependency_b(dep_a=Depends(dependency_a)):
dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)
async def dependency_c(dep_b=Depends(dependency_b)):
dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)
================================================
FILE: docs_src/dependencies/tutorial008b_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item
================================================
FILE: docs_src/dependencies/tutorial008b_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}
class OwnerError(Exception):
pass
def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item
================================================
FILE: docs_src/dependencies/tutorial008c_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("Oops, we didn't raise again, Britney 😱")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
================================================
FILE: docs_src/dependencies/tutorial008c_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("Oops, we didn't raise again, Britney 😱")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
================================================
FILE: docs_src/dependencies/tutorial008d_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("We don't swallow the internal error here, we raise again 😎")
raise
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
================================================
FILE: docs_src/dependencies/tutorial008d_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
class InternalError(Exception):
pass
def get_username():
try:
yield "Rick"
except InternalError:
print("We don't swallow the internal error here, we raise again 😎")
raise
@app.get("/items/{item_id}")
def get_item(item_id: str, username: str = Depends(get_username)):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id
================================================
FILE: docs_src/dependencies/tutorial008e_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_username():
try:
yield "Rick"
finally:
print("Cleanup up before response is sent")
@app.get("/users/me")
def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
return username
================================================
FILE: docs_src/dependencies/tutorial008e_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
def get_username():
try:
yield "Rick"
finally:
print("Cleanup up before response is sent")
@app.get("/users/me")
def get_user_me(username: str = Depends(get_username, scope="function")):
return username
================================================
FILE: docs_src/dependencies/tutorial010_py310.py
================================================
class MySuperContextManager:
def __init__(self):
self.db = DBSession()
def __enter__(self):
return self.db
def __exit__(self, exc_type, exc_value, traceback):
self.db.close()
async def get_db():
with MySuperContextManager() as db:
yield db
================================================
FILE: docs_src/dependencies/tutorial011_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
class FixedContentQueryChecker:
def __init__(self, fixed_content: str):
self.fixed_content = fixed_content
def __call__(self, q: str = ""):
if q:
return self.fixed_content in q
return False
checker = FixedContentQueryChecker("bar")
@app.get("/query-checker/")
async def read_query_check(fixed_content_included: Annotated[bool, Depends(checker)]):
return {"fixed_content_in_query": fixed_content_included}
================================================
FILE: docs_src/dependencies/tutorial011_py310.py
================================================
from fastapi import Depends, FastAPI
app = FastAPI()
class FixedContentQueryChecker:
def __init__(self, fixed_content: str):
self.fixed_content = fixed_content
def __call__(self, q: str = ""):
if q:
return self.fixed_content in q
return False
checker = FixedContentQueryChecker("bar")
@app.get("/query-checker/")
async def read_query_check(fixed_content_included: bool = Depends(checker)):
return {"fixed_content_in_query": fixed_content_included}
================================================
FILE: docs_src/dependencies/tutorial012_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
@app.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
================================================
FILE: docs_src/dependencies/tutorial012_py310.py
================================================
from fastapi import Depends, FastAPI, Header, HTTPException
async def verify_token(x_token: str = Header()):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: str = Header()):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
@app.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
================================================
FILE: docs_src/dependencies/tutorial013_an_py310.py
================================================
import time
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from sqlmodel import Field, Session, SQLModel, create_engine
engine = create_engine("postgresql+psycopg://postgres:postgres@localhost/db")
class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
app = FastAPI()
def get_session():
with Session(engine) as session:
yield session
def get_user(user_id: int, session: Annotated[Session, Depends(get_session)]):
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=403, detail="Not authorized")
def generate_stream(query: str):
for ch in query:
yield ch
time.sleep(0.1)
@app.get("/generate", dependencies=[Depends(get_user)])
def generate(query: str):
return StreamingResponse(content=generate_stream(query))
================================================
FILE: docs_src/dependencies/tutorial014_an_py310.py
================================================
import time
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from sqlmodel import Field, Session, SQLModel, create_engine
engine = create_engine("postgresql+psycopg://postgres:postgres@localhost/db")
class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
app = FastAPI()
def get_session():
with Session(engine) as session:
yield session
def get_user(user_id: int, session: Annotated[Session, Depends(get_session)]):
user = session.get(User, user_id)
if not user:
raise HTTPException(status_code=403, detail="Not authorized")
session.close()
def generate_stream(query: str):
for ch in query:
yield ch
time.sleep(0.1)
@app.get("/generate", dependencies=[Depends(get_user)])
def generate(query: str):
return StreamingResponse(content=generate_stream(query))
================================================
FILE: docs_src/dependency_testing/__init__.py
================================================
================================================
FILE: docs_src/dependency_testing/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return {"message": "Hello Items!", "params": commons}
@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return {"message": "Hello Users!", "params": commons}
client = TestClient(app)
async def override_dependency(q: str | None = None):
return {"q": q, "skip": 5, "limit": 10}
app.dependency_overrides[common_parameters] = override_dependency
def test_override_in_items():
response = client.get("/items/")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": None, "skip": 5, "limit": 10},
}
def test_override_in_items_with_q():
response = client.get("/items/?q=foo")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": "foo", "skip": 5, "limit": 10},
}
def test_override_in_items_with_params():
response = client.get("/items/?q=foo&skip=100&limit=200")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": "foo", "skip": 5, "limit": 10},
}
================================================
FILE: docs_src/dependency_testing/tutorial001_py310.py
================================================
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return {"message": "Hello Items!", "params": commons}
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return {"message": "Hello Users!", "params": commons}
client = TestClient(app)
async def override_dependency(q: str | None = None):
return {"q": q, "skip": 5, "limit": 10}
app.dependency_overrides[common_parameters] = override_dependency
def test_override_in_items():
response = client.get("/items/")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": None, "skip": 5, "limit": 10},
}
def test_override_in_items_with_q():
response = client.get("/items/?q=foo")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": "foo", "skip": 5, "limit": 10},
}
def test_override_in_items_with_params():
response = client.get("/items/?q=foo&skip=100&limit=200")
assert response.status_code == 200
assert response.json() == {
"message": "Hello Items!",
"params": {"q": "foo", "skip": 5, "limit": 10},
}
================================================
FILE: docs_src/encoder/__init__.py
================================================
================================================
FILE: docs_src/encoder/tutorial001_py310.py
================================================
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime
description: str | None = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data
================================================
FILE: docs_src/events/__init__.py
================================================
================================================
FILE: docs_src/events/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
================================================
FILE: docs_src/events/tutorial002_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.on_event("shutdown")
def shutdown_event():
with open("log.txt", mode="a") as log:
log.write("Application shutdown")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
================================================
FILE: docs_src/events/tutorial003_py310.py
================================================
from contextlib import asynccontextmanager
from fastapi import FastAPI
def fake_answer_to_everything_ml_model(x: float):
return x * 42
ml_models = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
# Load the ML model
ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model
yield
# Clean up the ML models and release the resources
ml_models.clear()
app = FastAPI(lifespan=lifespan)
@app.get("/predict")
async def predict(x: float):
result = ml_models["answer_to_everything"](x)
return {"result": result}
================================================
FILE: docs_src/extending_openapi/__init__.py
================================================
================================================
FILE: docs_src/extending_openapi/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
summary="This is a very custom OpenAPI schema",
description="Here's a longer description of the custom **OpenAPI** schema",
routes=app.routes,
)
openapi_schema["info"]["x-logo"] = {
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
}
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
================================================
FILE: docs_src/extra_data_types/__init__.py
================================================
================================================
FILE: docs_src/extra_data_types/tutorial001_an_py310.py
================================================
from datetime import datetime, time, timedelta
from typing import Annotated
from uuid import UUID
from fastapi import Body, FastAPI
app = FastAPI()
@app.put("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: Annotated[datetime, Body()],
end_datetime: Annotated[datetime, Body()],
process_after: Annotated[timedelta, Body()],
repeat_at: Annotated[time | None, Body()] = None,
):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}
================================================
FILE: docs_src/extra_data_types/tutorial001_py310.py
================================================
from datetime import datetime, time, timedelta
from uuid import UUID
from fastapi import Body, FastAPI
app = FastAPI()
@app.put("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: datetime = Body(),
end_datetime: datetime = Body(),
process_after: timedelta = Body(),
repeat_at: time | None = Body(default=None),
):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}
================================================
FILE: docs_src/extra_models/__init__.py
================================================
================================================
FILE: docs_src/extra_models/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
class UserInDB(BaseModel):
username: str
hashed_password: str
email: EmailStr
full_name: str | None = None
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
================================================
FILE: docs_src/extra_models/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
class UserIn(UserBase):
password: str
class UserOut(UserBase):
pass
class UserInDB(UserBase):
hashed_password: str
def fake_password_hasher(raw_password: str):
return "supersecret" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
================================================
FILE: docs_src/extra_models/tutorial003_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class BaseItem(BaseModel):
description: str
type: str
class CarItem(BaseItem):
type: str = "car"
class PlaneItem(BaseItem):
type: str = "plane"
size: int
items = {
"item1": {"description": "All my friends drive a low rider", "type": "car"},
"item2": {
"description": "Music is my aeroplane, it's my aeroplane",
"type": "plane",
"size": 5,
},
}
@app.get("/items/{item_id}", response_model=PlaneItem | CarItem)
async def read_item(item_id: str):
return items[item_id]
================================================
FILE: docs_src/extra_models/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str
items = [
{"name": "Foo", "description": "There comes my hero"},
{"name": "Red", "description": "It's my aeroplane"},
]
@app.get("/items/", response_model=list[Item])
async def read_items():
return items
================================================
FILE: docs_src/extra_models/tutorial005_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/keyword-weights/", response_model=dict[str, float])
async def read_keyword_weights():
return {"foo": 2.3, "bar": 3.4}
================================================
FILE: docs_src/first_steps/__init__.py
================================================
================================================
FILE: docs_src/first_steps/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
================================================
FILE: docs_src/first_steps/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Hello World"}
================================================
FILE: docs_src/generate_clients/__init__.py
================================================
================================================
FILE: docs_src/generate_clients/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
@app.post("/items/", response_model=ResponseMessage)
async def create_item(item: Item):
return {"message": "item received"}
@app.get("/items/", response_model=list[Item])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
================================================
FILE: docs_src/generate_clients/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
================================================
FILE: docs_src/generate_clients/tutorial003_py310.py
================================================
from fastapi import FastAPI
from fastapi.routing import APIRoute
from pydantic import BaseModel
def custom_generate_unique_id(route: APIRoute):
return f"{route.tags[0]}-{route.name}"
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
class Item(BaseModel):
name: str
price: float
class ResponseMessage(BaseModel):
message: str
class User(BaseModel):
username: str
email: str
@app.post("/items/", response_model=ResponseMessage, tags=["items"])
async def create_item(item: Item):
return {"message": "Item received"}
@app.get("/items/", response_model=list[Item], tags=["items"])
async def get_items():
return [
{"name": "Plumbus", "price": 3},
{"name": "Portal Gun", "price": 9001},
]
@app.post("/users/", response_model=ResponseMessage, tags=["users"])
async def create_user(user: User):
return {"message": "User received"}
================================================
FILE: docs_src/generate_clients/tutorial004.js
================================================
import * as fs from 'fs'
async function modifyOpenAPIFile(filePath) {
try {
const data = await fs.promises.readFile(filePath)
const openapiContent = JSON.parse(data)
const paths = openapiContent.paths
for (const pathKey of Object.keys(paths)) {
const pathData = paths[pathKey]
for (const method of Object.keys(pathData)) {
const operation = pathData[method]
if (operation.tags && operation.tags.length > 0) {
const tag = operation.tags[0]
const operationId = operation.operationId
const toRemove = `${tag}-`
if (operationId.startsWith(toRemove)) {
const newOperationId = operationId.substring(toRemove.length)
operation.operationId = newOperationId
}
}
}
}
await fs.promises.writeFile(
filePath,
JSON.stringify(openapiContent, null, 2),
)
console.log('File successfully modified')
} catch (err) {
console.error('Error:', err)
}
}
const filePath = './openapi.json'
modifyOpenAPIFile(filePath)
================================================
FILE: docs_src/generate_clients/tutorial004_py310.py
================================================
import json
from pathlib import Path
file_path = Path("./openapi.json")
openapi_content = json.loads(file_path.read_text())
for path_data in openapi_content["paths"].values():
for operation in path_data.values():
tag = operation["tags"][0]
operation_id = operation["operationId"]
to_remove = f"{tag}-"
new_operation_id = operation_id[len(to_remove) :]
operation["operationId"] = new_operation_id
file_path.write_text(json.dumps(openapi_content))
================================================
FILE: docs_src/graphql_/__init__.py
================================================
================================================
FILE: docs_src/graphql_/tutorial001_py310.py
================================================
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
@strawberry.type
class User:
name: str
age: int
@strawberry.type
class Query:
@strawberry.field
def user(self) -> User:
return User(name="Patrick", age=100)
schema = strawberry.Schema(query=Query)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")
================================================
FILE: docs_src/handling_errors/__init__.py
================================================
================================================
FILE: docs_src/handling_errors/tutorial001_py310.py
================================================
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
================================================
FILE: docs_src/handling_errors/tutorial002_py310.py
================================================
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "There goes my error"},
)
return {"item": items[item_id]}
================================================
FILE: docs_src/handling_errors/tutorial003_py310.py
================================================
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
================================================
FILE: docs_src/handling_errors/tutorial004_py310.py
================================================
from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc: RequestValidationError):
message = "Validation errors:"
for error in exc.errors():
message += f"\nField: {error['loc']}, Error: {error['msg']}"
return PlainTextResponse(message, status_code=400)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}
================================================
FILE: docs_src/handling_errors/tutorial005_py310.py
================================================
from fastapi import FastAPI, Request
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)
class Item(BaseModel):
title: str
size: int
@app.post("/items/")
async def create_item(item: Item):
return item
================================================
FILE: docs_src/handling_errors/tutorial006_py310.py
================================================
from fastapi import FastAPI, HTTPException
from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
app = FastAPI()
@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"OMG! An HTTP error!: {repr(exc)}")
return await http_exception_handler(request, exc)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
print(f"OMG! The client sent invalid data!: {exc}")
return await request_validation_exception_handler(request, exc)
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}
================================================
FILE: docs_src/header_param_models/__init__.py
================================================
================================================
FILE: docs_src/header_param_models/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: Annotated[CommonHeaders, Header()]):
return headers
================================================
FILE: docs_src/header_param_models/tutorial001_py310.py
================================================
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: CommonHeaders = Header()):
return headers
================================================
FILE: docs_src/header_param_models/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
model_config = {"extra": "forbid"}
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: Annotated[CommonHeaders, Header()]):
return headers
================================================
FILE: docs_src/header_param_models/tutorial002_py310.py
================================================
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
model_config = {"extra": "forbid"}
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: CommonHeaders = Header()):
return headers
================================================
FILE: docs_src/header_param_models/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(
headers: Annotated[CommonHeaders, Header(convert_underscores=False)],
):
return headers
================================================
FILE: docs_src/header_param_models/tutorial003_py310.py
================================================
from fastapi import FastAPI, Header
from pydantic import BaseModel
app = FastAPI()
class CommonHeaders(BaseModel):
host: str
save_data: bool
if_modified_since: str | None = None
traceparent: str | None = None
x_tag: list[str] = []
@app.get("/items/")
async def read_items(headers: CommonHeaders = Header(convert_underscores=False)):
return headers
================================================
FILE: docs_src/header_params/__init__.py
================================================
================================================
FILE: docs_src/header_params/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):
return {"User-Agent": user_agent}
================================================
FILE: docs_src/header_params/tutorial001_py310.py
================================================
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(user_agent: str | None = Header(default=None)):
return {"User-Agent": user_agent}
================================================
FILE: docs_src/header_params/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(
strange_header: Annotated[str | None, Header(convert_underscores=False)] = None,
):
return {"strange_header": strange_header}
================================================
FILE: docs_src/header_params/tutorial002_py310.py
================================================
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(
strange_header: str | None = Header(default=None, convert_underscores=False),
):
return {"strange_header": strange_header}
================================================
FILE: docs_src/header_params/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(x_token: Annotated[list[str] | None, Header()] = None):
return {"X-Token values": x_token}
================================================
FILE: docs_src/header_params/tutorial003_py310.py
================================================
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(x_token: list[str] | None = Header(default=None)):
return {"X-Token values": x_token}
================================================
FILE: docs_src/json_base64_bytes/__init__.py
================================================
================================================
FILE: docs_src/json_base64_bytes/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class DataInput(BaseModel):
description: str
data: bytes
model_config = {"val_json_bytes": "base64"}
class DataOutput(BaseModel):
description: str
data: bytes
model_config = {"ser_json_bytes": "base64"}
class DataInputOutput(BaseModel):
description: str
data: bytes
model_config = {
"val_json_bytes": "base64",
"ser_json_bytes": "base64",
}
app = FastAPI()
@app.post("/data")
def post_data(body: DataInput):
content = body.data.decode("utf-8")
return {"description": body.description, "content": content}
@app.get("/data")
def get_data() -> DataOutput:
data = "hello".encode("utf-8")
return DataOutput(description="A plumbus", data=data)
@app.post("/data-in-out")
def post_data_in_out(body: DataInputOutput) -> DataInputOutput:
return body
================================================
FILE: docs_src/metadata/__init__.py
================================================
================================================
FILE: docs_src/metadata/tutorial001_1_py310.py
================================================
from fastapi import FastAPI
description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items
You can **read items**.
## Users
You will be able to:
* **Create users** (_not implemented_).
* **Read users** (_not implemented_).
"""
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "dp@x-force.example.com",
},
license_info={
"name": "Apache 2.0",
"identifier": "Apache-2.0",
},
)
@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]
================================================
FILE: docs_src/metadata/tutorial001_py310.py
================================================
from fastapi import FastAPI
description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items
You can **read items**.
## Users
You will be able to:
* **Create users** (_not implemented_).
* **Read users** (_not implemented_).
"""
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "dp@x-force.example.com",
},
license_info={
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
},
)
@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]
================================================
FILE: docs_src/metadata/tutorial002_py310.py
================================================
from fastapi import FastAPI
app = FastAPI(openapi_url="/api/v1/openapi.json")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
================================================
FILE: docs_src/metadata/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI(docs_url="/documentation", redoc_url=None)
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
================================================
FILE: docs_src/metadata/tutorial004_py310.py
================================================
from fastapi import FastAPI
tags_metadata = [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}, {"name": "Ron"}]
@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "wand"}, {"name": "flying broom"}]
================================================
FILE: docs_src/middleware/__init__.py
================================================
================================================
FILE: docs_src/middleware/tutorial001_py310.py
================================================
import time
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.perf_counter()
response = await call_next(request)
process_time = time.perf_counter() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
================================================
FILE: docs_src/openapi_callbacks/__init__.py
================================================
================================================
FILE: docs_src/openapi_callbacks/tutorial001_py310.py
================================================
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Invoice(BaseModel):
id: str
title: str | None = None
customer: str
total: float
class InvoiceEvent(BaseModel):
description: str
paid: bool
class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter()
@invoices_callback_router.post(
"{$callback_url}/invoices/{$request.body.id}", response_model=InvoiceEventReceived
)
def invoice_notification(body: InvoiceEvent):
pass
@app.post("/invoices/", callbacks=invoices_callback_router.routes)
def create_invoice(invoice: Invoice, callback_url: HttpUrl | None = None):
"""
Create an invoice.
This will (let's imagine) let the API user (some external developer) create an
invoice.
And this path operation will:
* Send the invoice to the client.
* Collect the money from the client.
* Send a notification back to the API user (the external developer), as a callback.
* At this point is that the API will somehow send a POST request to the
external API with the notification of the invoice event
(e.g. "payment successful").
"""
# Send the invoice, collect the money, send the notification (the callback)
return {"msg": "Invoice received"}
================================================
FILE: docs_src/openapi_webhooks/__init__.py
================================================
================================================
FILE: docs_src/openapi_webhooks/tutorial001_py310.py
================================================
from datetime import datetime
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Subscription(BaseModel):
username: str
monthly_fee: float
start_date: datetime
@app.webhooks.post("new-subscription")
def new_subscription(body: Subscription):
"""
When a new user subscribes to your service we'll send you a POST request with this
data to the URL that you register for the event `new-subscription` in the dashboard.
"""
@app.get("/users/")
def read_users():
return ["Rick", "Morty"]
================================================
FILE: docs_src/path_operation_advanced_configuration/__init__.py
================================================
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", operation_id="some_specific_id_you_define")
async def read_items():
return [{"item_id": "Foo"}]
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial002_py310.py
================================================
from fastapi import FastAPI
from fastapi.routing import APIRoute
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
def use_route_names_as_operation_ids(app: FastAPI) -> None:
"""
Simplify operation IDs so that generated API clients have simpler function
names.
Should be called only after all routes have been added.
"""
for route in app.routes:
if isinstance(route, APIRoute):
route.operation_id = route.name # in this case, 'read_items'
use_route_names_as_operation_ids(app)
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", include_in_schema=False)
async def read_items():
return [{"item_id": "Foo"}]
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", summary="Create an item")
async def create_item(item: Item) -> Item:
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial005_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"})
async def read_items():
return [{"item_id": "portal-gun"}]
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial006_py310.py
================================================
from fastapi import FastAPI, Request
app = FastAPI()
def magic_data_reader(raw_body: bytes):
return {
"size": len(raw_body),
"content": {
"name": "Maaaagic",
"price": 42,
"description": "Just kiddin', no magic here. ✨",
},
}
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {
"application/json": {
"schema": {
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"description": {"type": "string"},
},
}
}
},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
data = magic_data_reader(raw_body)
return data
================================================
FILE: docs_src/path_operation_advanced_configuration/tutorial007_py310.py
================================================
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: list[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.model_validate(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors(include_url=False))
return item
================================================
FILE: docs_src/path_operation_configuration/__init__.py
================================================
================================================
FILE: docs_src/path_operation_configuration/tutorial001_py310.py
================================================
from fastapi import FastAPI, status
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item) -> Item:
return item
================================================
FILE: docs_src/path_operation_configuration/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", tags=["items"])
async def create_item(item: Item) -> Item:
return item
@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]
@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]
================================================
FILE: docs_src/path_operation_configuration/tutorial002b_py310.py
================================================
from enum import Enum
from fastapi import FastAPI
app = FastAPI()
class Tags(Enum):
items = "items"
users = "users"
@app.get("/items/", tags=[Tags.items])
async def get_items():
return ["Portal gun", "Plumbus"]
@app.get("/users/", tags=[Tags.users])
async def read_users():
return ["Rick", "Morty"]
================================================
FILE: docs_src/path_operation_configuration/tutorial003_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post(
"/items/",
summary="Create an item",
description="Create an item with all the information, name, description, price, tax and a set of unique tags",
)
async def create_item(item: Item) -> Item:
return item
================================================
FILE: docs_src/path_operation_configuration/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", summary="Create an item")
async def create_item(item: Item) -> Item:
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item
================================================
FILE: docs_src/path_operation_configuration/tutorial005_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post(
"/items/",
summary="Create an item",
response_description="The created item",
)
async def create_item(item: Item) -> Item:
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item
================================================
FILE: docs_src/path_operation_configuration/tutorial006_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]
@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]
@app.get("/elements/", tags=["items"], deprecated=True)
async def read_elements():
return [{"item_id": "Foo"}]
================================================
FILE: docs_src/path_params/__init__.py
================================================
================================================
FILE: docs_src/path_params/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
================================================
FILE: docs_src/path_params/tutorial002_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
================================================
FILE: docs_src/path_params/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}
================================================
FILE: docs_src/path_params/tutorial003b_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/users")
async def read_users():
return ["Rick", "Morty"]
@app.get("/users")
async def read_users2():
return ["Bean", "Elfo"]
================================================
FILE: docs_src/path_params/tutorial004_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
================================================
FILE: docs_src/path_params/tutorial005_py310.py
================================================
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
================================================
FILE: docs_src/path_params_numeric_validations/__init__.py
================================================
================================================
FILE: docs_src/path_params_numeric_validations/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial001_py310.py
================================================
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial002_py310.py
================================================
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial003_py310.py
================================================
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial004_py310.py
================================================
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial005_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial005_py310.py
================================================
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial006_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str,
size: Annotated[float, Query(gt=0, lt=10.5)],
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
================================================
FILE: docs_src/path_params_numeric_validations/tutorial006_py310.py
================================================
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
q: str,
size: float = Query(gt=0, lt=10.5),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
================================================
FILE: docs_src/pydantic_v1_in_v2/__init__.py
================================================
================================================
FILE: docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py
================================================
from pydantic.v1 import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
size: float
================================================
FILE: docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py
================================================
from fastapi import FastAPI
from pydantic.v1 import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
size: float
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item) -> Item:
return item
================================================
FILE: docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel as BaseModelV2
from pydantic.v1 import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
size: float
class ItemV2(BaseModelV2):
name: str
description: str | None = None
size: float
app = FastAPI()
@app.post("/items/", response_model=ItemV2)
async def create_item(item: Item):
return item
================================================
FILE: docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI
from fastapi.temp_pydantic_v1_params import Body
from pydantic.v1 import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
size: float
app = FastAPI()
@app.post("/items/")
async def create_item(item: Annotated[Item, Body(embed=True)]) -> Item:
return item
================================================
FILE: docs_src/python_types/__init__.py
================================================
================================================
FILE: docs_src/python_types/tutorial001_py310.py
================================================
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
================================================
FILE: docs_src/python_types/tutorial002_py310.py
================================================
def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
================================================
FILE: docs_src/python_types/tutorial003_py310.py
================================================
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
================================================
FILE: docs_src/python_types/tutorial004_py310.py
================================================
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + str(age)
return name_with_age
================================================
FILE: docs_src/python_types/tutorial005_py310.py
================================================
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_e
================================================
FILE: docs_src/python_types/tutorial006_py310.py
================================================
def process_items(items: list[str]):
for item in items:
print(item)
================================================
FILE: docs_src/python_types/tutorial007_py310.py
================================================
def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
return items_t, items_s
================================================
FILE: docs_src/python_types/tutorial008_py310.py
================================================
def process_items(prices: dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
================================================
FILE: docs_src/python_types/tutorial008b_py310.py
================================================
def process_item(item: int | str):
print(item)
================================================
FILE: docs_src/python_types/tutorial009_py310.py
================================================
def say_hi(name: str | None = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
================================================
FILE: docs_src/python_types/tutorial010_py310.py
================================================
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
================================================
FILE: docs_src/python_types/tutorial011_py310.py
================================================
from datetime import datetime
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = "John Doe"
signup_ts: datetime | None = None
friends: list[int] = []
external_data = {
"id": "123",
"signup_ts": "2017-06-01 12:22",
"friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
================================================
FILE: docs_src/python_types/tutorial013_py310.py
================================================
from typing import Annotated
def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
return f"Hello {name}"
================================================
FILE: docs_src/query_param_models/__init__.py
================================================
================================================
FILE: docs_src/query_param_models/tutorial001_an_py310.py
================================================
from typing import Annotated, Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
return filter_query
================================================
FILE: docs_src/query_param_models/tutorial001_py310.py
================================================
from typing import Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
return filter_query
================================================
FILE: docs_src/query_param_models/tutorial002_an_py310.py
================================================
from typing import Annotated, Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
model_config = {"extra": "forbid"}
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
return filter_query
================================================
FILE: docs_src/query_param_models/tutorial002_py310.py
================================================
from typing import Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
model_config = {"extra": "forbid"}
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
return filter_query
================================================
FILE: docs_src/query_params/__init__.py
================================================
================================================
FILE: docs_src/query_params/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
================================================
FILE: docs_src/query_params/tutorial002_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
================================================
FILE: docs_src/query_params/tutorial003_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None, short: bool = False):
item = {"item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
================================================
FILE: docs_src/query_params/tutorial004_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str | None = None, short: bool = False
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
================================================
FILE: docs_src/query_params/tutorial005_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item
================================================
FILE: docs_src/query_params/tutorial006_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(
item_id: str, needy: str, skip: int = 0, limit: int | None = None
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item
================================================
FILE: docs_src/query_params_str_validations/__init__.py
================================================
================================================
FILE: docs_src/query_params_str_validations/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial002_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial003_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial004_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial005_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial005_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial006_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial006_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial006c_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial006c_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial007_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial007_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(default=None, title="Query string", min_length=3),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial008_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial008_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial009_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial009_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial010_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial010_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None,
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
================================================
FILE: docs_src/query_params_str_validations/tutorial011_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial011_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] | None = Query(default=None)):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial012_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial012_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] = Query(default=["foo", "bar"])):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial013_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial013_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list = Query(default=[])):
query_items = {"q": q}
return query_items
================================================
FILE: docs_src/query_params_str_validations/tutorial014_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
================================================
FILE: docs_src/query_params_str_validations/tutorial014_py310.py
================================================
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: str | None = Query(default=None, include_in_schema=False),
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
================================================
FILE: docs_src/query_params_str_validations/tutorial015_an_py310.py
================================================
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
================================================
FILE: docs_src/request_files/__init__.py
================================================
================================================
FILE: docs_src/request_files/tutorial001_02_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial001_02_py310.py
================================================
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial001_03_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial001_03_py310.py
================================================
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File(description="A file read as bytes")):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: UploadFile = File(description="A file read as UploadFile"),
):
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial001_py310.py
================================================
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
================================================
FILE: docs_src/request_files/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: Annotated[list[bytes], File()]):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
"""
return HTMLResponse(content=content)
================================================
FILE: docs_src/request_files/tutorial002_py310.py
================================================
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: list[bytes] = File()):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
"""
return HTMLResponse(content=content)
================================================
FILE: docs_src/request_files/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: Annotated[list[bytes], File(description="Multiple files as bytes")],
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: Annotated[
list[UploadFile], File(description="Multiple files as UploadFile")
],
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
"""
return HTMLResponse(content=content)
================================================
FILE: docs_src/request_files/tutorial003_py310.py
================================================
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: list[bytes] = File(description="Multiple files as bytes"),
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
"""
return HTMLResponse(content=content)
================================================
FILE: docs_src/request_form_models/__init__.py
================================================
================================================
FILE: docs_src/request_form_models/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Form
from pydantic import BaseModel
app = FastAPI()
class FormData(BaseModel):
username: str
password: str
@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
================================================
FILE: docs_src/request_form_models/tutorial001_py310.py
================================================
from fastapi import FastAPI, Form
from pydantic import BaseModel
app = FastAPI()
class FormData(BaseModel):
username: str
password: str
@app.post("/login/")
async def login(data: FormData = Form()):
return data
================================================
FILE: docs_src/request_form_models/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Form
from pydantic import BaseModel
app = FastAPI()
class FormData(BaseModel):
username: str
password: str
model_config = {"extra": "forbid"}
@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
return data
================================================
FILE: docs_src/request_form_models/tutorial002_py310.py
================================================
from fastapi import FastAPI, Form
from pydantic import BaseModel
app = FastAPI()
class FormData(BaseModel):
username: str
password: str
model_config = {"extra": "forbid"}
@app.post("/login/")
async def login(data: FormData = Form()):
return data
================================================
FILE: docs_src/request_forms/__init__.py
================================================
================================================
FILE: docs_src/request_forms/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
return {"username": username}
================================================
FILE: docs_src/request_forms/tutorial001_py310.py
================================================
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: str = Form(), password: str = Form()):
return {"username": username}
================================================
FILE: docs_src/request_forms_and_files/__init__.py
================================================
================================================
FILE: docs_src/request_forms_and_files/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import FastAPI, File, Form, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(
file: Annotated[bytes, File()],
fileb: Annotated[UploadFile, File()],
token: Annotated[str, Form()],
):
return {
"file_size": len(file),
"token": token,
"fileb_content_type": fileb.content_type,
}
================================================
FILE: docs_src/request_forms_and_files/tutorial001_py310.py
================================================
from fastapi import FastAPI, File, Form, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(
file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):
return {
"file_size": len(file),
"token": token,
"fileb_content_type": fileb.content_type,
}
================================================
FILE: docs_src/response_change_status_code/__init__.py
================================================
================================================
FILE: docs_src/response_change_status_code/tutorial001_py310.py
================================================
from fastapi import FastAPI, Response, status
app = FastAPI()
tasks = {"foo": "Listen to the Bar Fighters"}
@app.put("/get-or-create-task/{task_id}", status_code=200)
def get_or_create_task(task_id: str, response: Response):
if task_id not in tasks:
tasks[task_id] = "This didn't exist before"
response.status_code = status.HTTP_201_CREATED
return tasks[task_id]
================================================
FILE: docs_src/response_cookies/__init__.py
================================================
================================================
FILE: docs_src/response_cookies/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/cookie/")
def create_cookie():
content = {"message": "Come to the dark side, we have cookies"}
response = JSONResponse(content=content)
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return response
================================================
FILE: docs_src/response_cookies/tutorial002_py310.py
================================================
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("/cookie-and-object/")
def create_cookie(response: Response):
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return {"message": "Come to the dark side, we have cookies"}
================================================
FILE: docs_src/response_directly/__init__.py
================================================
================================================
FILE: docs_src/response_directly/tutorial001_py310.py
================================================
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class Item(BaseModel):
title: str
timestamp: datetime
description: str | None = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
return JSONResponse(content=json_compatible_item_data)
================================================
FILE: docs_src/response_directly/tutorial002_py310.py
================================================
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """
Apply shampoo here.
You'll have to use soap here.
"""
return Response(content=data, media_type="application/xml")
================================================
FILE: docs_src/response_headers/__init__.py
================================================
================================================
FILE: docs_src/response_headers/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/headers/")
def get_headers():
content = {"message": "Hello World"}
headers = {"X-Cat-Dog": "alone in the world", "Content-Language": "en-US"}
return JSONResponse(content=content, headers=headers)
================================================
FILE: docs_src/response_headers/tutorial002_py310.py
================================================
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/headers-and-object/")
def get_headers(response: Response):
response.headers["X-Cat-Dog"] = "alone in the world"
return {"message": "Hello World"}
================================================
FILE: docs_src/response_model/__init__.py
================================================
================================================
FILE: docs_src/response_model/tutorial001_01_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items/")
async def create_item(item: Item) -> Item:
return item
@app.get("/items/")
async def read_items() -> list[Item]:
return [
Item(name="Portal Gun", price=42.0),
Item(name="Plumbus", price=32.0),
]
================================================
FILE: docs_src/response_model/tutorial001_py310.py
================================================
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
return item
@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]
================================================
FILE: docs_src/response_model/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
return user
================================================
FILE: docs_src/response_model/tutorial003_01_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class BaseUser(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
class UserIn(BaseUser):
password: str
@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
return user
================================================
FILE: docs_src/response_model/tutorial003_02_py310.py
================================================
from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return JSONResponse(content={"message": "Here's your interdimensional portal."})
================================================
FILE: docs_src/response_model/tutorial003_03_py310.py
================================================
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
================================================
FILE: docs_src/response_model/tutorial003_04_py310.py
================================================
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}
================================================
FILE: docs_src/response_model/tutorial003_05_py310.py
================================================
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}
================================================
FILE: docs_src/response_model/tutorial003_py310.py
================================================
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user
================================================
FILE: docs_src/response_model/tutorial004_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
================================================
FILE: docs_src/response_model/tutorial005_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items[item_id]
================================================
FILE: docs_src/response_model/tutorial006_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
return items[item_id]
================================================
FILE: docs_src/response_status_code/__init__.py
================================================
================================================
FILE: docs_src/response_status_code/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/", status_code=201)
async def create_item(name: str):
return {"name": name}
================================================
FILE: docs_src/response_status_code/tutorial002_py310.py
================================================
from fastapi import FastAPI, status
app = FastAPI()
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
return {"name": name}
================================================
FILE: docs_src/schema_extra_example/__init__.py
================================================
================================================
FILE: docs_src/schema_extra_example/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: str | None = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: float | None = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial003_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial004_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial004_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial005_an_py310.py
================================================
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/schema_extra_example/tutorial005_py310.py
================================================
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
):
results = {"item_id": item_id, "item": item}
return results
================================================
FILE: docs_src/security/__init__.py
================================================
================================================
FILE: docs_src/security/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
================================================
FILE: docs_src/security/tutorial001_py310.py
================================================
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
================================================
FILE: docs_src/security/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
)
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
user = fake_decode_token(token)
return user
@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user
================================================
FILE: docs_src/security/tutorial002_py310.py
================================================
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
)
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
return user
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
================================================
FILE: docs_src/security/tutorial003_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "alice@example.com",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}
app = FastAPI()
def fake_hash_password(password: str):
return "fakehashed" + password
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")
return {"access_token": user.username, "token_type": "bearer"}
@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user
================================================
FILE: docs_src/security/tutorial003_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "alice@example.com",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}
app = FastAPI()
def fake_hash_password(password: str):
return "fakehashed" + password
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")
return {"access_token": user.username, "token_type": "bearer"}
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
================================================
FILE: docs_src/security/tutorial004_an_py310.py
================================================
from datetime import datetime, timedelta, timezone
from typing import Annotated
import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from pwdlib import PasswordHash
from pydantic import BaseModel
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
"disabled": False,
}
}
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str | None = None
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
def verify_password(plain_password, hashed_password):
return password_hash.verify(plain_password, hashed_password)
def get_password_hash(password):
return password_hash.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
verify_password(password, DUMMY_HASH)
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")
@app.get("/users/me/")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
) -> User:
return current_user
@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return [{"item_id": "Foo", "owner": current_user.username}]
================================================
FILE: docs_src/security/tutorial004_py310.py
================================================
from datetime import datetime, timedelta, timezone
import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from pwdlib import PasswordHash
from pydantic import BaseModel
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
"disabled": False,
}
}
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str | None = None
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
def verify_password(plain_password, hashed_password):
return password_hash.verify(plain_password, hashed_password)
def get_password_hash(password):
return password_hash.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
verify_password(password, DUMMY_HASH)
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")
@app.get("/users/me/")
async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User:
return current_user
@app.get("/users/me/items/")
async def read_own_items(current_user: User = Depends(get_current_active_user)):
return [{"item_id": "Foo", "owner": current_user.username}]
================================================
FILE: docs_src/security/tutorial005_an_py310.py
================================================
from datetime import datetime, timedelta, timezone
from typing import Annotated
import jwt
from fastapi import Depends, FastAPI, HTTPException, Security, status
from fastapi.security import (
OAuth2PasswordBearer,
OAuth2PasswordRequestForm,
SecurityScopes,
)
from jwt.exceptions import InvalidTokenError
from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Chains",
"email": "alicechains@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True,
},
}
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str | None = None
scopes: list[str] = []
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token",
scopes={"me": "Read information about the current user.", "items": "Read items."},
)
app = FastAPI()
def verify_password(plain_password, hashed_password):
return password_hash.verify(plain_password, hashed_password)
def get_password_hash(password):
return password_hash.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
verify_password(password, DUMMY_HASH)
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(
security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]
):
if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else:
authenticate_value = "Bearer"
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise credentials_exception
scope: str = payload.get("scope", "")
token_scopes = scope.split(" ")
token_data = TokenData(scopes=token_scopes, username=username)
except (InvalidTokenError, ValidationError):
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
for scope in security_scopes.scopes:
if scope not in token_data.scopes:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not enough permissions",
headers={"WWW-Authenticate": authenticate_value},
)
return user
async def get_current_active_user(
current_user: Annotated[User, Security(get_current_user, scopes=["me"])],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username, "scope": " ".join(form_data.scopes)},
expires_delta=access_token_expires,
)
return Token(access_token=access_token, token_type="bearer")
@app.get("/users/me/")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
) -> User:
return current_user
@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Security(get_current_active_user, scopes=["items"])],
):
return [{"item_id": "Foo", "owner": current_user.username}]
@app.get("/status/")
async def read_system_status(current_user: Annotated[User, Depends(get_current_user)]):
return {"status": "ok"}
================================================
FILE: docs_src/security/tutorial005_py310.py
================================================
from datetime import datetime, timedelta, timezone
import jwt
from fastapi import Depends, FastAPI, HTTPException, Security, status
from fastapi.security import (
OAuth2PasswordBearer,
OAuth2PasswordRequestForm,
SecurityScopes,
)
from jwt.exceptions import InvalidTokenError
from pwdlib import PasswordHash
from pydantic import BaseModel, ValidationError
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Chains",
"email": "alicechains@example.com",
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
"disabled": True,
},
}
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str | None = None
scopes: list[str] = []
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
class UserInDB(User):
hashed_password: str
password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token",
scopes={"me": "Read information about the current user.", "items": "Read items."},
)
app = FastAPI()
def verify_password(plain_password, hashed_password):
return password_hash.verify(plain_password, hashed_password)
def get_password_hash(password):
return password_hash.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
verify_password(password, DUMMY_HASH)
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: timedelta | None = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(
security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
):
if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else:
authenticate_value = "Bearer"
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
scope: str = payload.get("scope", "")
token_scopes = scope.split(" ")
token_data = TokenData(scopes=token_scopes, username=username)
except (InvalidTokenError, ValidationError):
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
for scope in security_scopes.scopes:
if scope not in token_data.scopes:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not enough permissions",
headers={"WWW-Authenticate": authenticate_value},
)
return user
async def get_current_active_user(
current_user: User = Security(get_current_user, scopes=["me"]),
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username, "scope": " ".join(form_data.scopes)},
expires_delta=access_token_expires,
)
return Token(access_token=access_token, token_type="bearer")
@app.get("/users/me/")
async def read_users_me(current_user: User = Depends(get_current_active_user)) -> User:
return current_user
@app.get("/users/me/items/")
async def read_own_items(
current_user: User = Security(get_current_active_user, scopes=["items"]),
):
return [{"item_id": "Foo", "owner": current_user.username}]
@app.get("/status/")
async def read_system_status(current_user: User = Depends(get_current_user)):
return {"status": "ok"}
================================================
FILE: docs_src/security/tutorial006_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
@app.get("/users/me")
def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
return {"username": credentials.username, "password": credentials.password}
================================================
FILE: docs_src/security/tutorial006_py310.py
================================================
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
@app.get("/users/me")
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
return {"username": credentials.username, "password": credentials.password}
================================================
FILE: docs_src/security/tutorial007_an_py310.py
================================================
import secrets
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def get_current_username(
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
):
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = b"stanleyjobson"
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = b"swordfish"
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/users/me")
def read_current_user(username: Annotated[str, Depends(get_current_username)]):
return {"username": username}
================================================
FILE: docs_src/security/tutorial007_py310.py
================================================
import secrets
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = b"stanleyjobson"
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = b"swordfish"
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/users/me")
def read_current_user(username: str = Depends(get_current_username)):
return {"username": username}
================================================
FILE: docs_src/separate_openapi_schemas/__init__.py
================================================
================================================
FILE: docs_src/separate_openapi_schemas/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI()
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
================================================
FILE: docs_src/separate_openapi_schemas/tutorial002_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI(separate_input_output_schemas=False)
@app.post("/items/")
def create_item(item: Item):
return item
@app.get("/items/")
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",
description="Device to travel through the multi-rick-verse",
),
Item(name="Plumbus"),
]
================================================
FILE: docs_src/server_sent_events/__init__.py
================================================
================================================
FILE: docs_src/server_sent_events/tutorial001_py310.py
================================================
from collections.abc import AsyncIterable, Iterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None
items = [
Item(name="Plumbus", description="A multi-purpose household device."),
Item(name="Portal Gun", description="A portal opening device."),
Item(name="Meeseeks Box", description="A box that summons a Meeseeks."),
]
@app.get("/items/stream", response_class=EventSourceResponse)
async def sse_items() -> AsyncIterable[Item]:
for item in items:
yield item
@app.get("/items/stream-no-async", response_class=EventSourceResponse)
def sse_items_no_async() -> Iterable[Item]:
for item in items:
yield item
@app.get("/items/stream-no-annotation", response_class=EventSourceResponse)
async def sse_items_no_annotation():
for item in items:
yield item
@app.get("/items/stream-no-async-no-annotation", response_class=EventSourceResponse)
def sse_items_no_async_no_annotation():
for item in items:
yield item
================================================
FILE: docs_src/server_sent_events/tutorial002_py310.py
================================================
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
items = [
Item(name="Plumbus", price=32.99),
Item(name="Portal Gun", price=999.99),
Item(name="Meeseeks Box", price=49.99),
]
@app.get("/items/stream", response_class=EventSourceResponse)
async def stream_items() -> AsyncIterable[ServerSentEvent]:
yield ServerSentEvent(comment="stream of item updates")
for i, item in enumerate(items):
yield ServerSentEvent(data=item, event="item_update", id=str(i + 1), retry=5000)
================================================
FILE: docs_src/server_sent_events/tutorial003_py310.py
================================================
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
app = FastAPI()
@app.get("/logs/stream", response_class=EventSourceResponse)
async def stream_logs() -> AsyncIterable[ServerSentEvent]:
logs = [
"2025-01-01 INFO Application started",
"2025-01-01 DEBUG Connected to database",
"2025-01-01 WARN High memory usage detected",
]
for log_line in logs:
yield ServerSentEvent(raw_data=log_line)
================================================
FILE: docs_src/server_sent_events/tutorial004_py310.py
================================================
from collections.abc import AsyncIterable
from typing import Annotated
from fastapi import FastAPI, Header
from fastapi.sse import EventSourceResponse, ServerSentEvent
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
items = [
Item(name="Plumbus", price=32.99),
Item(name="Portal Gun", price=999.99),
Item(name="Meeseeks Box", price=49.99),
]
@app.get("/items/stream", response_class=EventSourceResponse)
async def stream_items(
last_event_id: Annotated[int | None, Header()] = None,
) -> AsyncIterable[ServerSentEvent]:
start = last_event_id + 1 if last_event_id is not None else 0
for i, item in enumerate(items):
if i < start:
continue
yield ServerSentEvent(data=item, id=str(i))
================================================
FILE: docs_src/server_sent_events/tutorial005_py310.py
================================================
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
from pydantic import BaseModel
app = FastAPI()
class Prompt(BaseModel):
text: str
@app.post("/chat/stream", response_class=EventSourceResponse)
async def stream_chat(prompt: Prompt) -> AsyncIterable[ServerSentEvent]:
words = prompt.text.split()
for word in words:
yield ServerSentEvent(data=word, event="token")
yield ServerSentEvent(raw_data="[DONE]", event="done")
================================================
FILE: docs_src/settings/__init__.py
================================================
================================================
FILE: docs_src/settings/app01_py310/__init__.py
================================================
================================================
FILE: docs_src/settings/app01_py310/config.py
================================================
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
================================================
FILE: docs_src/settings/app01_py310/main.py
================================================
from fastapi import FastAPI
from .config import settings
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/settings/app02_an_py310/__init__.py
================================================
================================================
FILE: docs_src/settings/app02_an_py310/config.py
================================================
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
================================================
FILE: docs_src/settings/app02_an_py310/main.py
================================================
from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Annotated[Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/settings/app02_an_py310/test_main.py
================================================
from fastapi.testclient import TestClient
from .config import Settings
from .main import app, get_settings
client = TestClient(app)
def get_settings_override():
return Settings(admin_email="testing_admin@example.com")
app.dependency_overrides[get_settings] = get_settings_override
def test_app():
response = client.get("/info")
data = response.json()
assert data == {
"app_name": "Awesome API",
"admin_email": "testing_admin@example.com",
"items_per_user": 50,
}
================================================
FILE: docs_src/settings/app02_py310/__init__.py
================================================
================================================
FILE: docs_src/settings/app02_py310/config.py
================================================
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
================================================
FILE: docs_src/settings/app02_py310/main.py
================================================
from functools import lru_cache
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/settings/app02_py310/test_main.py
================================================
from fastapi.testclient import TestClient
from .config import Settings
from .main import app, get_settings
client = TestClient(app)
def get_settings_override():
return Settings(admin_email="testing_admin@example.com")
app.dependency_overrides[get_settings] = get_settings_override
def test_app():
response = client.get("/info")
data = response.json()
assert data == {
"app_name": "Awesome API",
"admin_email": "testing_admin@example.com",
"items_per_user": 50,
}
================================================
FILE: docs_src/settings/app03_an_py310/__init__.py
================================================
================================================
FILE: docs_src/settings/app03_an_py310/config.py
================================================
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
model_config = SettingsConfigDict(env_file=".env")
================================================
FILE: docs_src/settings/app03_an_py310/main.py
================================================
from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI
from . import config
app = FastAPI()
@lru_cache
def get_settings():
return config.Settings()
@app.get("/info")
async def info(settings: Annotated[config.Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/settings/app03_py310/__init__.py
================================================
================================================
FILE: docs_src/settings/app03_py310/config.py
================================================
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
model_config = SettingsConfigDict(env_file=".env")
================================================
FILE: docs_src/settings/app03_py310/main.py
================================================
from functools import lru_cache
from fastapi import Depends, FastAPI
from . import config
app = FastAPI()
@lru_cache
def get_settings():
return config.Settings()
@app.get("/info")
async def info(settings: config.Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/settings/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
================================================
FILE: docs_src/sql_databases/__init__.py
================================================
================================================
FILE: docs_src/sql_databases/tutorial001_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
age: int | None = Field(default=None, index=True)
secret_name: str
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
SessionDep = Annotated[Session, Depends(get_session)]
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/")
def create_hero(hero: Hero, session: SessionDep) -> Hero:
session.add(hero)
session.commit()
session.refresh(hero)
return hero
@app.get("/heroes/")
def read_heroes(
session: SessionDep,
offset: int = 0,
limit: Annotated[int, Query(le=100)] = 100,
) -> list[Hero]:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}")
def read_hero(hero_id: int, session: SessionDep) -> Hero:
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"ok": True}
================================================
FILE: docs_src/sql_databases/tutorial001_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
age: int | None = Field(default=None, index=True)
secret_name: str
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/")
def create_hero(hero: Hero, session: Session = Depends(get_session)) -> Hero:
session.add(hero)
session.commit()
session.refresh(hero)
return hero
@app.get("/heroes/")
def read_heroes(
session: Session = Depends(get_session),
offset: int = 0,
limit: int = Query(default=100, le=100),
) -> list[Hero]:
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}")
def read_hero(hero_id: int, session: Session = Depends(get_session)) -> Hero:
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: Session = Depends(get_session)):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"ok": True}
================================================
FILE: docs_src/sql_databases/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, select
class HeroBase(SQLModel):
name: str = Field(index=True)
age: int | None = Field(default=None, index=True)
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)
secret_name: str
class HeroPublic(HeroBase):
id: int
class HeroCreate(HeroBase):
secret_name: str
class HeroUpdate(HeroBase):
name: str | None = None
age: int | None = None
secret_name: str | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
SessionDep = Annotated[Session, Depends(get_session)]
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate, session: SessionDep):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
session: SessionDep,
offset: int = 0,
limit: Annotated[int, Query(le=100)] = 100,
):
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int, session: SessionDep):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep):
hero_db = session.get(Hero, hero_id)
if not hero_db:
raise HTTPException(status_code=404, detail="Hero not found")
hero_data = hero.model_dump(exclude_unset=True)
hero_db.sqlmodel_update(hero_data)
session.add(hero_db)
session.commit()
session.refresh(hero_db)
return hero_db
@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"ok": True}
================================================
FILE: docs_src/sql_databases/tutorial002_py310.py
================================================
from fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, select
class HeroBase(SQLModel):
name: str = Field(index=True)
age: int | None = Field(default=None, index=True)
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)
secret_name: str
class HeroPublic(HeroBase):
id: int
class HeroCreate(HeroBase):
secret_name: str
class HeroUpdate(HeroBase):
name: str | None = None
age: int | None = None
secret_name: str | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/", response_model=HeroPublic)
def create_hero(hero: HeroCreate, session: Session = Depends(get_session)):
db_hero = Hero.model_validate(hero)
session.add(db_hero)
session.commit()
session.refresh(db_hero)
return db_hero
@app.get("/heroes/", response_model=list[HeroPublic])
def read_heroes(
session: Session = Depends(get_session),
offset: int = 0,
limit: int = Query(default=100, le=100),
):
heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
@app.get("/heroes/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int, session: Session = Depends(get_session)):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
return hero
@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
def update_hero(
hero_id: int, hero: HeroUpdate, session: Session = Depends(get_session)
):
hero_db = session.get(Hero, hero_id)
if not hero_db:
raise HTTPException(status_code=404, detail="Hero not found")
hero_data = hero.model_dump(exclude_unset=True)
hero_db.sqlmodel_update(hero_data)
session.add(hero_db)
session.commit()
session.refresh(hero_db)
return hero_db
@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: Session = Depends(get_session)):
hero = session.get(Hero, hero_id)
if not hero:
raise HTTPException(status_code=404, detail="Hero not found")
session.delete(hero)
session.commit()
return {"ok": True}
================================================
FILE: docs_src/static_files/__init__.py
================================================
================================================
FILE: docs_src/static_files/tutorial001_py310.py
================================================
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
================================================
FILE: docs_src/stream_data/__init__.py
================================================
================================================
FILE: docs_src/stream_data/tutorial001_py310.py
================================================
from collections.abc import AsyncIterable, Iterable
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
message = """
Rick: (stumbles in drunkenly, and turns on the lights) Morty! You gotta come on. You got--... you gotta come with me.
Morty: (rubs his eyes) What, Rick? What's going on?
Rick: I got a surprise for you, Morty.
Morty: It's the middle of the night. What are you talking about?
Rick: (spills alcohol on Morty's bed) Come on, I got a surprise for you. (drags Morty by the ankle) Come on, hurry up. (pulls Morty out of his bed and into the hall)
Morty: Ow! Ow! You're tugging me too hard!
Rick: We gotta go, gotta get outta here, come on. Got a surprise for you Morty.
"""
@app.get("/story/stream", response_class=StreamingResponse)
async def stream_story() -> AsyncIterable[str]:
for line in message.splitlines():
yield line
@app.get("/story/stream-no-async", response_class=StreamingResponse)
def stream_story_no_async() -> Iterable[str]:
for line in message.splitlines():
yield line
@app.get("/story/stream-no-annotation", response_class=StreamingResponse)
async def stream_story_no_annotation():
for line in message.splitlines():
yield line
@app.get("/story/stream-no-async-no-annotation", response_class=StreamingResponse)
def stream_story_no_async_no_annotation():
for line in message.splitlines():
yield line
@app.get("/story/stream-bytes", response_class=StreamingResponse)
async def stream_story_bytes() -> AsyncIterable[bytes]:
for line in message.splitlines():
yield line.encode("utf-8")
@app.get("/story/stream-no-async-bytes", response_class=StreamingResponse)
def stream_story_no_async_bytes() -> Iterable[bytes]:
for line in message.splitlines():
yield line.encode("utf-8")
@app.get("/story/stream-no-annotation-bytes", response_class=StreamingResponse)
async def stream_story_no_annotation_bytes():
for line in message.splitlines():
yield line.encode("utf-8")
@app.get("/story/stream-no-async-no-annotation-bytes", response_class=StreamingResponse)
def stream_story_no_async_no_annotation_bytes():
for line in message.splitlines():
yield line.encode("utf-8")
================================================
FILE: docs_src/stream_data/tutorial002_py310.py
================================================
import base64
from collections.abc import AsyncIterable, Iterable
from io import BytesIO
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
image_base64 = "iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAYAAABWk2cPAAAAbnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjadYzRDYAwCET/mcIRDoq0jGOiJm7g+NJK0vjhS4DjIEfHfZ20DKqSrrWZmyFQV5ctRMOLACxglNCcXk7zVqFzJzF8kV6R5vOJ97yVH78HjfYAtg0ged033ZgAAAoCaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMjkiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIyOSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyOSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjkiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiLz4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PnQkBZAAAAAEc0JJVAgICAh8CGSIAAABoklEQVRIx8VXwY7FIAjE5iXWU+P/f6RHPNW9LIaOoHYP+0yMShVkwNGG1lqjfy4HfaF0oyEEt+oSQqBaa//m9Wd6PlqhhbRMDiEQM3e59FNKw5qZHpnQfuPaW6lazsztvu/eElFj5j63lNLlMz2ttbZtVMu1MTGo5Sujn93gMzOllKiUQjHGB9QxxneZhJ5iwZ1rL2fwenoGeL0q3wVGhBPHMz0PeFccIfASEeWcO8xEROd50q6eAV6s1s5XXoncas1EKqVQznnwUBdJJmm1l3hmmdlOMrGO8Vl5gZ56Y0y8IZF0BuqkQWM4B6HXrRCKa1SEqyzEo7KK59RT/VHDjX3ZvSefeW3CO6O6vsiA1NrwVkxxAcYTCcHyTjZmJd00pugBQoTnzjvn+kzLBh9GtRDjhleZFwbx3kugP3GvFzdkqRlbDYw0u/HxKjuOw2QxZCGL5V5f4l7cd6qsffUa1DcLM9N1XcTMvep5ul1e4jNPtZfWGIkE6dI8MquXg/dS2CGVJQ2ushd5GmlxFdOw+1tRa32MY4zDQ9yaZ60J3/iX+QG4U3qGrFHmswAAAABJRU5ErkJggg=="
binary_image = base64.b64decode(image_base64)
def read_image() -> BytesIO:
return BytesIO(binary_image)
app = FastAPI()
class PNGStreamingResponse(StreamingResponse):
media_type = "image/png"
@app.get("/image/stream", response_class=PNGStreamingResponse)
async def stream_image() -> AsyncIterable[bytes]:
with read_image() as image_file:
for chunk in image_file:
yield chunk
@app.get("/image/stream-no-async", response_class=PNGStreamingResponse)
def stream_image_no_async() -> Iterable[bytes]:
with read_image() as image_file:
for chunk in image_file:
yield chunk
@app.get("/image/stream-no-async-yield-from", response_class=PNGStreamingResponse)
def stream_image_no_async_yield_from() -> Iterable[bytes]:
with read_image() as image_file:
yield from image_file
@app.get("/image/stream-no-annotation", response_class=PNGStreamingResponse)
async def stream_image_no_annotation():
with read_image() as image_file:
for chunk in image_file:
yield chunk
@app.get("/image/stream-no-async-no-annotation", response_class=PNGStreamingResponse)
def stream_image_no_async_no_annotation():
with read_image() as image_file:
for chunk in image_file:
yield chunk
================================================
FILE: docs_src/stream_json_lines/__init__.py
================================================
================================================
FILE: docs_src/stream_json_lines/tutorial001_py310.py
================================================
from collections.abc import AsyncIterable, Iterable
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None
items = [
Item(name="Plumbus", description="A multi-purpose household device."),
Item(name="Portal Gun", description="A portal opening device."),
Item(name="Meeseeks Box", description="A box that summons a Meeseeks."),
]
@app.get("/items/stream")
async def stream_items() -> AsyncIterable[Item]:
for item in items:
yield item
@app.get("/items/stream-no-async")
def stream_items_no_async() -> Iterable[Item]:
for item in items:
yield item
@app.get("/items/stream-no-annotation")
async def stream_items_no_annotation():
for item in items:
yield item
@app.get("/items/stream-no-async-no-annotation")
def stream_items_no_async_no_annotation():
for item in items:
yield item
================================================
FILE: docs_src/strict_content_type/__init__.py
================================================
================================================
FILE: docs_src/strict_content_type/tutorial001_py310.py
================================================
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(strict_content_type=False)
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
async def create_item(item: Item):
return item
================================================
FILE: docs_src/sub_applications/__init__.py
================================================
================================================
FILE: docs_src/sub_applications/tutorial001_py310.py
================================================
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
return {"message": "Hello World from sub API"}
app.mount("/subapi", subapi)
================================================
FILE: docs_src/templates/__init__.py
================================================
================================================
FILE: docs_src/templates/static/__init__.py
================================================
================================================
FILE: docs_src/templates/static/styles.css
================================================
h1 {
color: green;
}
================================================
FILE: docs_src/templates/templates/__init__.py
================================================
================================================
FILE: docs_src/templates/templates/item.html
================================================
Item Details
"""
@app.get("/")
async def get():
return HTMLResponse(html)
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
================================================
FILE: docs_src/websockets_/tutorial002_an_py310.py
================================================
from typing import Annotated
from fastapi import (
Cookie,
Depends,
FastAPI,
Query,
WebSocket,
WebSocketException,
status,
)
from fastapi.responses import HTMLResponse
app = FastAPI()
html = """
Chat
WebSocket Chat
"""
@app.get("/")
async def get():
return HTMLResponse(html)
async def get_cookie_or_token(
websocket: WebSocket,
session: Annotated[str | None, Cookie()] = None,
token: Annotated[str | None, Query()] = None,
):
if session is None and token is None:
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
return session or token
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(
*,
websocket: WebSocket,
item_id: str,
q: int | None = None,
cookie_or_token: Annotated[str, Depends(get_cookie_or_token)],
):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(
f"Session cookie or query token value is: {cookie_or_token}"
)
if q is not None:
await websocket.send_text(f"Query parameter q is: {q}")
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
================================================
FILE: docs_src/websockets_/tutorial002_py310.py
================================================
from fastapi import (
Cookie,
Depends,
FastAPI,
Query,
WebSocket,
WebSocketException,
status,
)
from fastapi.responses import HTMLResponse
app = FastAPI()
html = """
Chat
WebSocket Chat
"""
@app.get("/")
async def get():
return HTMLResponse(html)
async def get_cookie_or_token(
websocket: WebSocket,
session: str | None = Cookie(default=None),
token: str | None = Query(default=None),
):
if session is None and token is None:
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
return session or token
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(
websocket: WebSocket,
item_id: str,
q: int | None = None,
cookie_or_token: str = Depends(get_cookie_or_token),
):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(
f"Session cookie or query token value is: {cookie_or_token}"
)
if q is not None:
await websocket.send_text(f"Query parameter q is: {q}")
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
================================================
FILE: docs_src/websockets_/tutorial003_py310.py
================================================
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
app = FastAPI()
html = """
Chat
WebSocket Chat
Your ID:
"""
class ConnectionManager:
def __init__(self):
self.active_connections: list[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.get("/")
async def get():
return HTMLResponse(html)
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.send_personal_message(f"You wrote: {data}", websocket)
await manager.broadcast(f"Client #{client_id} says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client #{client_id} left the chat")
================================================
FILE: docs_src/wsgi/__init__.py
================================================
================================================
FILE: docs_src/wsgi/tutorial001_py310.py
================================================
from a2wsgi import WSGIMiddleware
from fastapi import FastAPI
from flask import Flask, request
from markupsafe import escape
flask_app = Flask(__name__)
@flask_app.route("/")
def flask_main():
name = request.args.get("name", "World")
return f"Hello, {escape(name)} from Flask!"
app = FastAPI()
@app.get("/v2")
def read_main():
return {"message": "Hello World"}
app.mount("/v1", WSGIMiddleware(flask_app))
================================================
FILE: fastapi/.agents/skills/fastapi/SKILL.md
================================================
---
name: fastapi
description: FastAPI best practices and conventions. Use when working with FastAPI APIs and Pydantic models for them. Keeps FastAPI code clean and up to date with the latest features and patterns, updated with new versions. Write new code or refactor and update old code.
---
# FastAPI
Official FastAPI skill to write code with best practices, keeping up to date with new versions and features.
## Use the `fastapi` CLI
Run the development server on localhost with reload:
```bash
fastapi dev
```
Run the production server:
```bash
fastapi run
```
### Add an entrypoint in `pyproject.toml`
FastAPI CLI will read the entrypoint in `pyproject.toml` to know where the FastAPI app is declared.
```toml
[tool.fastapi]
entrypoint = "my_app.main:app"
```
### Use `fastapi` with a path
When adding the entrypoint to `pyproject.toml` is not possible, or the user explicitly asks not to, or it's running an independent small app, you can pass the app file path to the `fastapi` command:
```bash
fastapi dev my_app/main.py
```
Prefer to set the entrypoint in `pyproject.toml` when possible.
## Use `Annotated`
Always prefer the `Annotated` style for parameter and dependency declarations.
It keeps the function signatures working in other contexts, respects the types, allows reusability.
### In Parameter Declarations
Use `Annotated` for parameter declarations, including `Path`, `Query`, `Header`, etc.:
```python
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: Annotated[int, Path(ge=1, description="The item ID")],
q: Annotated[str | None, Query(max_length=50)] = None,
):
return {"message": "Hello World"}
```
instead of:
```python
# DO NOT DO THIS
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(ge=1, description="The item ID"),
q: str | None = Query(default=None, max_length=50),
):
return {"message": "Hello World"}
```
### For Dependencies
Use `Annotated` for dependencies with `Depends()`.
Unless asked not to, create a new type alias for the dependency to allow re-using it.
```python
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_current_user():
return {"username": "johndoe"}
CurrentUserDep = Annotated[dict, Depends(get_current_user)]
@app.get("/items/")
async def read_item(current_user: CurrentUserDep):
return {"message": "Hello World"}
```
instead of:
```python
# DO NOT DO THIS
@app.get("/items/")
async def read_item(current_user: dict = Depends(get_current_user)):
return {"message": "Hello World"}
```
## Do not use Ellipsis for *path operations* or Pydantic models
Do not use `...` as a default value for required parameters, it's not needed and not recommended.
Do this, without Ellipsis (`...`):
```python
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str
description: str | None = None
price: float = Field(gt=0)
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query()]): ...
```
instead of this:
```python
# DO NOT DO THIS
class Item(BaseModel):
name: str = ...
description: str | None = None
price: float = Field(..., gt=0)
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query(...)]): ...
```
## Return Type or Response Model
When possible, include a return type. It will be used to validate, filter, document, and serialize the response.
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me")
async def get_item() -> Item:
return Item(name="Plumbus", description="All-purpose home device")
```
**Important**: Return types or response models are what filter data ensuring no sensitive information is exposed. And they are used to serialize data with Pydantic (in Rust), this is the main idea that can increase response performance.
The return type doesn't have to be a Pydantic model, it could be a different type, like a list of integers, or a dict, etc.
### When to use `response_model` instead
If the return type is not the same as the type that you want to use to validate, filter, or serialize, use the `response_model` parameter on the decorator instead.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
return {"name": "Foo", "description": "A very nice Item"}
```
This can be particularly useful when filtering data to expose only the public fields and avoid exposing sensitive information.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class InternalItem(BaseModel):
name: str
description: str | None = None
secret_key: str
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
item = InternalItem(
name="Foo", description="A very nice Item", secret_key="supersecret"
)
return item
```
## Performance
Do not use `ORJSONResponse` or `UJSONResponse`, they are deprecated.
Instead, declare a return type or response model. Pydantic will handle the data serialization on the Rust side.
## Including Routers
When declaring routers, prefer to add router level parameters like prefix, tags, etc. to the router itself, instead of in `include_router()`.
Do this:
```python
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
async def list_items():
return []
# In main.py
app.include_router(router)
```
instead of this:
```python
# DO NOT DO THIS
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter()
@router.get("/")
async def list_items():
return []
# In main.py
app.include_router(router, prefix="/items", tags=["items"])
```
There could be exceptions, but try to follow this convention.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
## Dependency Injection
See [the dependency injection reference](references/dependencies.md) for detailed patterns including `yield` with `scope`, and class dependencies.
Use dependencies when the logic can't be declared in Pydantic validation, depends on external resources, needs cleanup (with `yield`), or is shared across endpoints.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
## Async vs Sync *path operations*
Use `async` *path operations* only when fully certain that the logic called inside is compatible with async and await (it's called with `await`) or that doesn't block.
```python
from fastapi import FastAPI
app = FastAPI()
# Use async def when calling async code
@app.get("/async-items/")
async def read_async_items():
data = await some_async_library.fetch_items()
return data
# Use plain def when calling blocking/sync code or when in doubt
@app.get("/items/")
def read_items():
data = some_blocking_library.fetch_items()
return data
```
In case of doubt, or by default, use regular `def` functions, those will be run in a threadpool so they don't block the event loop.
The same rules apply to dependencies.
Make sure blocking code is not run inside of `async` functions. The logic will work, but will damage the performance heavily.
When needing to mix blocking and async code, see Asyncer in [the other tools reference](references/other-tools.md).
## Streaming (JSON Lines, SSE, bytes)
See [the streaming reference](references/streaming.md) for JSON Lines, Server-Sent Events (`EventSourceResponse`, `ServerSentEvent`), and byte streaming (`StreamingResponse`) patterns.
## Tooling
See [the other tools reference](references/other-tools.md) for details on uv, Ruff, ty for package management, linting, type checking, formatting, etc.
## Other Libraries
See [the other tools reference](references/other-tools.md) for details on other libraries:
* Asyncer for handling async and await, concurrency, mixing async and blocking code, prefer it over AnyIO or asyncio.
* SQLModel for working with SQL databases, prefer it over SQLAlchemy.
* HTTPX for interacting with HTTP (other APIs), prefer it over Requests.
## Do not use Pydantic RootModels
Do not use Pydantic `RootModel`, instead use regular type annotations with `Annotated` and Pydantic validation utilities.
For example, for a list with validations you could do:
```python
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import Field
app = FastAPI()
@app.post("/items/")
async def create_items(items: Annotated[list[int], Field(min_length=1), Body()]):
return items
```
instead of:
```python
# DO NOT DO THIS
from typing import Annotated
from fastapi import FastAPI
from pydantic import Field, RootModel
app = FastAPI()
class ItemList(RootModel[Annotated[list[int], Field(min_length=1)]]):
pass
@app.post("/items/")
async def create_items(items: ItemList):
return items
```
FastAPI supports these type annotations and will create a Pydantic `TypeAdapter` for them, so that types can work as normally and there's no need for the custom logic and types in RootModels.
## Use one HTTP operation per function
Don't mix HTTP operations in a single function, having one function per HTTP operation helps separate concerns and organize the code.
Do this:
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.get("/items/")
async def list_items():
return []
@app.post("/items/")
async def create_item(item: Item):
return item
```
instead of this:
```python
# DO NOT DO THIS
from fastapi import FastAPI, Request
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.api_route("/items/", methods=["GET", "POST"])
async def handle_items(request: Request):
if request.method == "GET":
return []
```
================================================
FILE: fastapi/.agents/skills/fastapi/references/dependencies.md
================================================
# Dependency Injection
Use dependencies when:
* They can't be declared in Pydantic validation and require additional logic
* The logic depends on external resources or could block in any other way
* Other dependencies need their results (it's a sub-dependency)
* The logic can be shared by multiple endpoints to do things like error early, authentication, etc.
* They need to handle cleanup (e.g., DB sessions, file handles), using dependencies with `yield`
* Their logic needs input data from the request, like headers, query parameters, etc.
## Dependencies with `yield` and `scope`
When using dependencies with `yield`, they can have a `scope` that defines when the exit code is run.
Use the default scope `"request"` to run the exit code after the response is sent back.
```python
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
DBDep = Annotated[DBSession, Depends(get_db)]
@app.get("/items/")
async def read_items(db: DBDep):
return db.query(Item).all()
```
Use the scope `"function"` when they should run the exit code after the response data is generated but before the response is sent back to the client.
```python
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_username():
try:
yield "Rick"
finally:
print("Cleanup up before response is sent")
UserNameDep = Annotated[str, Depends(get_username, scope="function")]
@app.get("/users/me")
def get_user_me(username: UserNameDep):
return username
```
## Class Dependencies
Avoid creating class dependencies when possible.
If a class is needed, instead create a regular function dependency that returns a class instance.
Do this:
```python
from dataclasses import dataclass
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
@dataclass
class DatabasePaginator:
offset: int = 0
limit: int = 100
q: str | None = None
def get_page(self) -> dict:
# Simulate a page of data
return {
"offset": self.offset,
"limit": self.limit,
"q": self.q,
"items": [],
}
def get_db_paginator(
offset: int = 0, limit: int = 100, q: str | None = None
) -> DatabasePaginator:
return DatabasePaginator(offset=offset, limit=limit, q=q)
PaginatorDep = Annotated[DatabasePaginator, Depends(get_db_paginator)]
@app.get("/items/")
async def read_items(paginator: PaginatorDep):
return paginator.get_page()
```
instead of this:
```python
# DO NOT DO THIS
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
class DatabasePaginator:
def __init__(self, offset: int = 0, limit: int = 100, q: str | None = None):
self.offset = offset
self.limit = limit
self.q = q
def get_page(self) -> dict:
# Simulate a page of data
return {
"offset": self.offset,
"limit": self.limit,
"q": self.q,
"items": [],
}
@app.get("/items/")
async def read_items(paginator: Annotated[DatabasePaginator, Depends()]):
return paginator.get_page()
```
================================================
FILE: fastapi/.agents/skills/fastapi/references/other-tools.md
================================================
# Other Tools
## uv
If uv is available, use it to manage dependencies.
## Ruff
If Ruff is available, use it to lint and format the code. Consider enabling the FastAPI rules.
## ty
If ty is available, use it to check types.
## Asyncer
When needing to run blocking code inside of async functions, or async code inside of blocking functions, suggest using Asyncer.
Prefer it over AnyIO or asyncio.
Install:
```bash
uv add asyncer
```
Run blocking sync code inside of async with `asyncify()`:
```python
from asyncer import asyncify
from fastapi import FastAPI
app = FastAPI()
def do_blocking_work(name: str) -> str:
# Some blocking I/O operation
return f"Hello {name}"
@app.get("/items/")
async def read_items():
result = await asyncify(do_blocking_work)(name="World")
return {"message": result}
```
And run async code inside of blocking sync code with `syncify()`:
```python
from asyncer import syncify
from fastapi import FastAPI
app = FastAPI()
async def do_async_work(name: str) -> str:
return f"Hello {name}"
@app.get("/items/")
def read_items():
result = syncify(do_async_work)(name="World")
return {"message": result}
```
## SQLModel for SQL databases
When working with SQL databases, prefer using SQLModel as it is integrated with Pydantic and will allow declaring data validation with the same models.
Prefer it over SQLAlchemy.
## HTTPX
Use HTTPX for handling HTTP communication (e.g. with other APIs). It support sync and async usage.
Prefer it over Requests.
================================================
FILE: fastapi/.agents/skills/fastapi/references/streaming.md
================================================
# Streaming
## Stream JSON Lines
To stream JSON Lines, declare the return type and use `yield` to return the data.
```python
@app.get("/items/stream")
async def stream_items() -> AsyncIterable[Item]:
for item in items:
yield item
```
## Server-Sent Events (SSE)
To stream Server-Sent Events, use `response_class=EventSourceResponse` and `yield` items from the endpoint.
Plain objects are automatically JSON-serialized as `data:` fields, declare the return type so the serialization is done by Pydantic:
```python
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/items/stream", response_class=EventSourceResponse)
async def stream_items() -> AsyncIterable[Item]:
yield Item(name="Plumbus", price=32.99)
yield Item(name="Portal Gun", price=999.99)
```
For full control over SSE fields (`event`, `id`, `retry`, `comment`), yield `ServerSentEvent` instances:
```python
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
app = FastAPI()
@app.get("/events", response_class=EventSourceResponse)
async def stream_events() -> AsyncIterable[ServerSentEvent]:
yield ServerSentEvent(data={"status": "started"}, event="status", id="1")
yield ServerSentEvent(data={"progress": 50}, event="progress", id="2")
```
Use `raw_data` instead of `data` to send pre-formatted strings without JSON encoding:
```python
yield ServerSentEvent(raw_data="plain text line", event="log")
```
## Stream bytes
To stream bytes, declare a `response_class=` of `StreamingResponse` or a sub-class, and use `yield` to return the data.
```python
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from app.utils import read_image
app = FastAPI()
class PNGStreamingResponse(StreamingResponse):
media_type = "image/png"
@app.get("/image", response_class=PNGStreamingResponse)
def stream_image_no_async_no_annotation():
with read_image() as image_file:
yield from image_file
```
prefer this over returning a `StreamingResponse` directly:
```python
# DO NOT DO THIS
import anyio
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from app.utils import read_image
app = FastAPI()
class PNGStreamingResponse(StreamingResponse):
media_type = "image/png"
@app.get("/")
async def main():
return PNGStreamingResponse(read_image())
```
================================================
FILE: fastapi/__init__.py
================================================
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.136.1"
from starlette import status as status
from .applications import FastAPI as FastAPI
from .background import BackgroundTasks as BackgroundTasks
from .datastructures import UploadFile as UploadFile
from .exceptions import HTTPException as HTTPException
from .exceptions import WebSocketException as WebSocketException
from .param_functions import Body as Body
from .param_functions import Cookie as Cookie
from .param_functions import Depends as Depends
from .param_functions import File as File
from .param_functions import Form as Form
from .param_functions import Header as Header
from .param_functions import Path as Path
from .param_functions import Query as Query
from .param_functions import Security as Security
from .requests import Request as Request
from .responses import Response as Response
from .routing import APIRouter as APIRouter
from .websockets import WebSocket as WebSocket
from .websockets import WebSocketDisconnect as WebSocketDisconnect
================================================
FILE: fastapi/__main__.py
================================================
from fastapi.cli import main
main()
================================================
FILE: fastapi/_compat/__init__.py
================================================
from .shared import PYDANTIC_VERSION_MINOR_TUPLE as PYDANTIC_VERSION_MINOR_TUPLE
from .shared import annotation_is_pydantic_v1 as annotation_is_pydantic_v1
from .shared import field_annotation_is_scalar as field_annotation_is_scalar
from .shared import (
field_annotation_is_scalar_sequence as field_annotation_is_scalar_sequence,
)
from .shared import field_annotation_is_sequence as field_annotation_is_sequence
from .shared import (
is_bytes_or_nonable_bytes_annotation as is_bytes_or_nonable_bytes_annotation,
)
from .shared import is_bytes_sequence_annotation as is_bytes_sequence_annotation
from .shared import is_pydantic_v1_model_instance as is_pydantic_v1_model_instance
from .shared import (
is_uploadfile_or_nonable_uploadfile_annotation as is_uploadfile_or_nonable_uploadfile_annotation,
)
from .shared import (
is_uploadfile_sequence_annotation as is_uploadfile_sequence_annotation,
)
from .shared import lenient_issubclass as lenient_issubclass
from .shared import sequence_types as sequence_types
from .shared import value_is_sequence as value_is_sequence
from .v2 import ModelField as ModelField
from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from .v2 import RequiredParam as RequiredParam
from .v2 import Undefined as Undefined
from .v2 import Url as Url
from .v2 import copy_field_info as copy_field_info
from .v2 import create_body_model as create_body_model
from .v2 import evaluate_forwardref as evaluate_forwardref
from .v2 import get_cached_model_fields as get_cached_model_fields
from .v2 import get_definitions as get_definitions
from .v2 import get_flat_models_from_fields as get_flat_models_from_fields
from .v2 import get_missing_field_error as get_missing_field_error
from .v2 import get_model_name_map as get_model_name_map
from .v2 import get_schema_from_model_field as get_schema_from_model_field
from .v2 import is_scalar_field as is_scalar_field
from .v2 import serialize_sequence_value as serialize_sequence_value
from .v2 import (
with_info_plain_validator_function as with_info_plain_validator_function,
)
================================================
FILE: fastapi/_compat/shared.py
================================================
import types
import typing
import warnings
from collections import deque
from collections.abc import Mapping, Sequence
from dataclasses import is_dataclass
from typing import (
Annotated,
Any,
TypeGuard,
TypeVar,
Union,
get_args,
get_origin,
)
from fastapi.types import UnionType
from pydantic import BaseModel
from pydantic.version import VERSION as PYDANTIC_VERSION
from starlette.datastructures import UploadFile
_T = TypeVar("_T")
# Copy from Pydantic: pydantic/_internal/_typing_extra.py
WithArgsTypes: tuple[Any, ...] = (
typing._GenericAlias, # type: ignore[attr-defined] # ty: ignore[unresolved-attribute]
types.GenericAlias,
types.UnionType,
) # pyright: ignore[reportAttributeAccessIssue]
PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
sequence_annotation_to_type = {
Sequence: list,
list: list,
tuple: tuple,
set: set,
frozenset: frozenset,
deque: deque,
}
sequence_types: tuple[type[Any], ...] = tuple(sequence_annotation_to_type.keys())
# Copy of Pydantic: pydantic/_internal/_utils.py with added TypeGuard
def lenient_issubclass(
cls: Any, class_or_tuple: type[_T] | tuple[type[_T], ...] | None
) -> TypeGuard[type[_T]]:
try:
return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
except TypeError: # pragma: no cover
if isinstance(cls, WithArgsTypes):
return False
raise # pragma: no cover
def _annotation_is_sequence(annotation: type[Any] | None) -> bool:
if lenient_issubclass(annotation, (str, bytes)):
return False
return lenient_issubclass(annotation, sequence_types)
def field_annotation_is_sequence(annotation: type[Any] | None) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
if field_annotation_is_sequence(arg):
return True
return False
return _annotation_is_sequence(annotation) or _annotation_is_sequence(
get_origin(annotation)
)
def value_is_sequence(value: Any) -> bool:
return isinstance(value, sequence_types) and not isinstance(value, (str, bytes))
def _annotation_is_complex(annotation: type[Any] | None) -> bool:
return (
lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))
or _annotation_is_sequence(annotation)
or is_dataclass(annotation)
)
def field_annotation_is_complex(annotation: type[Any] | None) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
if origin is Annotated:
return field_annotation_is_complex(get_args(annotation)[0])
return (
_annotation_is_complex(annotation)
or _annotation_is_complex(origin)
or hasattr(origin, "__pydantic_core_schema__")
or hasattr(origin, "__get_pydantic_core_schema__")
)
def field_annotation_is_scalar(annotation: Any) -> bool:
# handle Ellipsis here to make tuple[int, ...] work nicely
return annotation is Ellipsis or not field_annotation_is_complex(annotation)
def field_annotation_is_scalar_sequence(annotation: type[Any] | None) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
at_least_one_scalar_sequence = False
for arg in get_args(annotation):
if field_annotation_is_scalar_sequence(arg):
at_least_one_scalar_sequence = True
continue
elif not field_annotation_is_scalar(arg):
return False
return at_least_one_scalar_sequence
return field_annotation_is_sequence(annotation) and all(
field_annotation_is_scalar(sub_annotation)
for sub_annotation in get_args(annotation)
)
def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool:
if lenient_issubclass(annotation, bytes):
return True
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
if lenient_issubclass(arg, bytes):
return True
return False
def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool:
if lenient_issubclass(annotation, UploadFile):
return True
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
if lenient_issubclass(arg, UploadFile):
return True
return False
def is_bytes_sequence_annotation(annotation: Any) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
at_least_one = False
for arg in get_args(annotation):
if is_bytes_sequence_annotation(arg):
at_least_one = True
continue
return at_least_one
return field_annotation_is_sequence(annotation) and all(
is_bytes_or_nonable_bytes_annotation(sub_annotation)
for sub_annotation in get_args(annotation)
)
def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
at_least_one = False
for arg in get_args(annotation):
if is_uploadfile_sequence_annotation(arg):
at_least_one = True
continue
return at_least_one
return field_annotation_is_sequence(annotation) and all(
is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation)
for sub_annotation in get_args(annotation)
)
def is_pydantic_v1_model_instance(obj: Any) -> bool:
# TODO: remove this function once the required version of Pydantic fully
# removes pydantic.v1
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
from pydantic import v1
except ImportError: # pragma: no cover
return False
return isinstance(obj, v1.BaseModel)
def is_pydantic_v1_model_class(cls: Any) -> bool:
# TODO: remove this function once the required version of Pydantic fully
# removes pydantic.v1
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
from pydantic import v1
except ImportError: # pragma: no cover
return False
return lenient_issubclass(cls, v1.BaseModel)
def annotation_is_pydantic_v1(annotation: Any) -> bool:
if is_pydantic_v1_model_class(annotation):
return True
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
if is_pydantic_v1_model_class(arg):
return True
if field_annotation_is_sequence(annotation):
for sub_annotation in get_args(annotation):
if annotation_is_pydantic_v1(sub_annotation):
return True
return False
================================================
FILE: fastapi/_compat/v2.py
================================================
import re
import warnings
from collections.abc import Sequence
from copy import copy
from dataclasses import dataclass, is_dataclass
from enum import Enum
from functools import lru_cache
from typing import (
Annotated,
Any,
Literal,
Union,
cast,
get_args,
get_origin,
)
from fastapi._compat import lenient_issubclass, shared
from fastapi.openapi.constants import REF_TEMPLATE
from fastapi.types import IncEx, ModelNameMap, UnionType
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, create_model
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
from pydantic import ValidationError as ValidationError
from pydantic._internal import _typing_extra as _pydantic_typing_extra
from pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined]
GetJsonSchemaHandler as GetJsonSchemaHandler,
)
from pydantic.fields import FieldInfo as FieldInfo
from pydantic.json_schema import GenerateJsonSchema as _GenerateJsonSchema
from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue
from pydantic_core import CoreSchema as CoreSchema
from pydantic_core import PydanticUndefined
from pydantic_core import Url as Url
from pydantic_core.core_schema import (
with_info_plain_validator_function as with_info_plain_validator_function,
)
RequiredParam = PydanticUndefined
Undefined = PydanticUndefined
def evaluate_forwardref(
value: Any,
globalns: dict[str, Any] | None = None,
localns: dict[str, Any] | None = None,
) -> Any:
# eval_type_lenient has been deprecated since Pydantic v2.10.0b1 (PR #10530)
try_eval_type = getattr(_pydantic_typing_extra, "try_eval_type", None)
if try_eval_type is not None:
return try_eval_type(value, globalns, localns)[0]
return _pydantic_typing_extra.eval_type_lenient( # ty: ignore[deprecated]
value, globalns, localns
)
class GenerateJsonSchema(_GenerateJsonSchema):
# TODO: remove when this is merged (or equivalent): https://github.com/pydantic/pydantic/pull/12841
# and dropping support for any version of Pydantic before that one (so, in a very long time)
def bytes_schema(self, schema: CoreSchema) -> JsonSchemaValue:
json_schema = {"type": "string", "contentMediaType": "application/octet-stream"}
bytes_mode = (
self._config.ser_json_bytes
if self.mode == "serialization"
else self._config.val_json_bytes
)
if bytes_mode == "base64":
json_schema["contentEncoding"] = "base64"
self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes)
return json_schema
# TODO: remove when dropping support for Pydantic < v2.12.3
_Attrs = {
"default": ...,
"default_factory": None,
"alias": None,
"alias_priority": None,
"validation_alias": None,
"serialization_alias": None,
"title": None,
"field_title_generator": None,
"description": None,
"examples": None,
"exclude": None,
"exclude_if": None,
"discriminator": None,
"deprecated": None,
"json_schema_extra": None,
"frozen": None,
"validate_default": None,
"repr": True,
"init": None,
"init_var": None,
"kw_only": None,
}
# TODO: remove when dropping support for Pydantic < v2.12.3
def asdict(field_info: FieldInfo) -> dict[str, Any]:
attributes = {}
for attr in _Attrs:
value = getattr(field_info, attr, Undefined)
if value is not Undefined:
attributes[attr] = value
return {
"annotation": field_info.annotation,
"metadata": field_info.metadata,
"attributes": attributes,
}
@dataclass
class ModelField:
field_info: FieldInfo
name: str
mode: Literal["validation", "serialization"] = "validation"
config: ConfigDict | None = None
@property
def alias(self) -> str:
a = self.field_info.alias
return a if a is not None else self.name
@property
def validation_alias(self) -> str | None:
va = self.field_info.validation_alias
if isinstance(va, str) and va:
return va
return None
@property
def serialization_alias(self) -> str | None:
sa = self.field_info.serialization_alias
return sa or None
@property
def default(self) -> Any:
return self.get_default()
def __post_init__(self) -> None:
with warnings.catch_warnings():
# Pydantic >= 2.12.0 warns about field specific metadata that is unused
# (e.g. `TypeAdapter(Annotated[int, Field(alias='b')])`). In some cases, we
# end up building the type adapter from a model field annotation so we
# need to ignore the warning:
if shared.PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):
from pydantic.warnings import UnsupportedFieldAttributeWarning
warnings.simplefilter(
"ignore", category=UnsupportedFieldAttributeWarning
)
# TODO: remove after setting the min Pydantic to v2.12.3
# that adds asdict(), and use self.field_info.asdict() instead
field_dict = asdict(self.field_info)
annotated_args = (
field_dict["annotation"],
*field_dict["metadata"],
# this FieldInfo needs to be created again so that it doesn't include
# the old field info metadata and only the rest of the attributes
Field(**field_dict["attributes"]),
)
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
Annotated[annotated_args], # ty: ignore[invalid-type-form]
config=self.config,
)
def get_default(self) -> Any:
if self.field_info.is_required():
return Undefined
return self.field_info.get_default(call_default_factory=True)
def validate(
self,
value: Any,
values: dict[str, Any] = {}, # noqa: B006
*,
loc: tuple[int | str, ...] = (),
) -> tuple[Any, list[dict[str, Any]]]:
try:
return (
self._type_adapter.validate_python(value, from_attributes=True),
[],
)
except ValidationError as exc:
return None, _regenerate_error_with_loc(
errors=exc.errors(include_url=False), loc_prefix=loc
)
def serialize(
self,
value: Any,
*,
mode: Literal["json", "python"] = "json",
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = True,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> Any:
# What calls this code passes a value that already called
# self._type_adapter.validate_python(value)
return self._type_adapter.dump_python(
value,
mode=mode,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
def serialize_json(
self,
value: Any,
*,
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = True,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> bytes:
# What calls this code passes a value that already called
# self._type_adapter.validate_python(value)
# This uses Pydantic's dump_json() which serializes directly to JSON
# bytes in one pass (via Rust), avoiding the intermediate Python dict
# step of dump_python(mode="json") + json.dumps().
return self._type_adapter.dump_json(
value,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from
# ModelField to its JSON Schema.
return id(self)
def _has_computed_fields(field: ModelField) -> bool:
computed_fields = field._type_adapter.core_schema.get("schema", {}).get(
"computed_fields", []
)
return len(computed_fields) > 0
def get_schema_from_model_field(
*,
field: ModelField,
model_name_map: ModelNameMap,
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> dict[str, Any]:
override_mode: Literal["validation"] | None = (
None
if (separate_input_output_schemas or _has_computed_fields(field))
else "validation"
)
field_alias = (
(field.validation_alias or field.alias)
if field.mode == "validation"
else (field.serialization_alias or field.alias)
)
# This expects that GenerateJsonSchema was already used to generate the definitions
json_schema = field_mapping[(field, override_mode or field.mode)]
if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
json_schema["title"] = field.field_info.title or field_alias.title().replace(
"_", " "
)
return json_schema
def get_definitions(
*,
fields: Sequence[ModelField],
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> tuple[
dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
dict[str, dict[str, Any]],
]:
schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
validation_fields = [field for field in fields if field.mode == "validation"]
serialization_fields = [field for field in fields if field.mode == "serialization"]
flat_validation_models = get_flat_models_from_fields(
validation_fields, known_models=set()
)
flat_serialization_models = get_flat_models_from_fields(
serialization_fields, known_models=set()
)
flat_validation_model_fields = [
ModelField(
field_info=FieldInfo(annotation=model),
name=model.__name__,
mode="validation",
)
for model in flat_validation_models
]
flat_serialization_model_fields = [
ModelField(
field_info=FieldInfo(annotation=model),
name=model.__name__,
mode="serialization",
)
for model in flat_serialization_models
]
flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields
input_types = {f.field_info.annotation for f in fields}
unique_flat_model_fields = {
f for f in flat_model_fields if f.field_info.annotation not in input_types
}
inputs = [
(
field,
(
field.mode
if (separate_input_output_schemas or _has_computed_fields(field))
else "validation"
),
field._type_adapter.core_schema,
)
for field in list(fields) + list(unique_flat_model_fields)
]
field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)
for item_def in cast(dict[str, dict[str, Any]], definitions).values():
if "description" in item_def:
item_description = cast(str, item_def["description"]).split("\f")[0]
item_def["description"] = item_description
# definitions: dict[DefsRef, dict[str, Any]]
# but mypy complains about general str in other places that are not declared as
# DefsRef, although DefsRef is just str:
# DefsRef = NewType('DefsRef', str)
# So, a cast to simplify the types here
return field_mapping, cast(dict[str, dict[str, Any]], definitions)
def is_scalar_field(field: ModelField) -> bool:
from fastapi import params
return shared.field_annotation_is_scalar(
field.field_info.annotation
) and not isinstance(field.field_info, params.Body)
def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
cls = type(field_info)
merged_field_info = cls.from_annotation(annotation)
new_field_info = copy(field_info)
new_field_info.metadata = merged_field_info.metadata
new_field_info.annotation = merged_field_info.annotation
return new_field_info
def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
origin_type = get_origin(field.field_info.annotation) or field.field_info.annotation
if origin_type is Union or origin_type is UnionType: # Handle optional sequences
union_args = get_args(field.field_info.annotation)
for union_arg in union_args:
if union_arg is type(None):
continue
origin_type = get_origin(union_arg) or union_arg
break
assert issubclass(origin_type, shared.sequence_types) # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return,index] # ty: ignore[invalid-return-type]
def get_missing_field_error(loc: tuple[int | str, ...]) -> dict[str, Any]:
error = ValidationError.from_exception_data(
"Field required", [{"type": "missing", "loc": loc, "input": {}}]
).errors(include_url=False)[0]
error["input"] = None
return error # type: ignore[return-value] # ty: ignore[invalid-return-type]
def create_body_model(
*, fields: Sequence[ModelField], model_name: str
) -> type[BaseModel]:
field_params = {f.name: (f.field_info.annotation, f.field_info) for f in fields}
BodyModel: type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload] # ty: ignore[no-matching-overload]
return BodyModel
def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
model_fields: list[ModelField] = []
for name, field_info in model.model_fields.items():
type_ = field_info.annotation
if lenient_issubclass(type_, (BaseModel, dict)) or is_dataclass(type_):
model_config = None
else:
model_config = model.model_config
model_fields.append(
ModelField(
field_info=field_info,
name=name,
config=model_config,
)
)
return model_fields
@lru_cache
def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
return get_model_fields(model)
# Duplicate of several schema functions from Pydantic v1 to make them compatible with
# Pydantic v2 and allow mixing the models
TypeModelOrEnum = type["BaseModel"] | type[Enum]
TypeModelSet = set[TypeModelOrEnum]
def normalize_name(name: str) -> str:
return re.sub(r"[^a-zA-Z0-9.\-_]", "_", name)
def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str]:
name_model_map = {}
for model in unique_models:
model_name = normalize_name(model.__name__)
name_model_map[model_name] = model
return {v: k for k, v in name_model_map.items()}
def get_flat_models_from_model(
model: type["BaseModel"], known_models: TypeModelSet | None = None
) -> TypeModelSet:
known_models = known_models or set()
fields = get_model_fields(model)
get_flat_models_from_fields(fields, known_models=known_models)
return known_models
def get_flat_models_from_annotation(
annotation: Any, known_models: TypeModelSet
) -> TypeModelSet:
origin = get_origin(annotation)
if origin is not None:
for arg in get_args(annotation):
if lenient_issubclass(arg, (BaseModel, Enum)):
if arg not in known_models:
known_models.add(arg) # type: ignore[arg-type]
if lenient_issubclass(arg, BaseModel):
get_flat_models_from_model(arg, known_models=known_models)
else:
get_flat_models_from_annotation(arg, known_models=known_models)
return known_models
def get_flat_models_from_field(
field: ModelField, known_models: TypeModelSet
) -> TypeModelSet:
field_type = field.field_info.annotation
if lenient_issubclass(field_type, BaseModel):
if field_type in known_models:
return known_models
known_models.add(field_type)
get_flat_models_from_model(field_type, known_models=known_models)
elif lenient_issubclass(field_type, Enum):
known_models.add(field_type)
else:
get_flat_models_from_annotation(field_type, known_models=known_models)
return known_models
def get_flat_models_from_fields(
fields: Sequence[ModelField], known_models: TypeModelSet
) -> TypeModelSet:
for field in fields:
get_flat_models_from_field(field, known_models=known_models)
return known_models
def _regenerate_error_with_loc(
*, errors: Sequence[Any], loc_prefix: tuple[str | int, ...]
) -> list[dict[str, Any]]:
updated_loc_errors: list[Any] = [
{**err, "loc": loc_prefix + err.get("loc", ())} for err in errors
]
return updated_loc_errors
================================================
FILE: fastapi/applications.py
================================================
[File too large to display: 177.0 KB]
================================================
FILE: fastapi/background.py
================================================
from collections.abc import Callable
from typing import Annotated, Any
from annotated_doc import Doc
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from typing_extensions import ParamSpec
P = ParamSpec("P")
class BackgroundTasks(StarletteBackgroundTasks):
"""
A collection of background tasks that will be called after a response has been
sent to the client.
Read more about it in the
[FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
## Example
```python
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification(email: str, message=""):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
```
"""
def add_task(
self,
func: Annotated[
Callable[P, Any],
Doc(
"""
The function to call after the response is sent.
It can be a regular `def` function or an `async def` function.
"""
),
],
*args: P.args,
**kwargs: P.kwargs,
) -> None:
"""
Add a function to be called in the background after the response is sent.
Read more about it in the
[FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
"""
return super().add_task(func, *args, **kwargs)
================================================
FILE: fastapi/cli.py
================================================
try:
from fastapi_cli.cli import main as cli_main
except ImportError: # pragma: no cover
cli_main = None # type: ignore
def main() -> None:
if not cli_main: # type: ignore[truthy-function]
message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n'
print(message)
raise RuntimeError(message) # noqa: B904
cli_main()
================================================
FILE: fastapi/concurrency.py
================================================
from collections.abc import AsyncGenerator
from contextlib import AbstractContextManager
from contextlib import asynccontextmanager as asynccontextmanager
from typing import TypeVar
import anyio.to_thread
from anyio import CapacityLimiter
from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa
from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa
from starlette.concurrency import ( # noqa
run_until_first_complete as run_until_first_complete,
)
_T = TypeVar("_T")
@asynccontextmanager
async def contextmanager_in_threadpool(
cm: AbstractContextManager[_T],
) -> AsyncGenerator[_T, None]:
# blocking __exit__ from running waiting on a free thread
# can create race conditions/deadlocks if the context manager itself
# has its own internal pool (e.g. a database connection pool)
# to avoid this we let __exit__ run without a capacity limit
# since we're creating a new limiter for each call, any non-zero limit
# works (1 is arbitrary)
exit_limiter = CapacityLimiter(1)
try:
yield await run_in_threadpool(cm.__enter__)
except Exception as e:
ok = bool(
await anyio.to_thread.run_sync(
cm.__exit__, type(e), e, e.__traceback__, limiter=exit_limiter
)
)
if not ok:
raise e
else:
await anyio.to_thread.run_sync(
cm.__exit__, None, None, None, limiter=exit_limiter
)
================================================
FILE: fastapi/datastructures.py
================================================
from collections.abc import Callable, Mapping
from typing import (
Annotated,
Any,
BinaryIO,
TypeVar,
cast,
)
from annotated_doc import Doc
from pydantic import GetJsonSchemaHandler
from starlette.datastructures import URL as URL # noqa: F401
from starlette.datastructures import Address as Address # noqa: F401
from starlette.datastructures import FormData as FormData # noqa: F401
from starlette.datastructures import Headers as Headers # noqa: F401
from starlette.datastructures import QueryParams as QueryParams # noqa: F401
from starlette.datastructures import State as State # noqa: F401
from starlette.datastructures import UploadFile as StarletteUploadFile
class UploadFile(StarletteUploadFile):
"""
A file uploaded in a request.
Define it as a *path operation function* (or dependency) parameter.
If you are using a regular `def` function, you can use the `upload_file.file`
attribute to access the raw standard Python file (blocking, not async), useful and
needed for non-async code.
Read more about it in the
[FastAPI docs for Request Files](https://fastapi.tiangolo.com/tutorial/request-files/).
## Example
```python
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
```
"""
file: Annotated[
BinaryIO,
Doc("The standard Python file object (non-async)."),
]
filename: Annotated[str | None, Doc("The original file name.")]
size: Annotated[int | None, Doc("The size of the file in bytes.")]
headers: Annotated[Headers, Doc("The headers of the request.")]
content_type: Annotated[
str | None, Doc("The content type of the request, from the headers.")
]
async def write(
self,
data: Annotated[
bytes,
Doc(
"""
The bytes to write to the file.
"""
),
],
) -> None:
"""
Write some bytes to the file.
You normally wouldn't use this from a file you read in a request.
To be awaitable, compatible with async, this is run in threadpool.
"""
return await super().write(data)
async def read(
self,
size: Annotated[
int,
Doc(
"""
The number of bytes to read from the file.
"""
),
] = -1,
) -> bytes:
"""
Read some bytes from the file.
To be awaitable, compatible with async, this is run in threadpool.
"""
return await super().read(size)
async def seek(
self,
offset: Annotated[
int,
Doc(
"""
The position in bytes to seek to in the file.
"""
),
],
) -> None:
"""
Move to a position in the file.
Any next read or write will be done from that position.
To be awaitable, compatible with async, this is run in threadpool.
"""
return await super().seek(offset)
async def close(self) -> None:
"""
Close the file.
To be awaitable, compatible with async, this is run in threadpool.
"""
return await super().close()
@classmethod
def _validate(cls, __input_value: Any, _: Any) -> "UploadFile":
if not isinstance(__input_value, StarletteUploadFile):
raise ValueError(f"Expected UploadFile, received: {type(__input_value)}")
return cast(UploadFile, __input_value)
@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
) -> dict[str, Any]:
return {"type": "string", "contentMediaType": "application/octet-stream"}
@classmethod
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
) -> Mapping[str, Any]:
from ._compat.v2 import with_info_plain_validator_function
return with_info_plain_validator_function(cls._validate)
class DefaultPlaceholder:
"""
You shouldn't use this class directly.
It's used internally to recognize when a default value has been overwritten, even
if the overridden default value was truthy.
"""
def __init__(self, value: Any):
self.value = value
def __bool__(self) -> bool:
return bool(self.value)
def __eq__(self, o: object) -> bool:
return isinstance(o, DefaultPlaceholder) and o.value == self.value
DefaultType = TypeVar("DefaultType")
def Default(value: DefaultType) -> DefaultType:
"""
You shouldn't use this function directly.
It's used internally to recognize when a default value has been overwritten, even
if the overridden default value was truthy.
"""
return DefaultPlaceholder(value) # type: ignore
# Sentinel for "parameter not provided" in Param/FieldInfo.
# Typed as None to satisfy ty
_Unset = Default(None)
================================================
FILE: fastapi/dependencies/__init__.py
================================================
================================================
FILE: fastapi/dependencies/models.py
================================================
import inspect
import sys
from collections.abc import Callable
from dataclasses import dataclass, field
from functools import cached_property, partial
from typing import Any, Literal
from fastapi._compat import ModelField
from fastapi.security.base import SecurityBase
from fastapi.types import DependencyCacheKey
if sys.version_info >= (3, 13): # pragma: no cover
from inspect import iscoroutinefunction
else: # pragma: no cover
from asyncio import iscoroutinefunction
def _unwrapped_call(call: Callable[..., Any] | None) -> Any:
if call is None:
return call # pragma: no cover
unwrapped = inspect.unwrap(_impartial(call))
return unwrapped
def _impartial(func: Callable[..., Any]) -> Callable[..., Any]:
while isinstance(func, partial):
func = func.func
return func
@dataclass
class Dependant:
path_params: list[ModelField] = field(default_factory=list)
query_params: list[ModelField] = field(default_factory=list)
header_params: list[ModelField] = field(default_factory=list)
cookie_params: list[ModelField] = field(default_factory=list)
body_params: list[ModelField] = field(default_factory=list)
dependencies: list["Dependant"] = field(default_factory=list)
name: str | None = None
call: Callable[..., Any] | None = None
request_param_name: str | None = None
websocket_param_name: str | None = None
http_connection_param_name: str | None = None
response_param_name: str | None = None
background_tasks_param_name: str | None = None
security_scopes_param_name: str | None = None
own_oauth_scopes: list[str] | None = None
parent_oauth_scopes: list[str] | None = None
use_cache: bool = True
path: str | None = None
scope: Literal["function", "request"] | None = None
@cached_property
def oauth_scopes(self) -> list[str]:
scopes = self.parent_oauth_scopes.copy() if self.parent_oauth_scopes else []
# This doesn't use a set to preserve order, just in case
for scope in self.own_oauth_scopes or []:
if scope not in scopes:
scopes.append(scope)
return scopes
@cached_property
def cache_key(self) -> DependencyCacheKey:
scopes_for_cache = (
tuple(sorted(set(self.oauth_scopes or []))) if self._uses_scopes else ()
)
return (
self.call,
scopes_for_cache,
self.computed_scope or "",
)
@cached_property
def _uses_scopes(self) -> bool:
if self.own_oauth_scopes:
return True
if self.security_scopes_param_name is not None:
return True
if self._is_security_scheme:
return True
for sub_dep in self.dependencies:
if sub_dep._uses_scopes:
return True
return False
@cached_property
def _is_security_scheme(self) -> bool:
if self.call is None:
return False # pragma: no cover
unwrapped = _unwrapped_call(self.call)
return isinstance(unwrapped, SecurityBase)
# Mainly to get the type of SecurityBase, but it's the same self.call
@cached_property
def _security_scheme(self) -> SecurityBase:
unwrapped = _unwrapped_call(self.call)
assert isinstance(unwrapped, SecurityBase)
return unwrapped
@cached_property
def _security_dependencies(self) -> list["Dependant"]:
security_deps = [dep for dep in self.dependencies if dep._is_security_scheme]
return security_deps
@cached_property
def is_gen_callable(self) -> bool:
if self.call is None:
return False # pragma: no cover
if inspect.isgeneratorfunction(
_impartial(self.call)
) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):
return True
if inspect.isclass(_unwrapped_call(self.call)):
return False
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
if dunder_call is None:
return False # pragma: no cover
if inspect.isgeneratorfunction(
_impartial(dunder_call)
) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):
return True
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
if dunder_unwrapped_call is None:
return False # pragma: no cover
if inspect.isgeneratorfunction(
_impartial(dunder_unwrapped_call)
) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)):
return True
return False
@cached_property
def is_async_gen_callable(self) -> bool:
if self.call is None:
return False # pragma: no cover
if inspect.isasyncgenfunction(
_impartial(self.call)
) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
return True
if inspect.isclass(_unwrapped_call(self.call)):
return False
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
if dunder_call is None:
return False # pragma: no cover
if inspect.isasyncgenfunction(
_impartial(dunder_call)
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):
return True
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
if dunder_unwrapped_call is None:
return False # pragma: no cover
if inspect.isasyncgenfunction(
_impartial(dunder_unwrapped_call)
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
return True
return False
@cached_property
def is_coroutine_callable(self) -> bool:
if self.call is None:
return False # pragma: no cover
if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(
_impartial(self.call)
):
return True
if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(
_unwrapped_call(self.call)
):
return True
if inspect.isclass(_unwrapped_call(self.call)):
return False
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
if dunder_call is None:
return False # pragma: no cover
if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(
_unwrapped_call(dunder_call)
):
return True
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
if dunder_unwrapped_call is None:
return False # pragma: no cover
if iscoroutinefunction(
_impartial(dunder_unwrapped_call)
) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):
return True
return False
@cached_property
def computed_scope(self) -> str | None:
if self.scope:
return self.scope
if self.is_gen_callable or self.is_async_gen_callable:
return "request"
return None
================================================
FILE: fastapi/dependencies/utils.py
================================================
import dataclasses
import inspect
import sys
from collections.abc import (
AsyncGenerator,
AsyncIterable,
AsyncIterator,
Callable,
Generator,
Iterable,
Iterator,
Mapping,
Sequence,
)
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
from dataclasses import dataclass
from typing import (
Annotated,
Any,
ForwardRef,
Literal,
Union,
cast,
get_args,
get_origin,
)
from fastapi import params
from fastapi._compat import (
ModelField,
RequiredParam,
Undefined,
copy_field_info,
create_body_model,
evaluate_forwardref,
field_annotation_is_scalar,
field_annotation_is_scalar_sequence,
field_annotation_is_sequence,
get_cached_model_fields,
get_missing_field_error,
is_bytes_or_nonable_bytes_annotation,
is_bytes_sequence_annotation,
is_scalar_field,
is_uploadfile_or_nonable_uploadfile_annotation,
is_uploadfile_sequence_annotation,
lenient_issubclass,
sequence_types,
serialize_sequence_value,
value_is_sequence,
)
from fastapi.background import BackgroundTasks
from fastapi.concurrency import (
asynccontextmanager,
contextmanager_in_threadpool,
)
from fastapi.dependencies.models import Dependant
from fastapi.exceptions import DependencyScopeError
from fastapi.logger import logger
from fastapi.security.oauth2 import SecurityScopes
from fastapi.types import DependencyCacheKey
from fastapi.utils import create_model_field, get_path_param_names
from pydantic import BaseModel, Json
from pydantic.fields import FieldInfo
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from starlette.concurrency import run_in_threadpool
from starlette.datastructures import (
FormData,
Headers,
ImmutableMultiDict,
QueryParams,
UploadFile,
)
from starlette.requests import HTTPConnection, Request
from starlette.responses import Response
from starlette.websockets import WebSocket
from typing_inspection.typing_objects import is_typealiastype
multipart_not_installed_error = (
'Form data requires "python-multipart" to be installed. \n'
'You can install "python-multipart" with: \n\n'
"pip install python-multipart\n"
)
multipart_incorrect_install_error = (
'Form data requires "python-multipart" to be installed. '
'It seems you installed "multipart" instead. \n'
'You can remove "multipart" with: \n\n'
"pip uninstall multipart\n\n"
'And then install "python-multipart" with: \n\n'
"pip install python-multipart\n"
)
def ensure_multipart_is_installed() -> None:
try:
from python_multipart import __version__
# Import an attribute that can be mocked/deleted in testing
assert __version__ > "0.0.12"
except (ImportError, AssertionError):
try:
# __version__ is available in both multiparts, and can be mocked
from multipart import ( # type: ignore[no-redef,import-untyped]
__version__,
)
assert __version__
try:
# parse_options_header is only available in the right multipart
from multipart.multipart import ( # type: ignore[import-untyped]
parse_options_header,
)
assert parse_options_header
except ImportError:
logger.error(multipart_incorrect_install_error)
raise RuntimeError(multipart_incorrect_install_error) from None
except ImportError:
logger.error(multipart_not_installed_error)
raise RuntimeError(multipart_not_installed_error) from None
def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
assert callable(depends.dependency), (
"A parameter-less dependency must have a callable dependency"
)
own_oauth_scopes: list[str] = []
if isinstance(depends, params.Security) and depends.scopes:
own_oauth_scopes.extend(depends.scopes)
return get_dependant(
path=path,
call=depends.dependency,
scope=depends.scope,
own_oauth_scopes=own_oauth_scopes,
)
def get_flat_dependant(
dependant: Dependant,
*,
skip_repeats: bool = False,
visited: list[DependencyCacheKey] | None = None,
parent_oauth_scopes: list[str] | None = None,
) -> Dependant:
if visited is None:
visited = []
visited.append(dependant.cache_key)
use_parent_oauth_scopes = (parent_oauth_scopes or []) + (
dependant.oauth_scopes or []
)
flat_dependant = Dependant(
path_params=dependant.path_params.copy(),
query_params=dependant.query_params.copy(),
header_params=dependant.header_params.copy(),
cookie_params=dependant.cookie_params.copy(),
body_params=dependant.body_params.copy(),
name=dependant.name,
call=dependant.call,
request_param_name=dependant.request_param_name,
websocket_param_name=dependant.websocket_param_name,
http_connection_param_name=dependant.http_connection_param_name,
response_param_name=dependant.response_param_name,
background_tasks_param_name=dependant.background_tasks_param_name,
security_scopes_param_name=dependant.security_scopes_param_name,
own_oauth_scopes=dependant.own_oauth_scopes,
parent_oauth_scopes=use_parent_oauth_scopes,
use_cache=dependant.use_cache,
path=dependant.path,
scope=dependant.scope,
)
for sub_dependant in dependant.dependencies:
if skip_repeats and sub_dependant.cache_key in visited:
continue
flat_sub = get_flat_dependant(
sub_dependant,
skip_repeats=skip_repeats,
visited=visited,
parent_oauth_scopes=flat_dependant.oauth_scopes,
)
flat_dependant.dependencies.append(flat_sub)
flat_dependant.path_params.extend(flat_sub.path_params)
flat_dependant.query_params.extend(flat_sub.query_params)
flat_dependant.header_params.extend(flat_sub.header_params)
flat_dependant.cookie_params.extend(flat_sub.cookie_params)
flat_dependant.body_params.extend(flat_sub.body_params)
flat_dependant.dependencies.extend(flat_sub.dependencies)
return flat_dependant
def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]:
if not fields:
return fields
first_field = fields[0]
if len(fields) == 1 and lenient_issubclass(
first_field.field_info.annotation, BaseModel
):
fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
return fields_to_extract
return fields
def get_flat_params(dependant: Dependant) -> list[ModelField]:
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
path_params = _get_flat_fields_from_params(flat_dependant.path_params)
query_params = _get_flat_fields_from_params(flat_dependant.query_params)
header_params = _get_flat_fields_from_params(flat_dependant.header_params)
cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
return path_params + query_params + header_params + cookie_params
def _get_signature(call: Callable[..., Any]) -> inspect.Signature:
try:
signature = inspect.signature(call, eval_str=True)
except NameError:
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI
# e.g. dependency return types
if sys.version_info >= (3, 14):
from annotationlib import Format
signature = inspect.signature(call, annotation_format=Format.FORWARDREF)
else:
signature = inspect.signature(call)
return signature
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = _get_signature(call)
unwrapped = inspect.unwrap(call)
globalns = getattr(unwrapped, "__globals__", {})
typed_params = [
inspect.Parameter(
name=param.name,
kind=param.kind,
default=param.default,
annotation=get_typed_annotation(param.annotation, globalns),
)
for param in signature.parameters.values()
]
typed_signature = inspect.Signature(typed_params)
return typed_signature
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
if isinstance(annotation, str):
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns)
if annotation is type(None):
return None
return annotation
def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
signature = _get_signature(call)
unwrapped = inspect.unwrap(call)
annotation = signature.return_annotation
if annotation is inspect.Signature.empty:
return None
globalns = getattr(unwrapped, "__globals__", {})
return get_typed_annotation(annotation, globalns)
_STREAM_ORIGINS = {
AsyncIterable,
AsyncIterator,
AsyncGenerator,
Iterable,
Iterator,
Generator,
}
def get_stream_item_type(annotation: Any) -> Any | None:
origin = get_origin(annotation)
if origin is not None and origin in _STREAM_ORIGINS:
type_args = get_args(annotation)
if type_args:
return type_args[0]
return Any
return None
def get_dependant(
*,
path: str,
call: Callable[..., Any],
name: str | None = None,
own_oauth_scopes: list[str] | None = None,
parent_oauth_scopes: list[str] | None = None,
use_cache: bool = True,
scope: Literal["function", "request"] | None = None,
) -> Dependant:
dependant = Dependant(
call=call,
name=name,
path=path,
use_cache=use_cache,
scope=scope,
own_oauth_scopes=own_oauth_scopes,
parent_oauth_scopes=parent_oauth_scopes,
)
current_scopes = (parent_oauth_scopes or []) + (own_oauth_scopes or [])
path_param_names = get_path_param_names(path)
endpoint_signature = get_typed_signature(call)
signature_params = endpoint_signature.parameters
for param_name, param in signature_params.items():
is_path_param = param_name in path_param_names
param_details = analyze_param(
param_name=param_name,
annotation=param.annotation,
value=param.default,
is_path_param=is_path_param,
)
if param_details.depends is not None:
assert param_details.depends.dependency
if (
(dependant.is_gen_callable or dependant.is_async_gen_callable)
and dependant.computed_scope == "request"
and param_details.depends.scope == "function"
):
assert dependant.call
call_name = getattr(dependant.call, "__name__", "")
raise DependencyScopeError(
f'The dependency "{call_name}" has a scope of '
'"request", it cannot depend on dependencies with scope "function".'
)
sub_own_oauth_scopes: list[str] = []
if isinstance(param_details.depends, params.Security):
if param_details.depends.scopes:
sub_own_oauth_scopes = list(param_details.depends.scopes)
sub_dependant = get_dependant(
path=path,
call=param_details.depends.dependency,
name=param_name,
own_oauth_scopes=sub_own_oauth_scopes,
parent_oauth_scopes=current_scopes,
use_cache=param_details.depends.use_cache,
scope=param_details.depends.scope,
)
dependant.dependencies.append(sub_dependant)
continue
if add_non_field_param_to_dependency(
param_name=param_name,
type_annotation=param_details.type_annotation,
dependant=dependant,
):
assert param_details.field is None, (
f"Cannot specify multiple FastAPI annotations for {param_name!r}"
)
continue
assert param_details.field is not None
if isinstance(param_details.field.field_info, params.Body):
dependant.body_params.append(param_details.field)
else:
add_param_to_fields(field=param_details.field, dependant=dependant)
return dependant
def add_non_field_param_to_dependency(
*, param_name: str, type_annotation: Any, dependant: Dependant
) -> bool | None:
if lenient_issubclass(type_annotation, Request):
dependant.request_param_name = param_name
return True
elif lenient_issubclass(type_annotation, WebSocket):
dependant.websocket_param_name = param_name
return True
elif lenient_issubclass(type_annotation, HTTPConnection):
dependant.http_connection_param_name = param_name
return True
elif lenient_issubclass(type_annotation, Response):
dependant.response_param_name = param_name
return True
elif lenient_issubclass(type_annotation, StarletteBackgroundTasks):
dependant.background_tasks_param_name = param_name
return True
elif lenient_issubclass(type_annotation, SecurityScopes):
dependant.security_scopes_param_name = param_name
return True
return None
@dataclass
class ParamDetails:
type_annotation: Any
depends: params.Depends | None
field: ModelField | None
def analyze_param(
*,
param_name: str,
annotation: Any,
value: Any,
is_path_param: bool,
) -> ParamDetails:
field_info = None
depends = None
type_annotation: Any = Any
use_annotation: Any = Any
if is_typealiastype(annotation):
# unpack in case PEP 695 type syntax is used
annotation = annotation.__value__
if annotation is not inspect.Signature.empty:
use_annotation = annotation
type_annotation = annotation
# Extract Annotated info
if get_origin(use_annotation) is Annotated:
annotated_args = get_args(annotation)
type_annotation = annotated_args[0]
fastapi_annotations = [
arg
for arg in annotated_args[1:]
if isinstance(arg, (FieldInfo, params.Depends))
]
fastapi_specific_annotations = [
arg
for arg in fastapi_annotations
if isinstance(
arg,
(
params.Param,
params.Body,
params.Depends,
),
)
]
if fastapi_specific_annotations:
fastapi_annotation: FieldInfo | params.Depends | None = (
fastapi_specific_annotations[-1]
)
else:
fastapi_annotation = None
# Set default for Annotated FieldInfo
if isinstance(fastapi_annotation, FieldInfo):
# Copy `field_info` because we mutate `field_info.default` below.
field_info = copy_field_info(
field_info=fastapi_annotation,
annotation=use_annotation,
)
assert (
field_info.default == Undefined or field_info.default == RequiredParam
), (
f"`{field_info.__class__.__name__}` default value cannot be set in"
f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
)
if value is not inspect.Signature.empty:
assert not is_path_param, "Path parameters cannot have default values"
field_info.default = value
else:
field_info.default = RequiredParam
# Get Annotated Depends
elif isinstance(fastapi_annotation, params.Depends):
depends = fastapi_annotation
# Get Depends from default value
if isinstance(value, params.Depends):
assert depends is None, (
"Cannot specify `Depends` in `Annotated` and default value"
f" together for {param_name!r}"
)
assert field_info is None, (
"Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a"
f" default value together for {param_name!r}"
)
depends = value
# Get FieldInfo from default value
elif isinstance(value, FieldInfo):
assert field_info is None, (
"Cannot specify FastAPI annotations in `Annotated` and default value"
f" together for {param_name!r}"
)
field_info = value
if isinstance(field_info, FieldInfo):
field_info.annotation = type_annotation
# Get Depends from type annotation
if depends is not None and depends.dependency is None:
# Copy `depends` before mutating it
depends = copy(depends)
depends = dataclasses.replace(depends, dependency=type_annotation)
# Handle non-param type annotations like Request
# Only apply special handling when there's no explicit Depends - if there's a Depends,
# the dependency will be called and its return value used instead of the special injection
if depends is None and lenient_issubclass(
type_annotation,
(
Request,
WebSocket,
HTTPConnection,
Response,
StarletteBackgroundTasks,
SecurityScopes,
),
):
assert field_info is None, (
f"Cannot specify FastAPI annotation for type {type_annotation!r}"
)
# Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
elif field_info is None and depends is None:
default_value = value if value is not inspect.Signature.empty else RequiredParam
if is_path_param:
# We might check here that `default_value is RequiredParam`, but the fact is that the same
# parameter might sometimes be a path parameter and sometimes not. See
# `tests/test_infer_param_optionality.py` for an example.
field_info = params.Path(annotation=use_annotation)
elif is_uploadfile_or_nonable_uploadfile_annotation(
type_annotation
) or is_uploadfile_sequence_annotation(type_annotation):
field_info = params.File(annotation=use_annotation, default=default_value)
elif not field_annotation_is_scalar(annotation=type_annotation):
field_info = params.Body(annotation=use_annotation, default=default_value)
else:
field_info = params.Query(annotation=use_annotation, default=default_value)
field = None
# It's a field_info, not a dependency
if field_info is not None:
# Handle field_info.in_
if is_path_param:
assert isinstance(field_info, params.Path), (
f"Cannot use `{field_info.__class__.__name__}` for path param"
f" {param_name!r}"
)
elif (
isinstance(field_info, params.Param)
and getattr(field_info, "in_", None) is None
):
field_info.in_ = params.ParamTypes.query
use_annotation_from_field_info = use_annotation
if isinstance(field_info, params.Form):
ensure_multipart_is_installed()
if not field_info.alias and getattr(field_info, "convert_underscores", None):
alias = param_name.replace("_", "-")
else:
alias = field_info.alias or param_name
field_info.alias = alias
field = create_model_field(
name=param_name,
type_=use_annotation_from_field_info,
default=field_info.default,
alias=alias,
field_info=field_info,
)
if is_path_param:
assert is_scalar_field(field=field), (
"Path params must be of one of the supported types"
)
elif isinstance(field_info, params.Query):
assert (
is_scalar_field(field)
or field_annotation_is_scalar_sequence(field.field_info.annotation)
or lenient_issubclass(field.field_info.annotation, BaseModel)
), f"Query parameter {param_name!r} must be one of the supported types"
return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
field_info = field.field_info
field_info_in = getattr(field_info, "in_", None)
if field_info_in == params.ParamTypes.path:
dependant.path_params.append(field)
elif field_info_in == params.ParamTypes.query:
dependant.query_params.append(field)
elif field_info_in == params.ParamTypes.header:
dependant.header_params.append(field)
else:
assert field_info_in == params.ParamTypes.cookie, (
f"non-body parameters must be in path, query, header or cookie: {field.name}"
)
dependant.cookie_params.append(field)
async def _solve_generator(
*, dependant: Dependant, stack: AsyncExitStack, sub_values: dict[str, Any]
) -> Any:
assert dependant.call
if dependant.is_async_gen_callable:
cm = asynccontextmanager(dependant.call)(**sub_values)
elif dependant.is_gen_callable:
cm = contextmanager_in_threadpool(contextmanager(dependant.call)(**sub_values))
return await stack.enter_async_context(cm)
@dataclass
class SolvedDependency:
values: dict[str, Any]
errors: list[Any]
background_tasks: StarletteBackgroundTasks | None
response: Response
dependency_cache: dict[DependencyCacheKey, Any]
async def solve_dependencies(
*,
request: Request | WebSocket,
dependant: Dependant,
body: dict[str, Any] | FormData | bytes | None = None,
background_tasks: StarletteBackgroundTasks | None = None,
response: Response | None = None,
dependency_overrides_provider: Any | None = None,
dependency_cache: dict[DependencyCacheKey, Any] | None = None,
# TODO: remove this parameter later, no longer used, not removing it yet as some
# people might be monkey patching this function (although that's not supported)
async_exit_stack: AsyncExitStack,
embed_body_fields: bool,
) -> SolvedDependency:
request_astack = request.scope.get("fastapi_inner_astack")
assert isinstance(request_astack, AsyncExitStack), (
"fastapi_inner_astack not found in request scope"
)
function_astack = request.scope.get("fastapi_function_astack")
assert isinstance(function_astack, AsyncExitStack), (
"fastapi_function_astack not found in request scope"
)
values: dict[str, Any] = {}
errors: list[Any] = []
if response is None:
response = Response()
del response.headers["content-length"]
response.status_code = None # type: ignore
if dependency_cache is None:
dependency_cache = {}
for sub_dependant in dependant.dependencies:
sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
call = sub_dependant.call
use_sub_dependant = sub_dependant
if (
dependency_overrides_provider
and dependency_overrides_provider.dependency_overrides
):
original_call = sub_dependant.call
call = getattr(
dependency_overrides_provider, "dependency_overrides", {}
).get(original_call, original_call)
use_path: str = sub_dependant.path # type: ignore
use_sub_dependant = get_dependant(
path=use_path,
call=call,
name=sub_dependant.name,
parent_oauth_scopes=sub_dependant.oauth_scopes,
scope=sub_dependant.scope,
)
solved_result = await solve_dependencies(
request=request,
dependant=use_sub_dependant,
body=body,
background_tasks=background_tasks,
response=response,
dependency_overrides_provider=dependency_overrides_provider,
dependency_cache=dependency_cache,
async_exit_stack=async_exit_stack,
embed_body_fields=embed_body_fields,
)
background_tasks = solved_result.background_tasks
if solved_result.errors:
errors.extend(solved_result.errors)
continue
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
solved = dependency_cache[sub_dependant.cache_key]
elif (
use_sub_dependant.is_gen_callable or use_sub_dependant.is_async_gen_callable
):
use_astack = request_astack
if sub_dependant.scope == "function":
use_astack = function_astack
solved = await _solve_generator(
dependant=use_sub_dependant,
stack=use_astack,
sub_values=solved_result.values,
)
elif use_sub_dependant.is_coroutine_callable:
solved = await call(**solved_result.values)
else:
solved = await run_in_threadpool(call, **solved_result.values)
if sub_dependant.name is not None:
values[sub_dependant.name] = solved
if sub_dependant.cache_key not in dependency_cache:
dependency_cache[sub_dependant.cache_key] = solved
path_values, path_errors = request_params_to_args(
dependant.path_params, request.path_params
)
query_values, query_errors = request_params_to_args(
dependant.query_params, request.query_params
)
header_values, header_errors = request_params_to_args(
dependant.header_params, request.headers
)
cookie_values, cookie_errors = request_params_to_args(
dependant.cookie_params, request.cookies
)
values.update(path_values)
values.update(query_values)
values.update(header_values)
values.update(cookie_values)
errors += path_errors + query_errors + header_errors + cookie_errors
if dependant.body_params:
(
body_values,
body_errors,
) = await request_body_to_args( # body_params checked above
body_fields=dependant.body_params,
received_body=body,
embed_body_fields=embed_body_fields,
)
values.update(body_values)
errors.extend(body_errors)
if dependant.http_connection_param_name:
values[dependant.http_connection_param_name] = request
if dependant.request_param_name and isinstance(request, Request):
values[dependant.request_param_name] = request
elif dependant.websocket_param_name and isinstance(request, WebSocket):
values[dependant.websocket_param_name] = request
if dependant.background_tasks_param_name:
if background_tasks is None:
background_tasks = BackgroundTasks()
values[dependant.background_tasks_param_name] = background_tasks
if dependant.response_param_name:
values[dependant.response_param_name] = response
if dependant.security_scopes_param_name:
values[dependant.security_scopes_param_name] = SecurityScopes(
scopes=dependant.oauth_scopes
)
return SolvedDependency(
values=values,
errors=errors,
background_tasks=background_tasks,
response=response,
dependency_cache=dependency_cache,
)
def _validate_value_with_model_field(
*, field: ModelField, value: Any, values: dict[str, Any], loc: tuple[str, ...]
) -> tuple[Any, list[Any]]:
if value is None:
if field.field_info.is_required():
return None, [get_missing_field_error(loc=loc)]
else:
return deepcopy(field.default), []
return field.validate(value, values, loc=loc)
def _is_json_field(field: ModelField) -> bool:
return any(type(item) is Json for item in field.field_info.metadata)
def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: str | None = None
) -> Any:
alias = alias or get_validation_alias(field)
if (
(not _is_json_field(field))
and field_annotation_is_sequence(field.field_info.annotation)
and isinstance(values, (ImmutableMultiDict, Headers))
):
value = values.getlist(alias)
else:
value = values.get(alias, None)
if (
value is None
or (
isinstance(field.field_info, params.Form)
and isinstance(value, str) # For type checks
and value == ""
)
or (
field_annotation_is_sequence(field.field_info.annotation)
and len(value) == 0
)
):
if field.field_info.is_required():
return
else:
return deepcopy(field.default)
return value
def request_params_to_args(
fields: Sequence[ModelField],
received_params: Mapping[str, Any] | QueryParams | Headers,
) -> tuple[dict[str, Any], list[Any]]:
values: dict[str, Any] = {}
errors: list[dict[str, Any]] = []
if not fields:
return values, errors
first_field = fields[0]
fields_to_extract = fields
single_not_embedded_field = False
default_convert_underscores = True
if len(fields) == 1 and lenient_issubclass(
first_field.field_info.annotation, BaseModel
):
fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
single_not_embedded_field = True
# If headers are in a Pydantic model, the way to disable convert_underscores
# would be with Header(convert_underscores=False) at the Pydantic model level
default_convert_underscores = getattr(
first_field.field_info, "convert_underscores", True
)
params_to_process: dict[str, Any] = {}
processed_keys = set()
for field in fields_to_extract:
alias = None
if isinstance(received_params, Headers):
# Handle fields extracted from a Pydantic Model for a header, each field
# doesn't have a FieldInfo of type Header with the default convert_underscores=True
convert_underscores = getattr(
field.field_info, "convert_underscores", default_convert_underscores
)
if convert_underscores:
alias = get_validation_alias(field)
if alias == field.name:
alias = alias.replace("_", "-")
value = _get_multidict_value(field, received_params, alias=alias)
if value is not None:
params_to_process[get_validation_alias(field)] = value
processed_keys.add(alias or get_validation_alias(field))
for key in received_params.keys():
if key not in processed_keys:
if isinstance(received_params, (ImmutableMultiDict, Headers)):
value = received_params.getlist(key)
if isinstance(value, list) and (len(value) == 1):
params_to_process[key] = value[0]
else:
params_to_process[key] = value
else:
params_to_process[key] = received_params.get(key)
if single_not_embedded_field:
field_info = first_field.field_info
assert isinstance(field_info, params.Param), (
"Params must be subclasses of Param"
)
loc: tuple[str, ...] = (field_info.in_.value,)
v_, errors_ = _validate_value_with_model_field(
field=first_field, value=params_to_process, values=values, loc=loc
)
return {first_field.name: v_}, errors_
for field in fields:
value = _get_multidict_value(field, received_params)
field_info = field.field_info
assert isinstance(field_info, params.Param), (
"Params must be subclasses of Param"
)
loc = (field_info.in_.value, get_validation_alias(field))
v_, errors_ = _validate_value_with_model_field(
field=field, value=value, values=values, loc=loc
)
if errors_:
errors.extend(errors_)
else:
values[field.name] = v_
return values, errors
def is_union_of_base_models(field_type: Any) -> bool:
"""Check if field type is a Union where all members are BaseModel subclasses."""
from fastapi.types import UnionType
origin = get_origin(field_type)
# Check if it's a Union type (covers both typing.Union and types.UnionType in Python 3.10+)
if origin is not Union and origin is not UnionType:
return False
union_args = get_args(field_type)
for arg in union_args:
if not lenient_issubclass(arg, BaseModel):
return False
return True
def _should_embed_body_fields(fields: list[ModelField]) -> bool:
if not fields:
return False
# More than one dependency could have the same field, it would show up as multiple
# fields but it's the same one, so count them by name
body_param_names_set = {field.name for field in fields}
# A top level field has to be a single field, not multiple
if len(body_param_names_set) > 1:
return True
first_field = fields[0]
# If it explicitly specifies it is embedded, it has to be embedded
if getattr(first_field.field_info, "embed", None):
return True
# If it's a Form (or File) field, it has to be a BaseModel (or a union of BaseModels) to be top level
# otherwise it has to be embedded, so that the key value pair can be extracted
if (
isinstance(first_field.field_info, params.Form)
and not lenient_issubclass(first_field.field_info.annotation, BaseModel)
and not is_union_of_base_models(first_field.field_info.annotation)
):
return True
return False
async def _extract_form_body(
body_fields: list[ModelField],
received_body: FormData,
) -> dict[str, Any]:
values = {}
for field in body_fields:
value = _get_multidict_value(field, received_body)
field_info = field.field_info
if (
isinstance(field_info, params.File)
and is_bytes_or_nonable_bytes_annotation(field.field_info.annotation)
and isinstance(value, UploadFile)
):
value = await value.read()
elif (
is_bytes_sequence_annotation(field.field_info.annotation)
and isinstance(field_info, params.File)
and value_is_sequence(value)
):
# For types
assert isinstance(value, sequence_types)
results: list[bytes | str] = []
for sub_value in value:
results.append(await sub_value.read())
value = serialize_sequence_value(field=field, value=results)
if value is not None:
values[get_validation_alias(field)] = value
field_aliases = {get_validation_alias(field) for field in body_fields}
for key in received_body.keys():
if key not in field_aliases:
param_values = received_body.getlist(key)
if len(param_values) == 1:
values[key] = param_values[0]
else:
values[key] = param_values
return values
async def request_body_to_args(
body_fields: list[ModelField],
received_body: dict[str, Any] | FormData | bytes | None,
embed_body_fields: bool,
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
values: dict[str, Any] = {}
errors: list[dict[str, Any]] = []
assert body_fields, "request_body_to_args() should be called with fields"
single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
first_field = body_fields[0]
body_to_process = received_body
fields_to_extract: list[ModelField] = body_fields
if (
single_not_embedded_field
and lenient_issubclass(first_field.field_info.annotation, BaseModel)
and isinstance(received_body, FormData)
):
fields_to_extract = get_cached_model_fields(first_field.field_info.annotation)
if isinstance(received_body, FormData):
body_to_process = await _extract_form_body(fields_to_extract, received_body)
if single_not_embedded_field:
loc: tuple[str, ...] = ("body",)
v_, errors_ = _validate_value_with_model_field(
field=first_field, value=body_to_process, values=values, loc=loc
)
return {first_field.name: v_}, errors_
for field in body_fields:
loc = ("body", get_validation_alias(field))
value: Any | None = None
if body_to_process is not None and not isinstance(body_to_process, bytes):
try:
value = body_to_process.get(get_validation_alias(field))
# If the received body is a list, not a dict
except AttributeError:
errors.append(get_missing_field_error(loc))
continue
v_, errors_ = _validate_value_with_model_field(
field=field, value=value, values=values, loc=loc
)
if errors_:
errors.extend(errors_)
else:
values[field.name] = v_
return values, errors
def get_body_field(
*, flat_dependant: Dependant, name: str, embed_body_fields: bool
) -> ModelField | None:
"""
Get a ModelField representing the request body for a path operation, combining
all body parameters into a single field if necessary.
Used to check if it's form data (with `isinstance(body_field, params.Form)`)
or JSON and to generate the JSON Schema for a request body.
This is **not** used to validate/parse the request body, that's done with each
individual body parameter.
"""
if not flat_dependant.body_params:
return None
first_param = flat_dependant.body_params[0]
if not embed_body_fields:
return first_param
model_name = "Body_" + name
BodyModel = create_body_model(
fields=flat_dependant.body_params, model_name=model_name
)
required = any(
True for f in flat_dependant.body_params if f.field_info.is_required()
)
BodyFieldInfo_kwargs: dict[str, Any] = {
"annotation": BodyModel,
"alias": "body",
}
if not required:
BodyFieldInfo_kwargs["default"] = None
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
BodyFieldInfo: type[params.Body] = params.File
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
BodyFieldInfo = params.Form
else:
BodyFieldInfo = params.Body
body_param_media_types = [
f.field_info.media_type
for f in flat_dependant.body_params
if isinstance(f.field_info, params.Body)
]
if len(set(body_param_media_types)) == 1:
BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
final_field = create_model_field(
name="body",
type_=BodyModel,
alias="body",
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
return final_field
def get_validation_alias(field: ModelField) -> str:
va = getattr(field, "validation_alias", None)
return va or field.alias
================================================
FILE: fastapi/encoders.py
================================================
import dataclasses
import datetime
from collections import defaultdict, deque
from collections.abc import Callable
from decimal import Decimal
from enum import Enum
from ipaddress import (
IPv4Address,
IPv4Interface,
IPv4Network,
IPv6Address,
IPv6Interface,
IPv6Network,
)
from pathlib import Path, PurePath
from re import Pattern
from types import GeneratorType
from typing import Annotated, Any
from uuid import UUID
from annotated_doc import Doc
from fastapi.exceptions import PydanticV1NotSupportedError
from fastapi.types import IncEx
from pydantic import BaseModel
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
from pydantic_core import PydanticUndefinedType
from ._compat import (
Url,
is_pydantic_v1_model_instance,
)
try:
# pydantic.color.Color is deprecated since v2.0b3, but supporting for bwd-compat
from pydantic.color import Color # ty: ignore[deprecated]
except ImportError: # pragma: no cover
class Color: # type: ignore[no-redef]
pass
try:
# Supporting the new Color format for newer versions of Pydantic
from pydantic_extra_types.color import Color as PyExtraColor
except ImportError: # pragma: no cover
class PyExtraColor: # type: ignore[no-redef]
pass
# Taken from Pydantic v1 as is
def isoformat(o: datetime.date | datetime.time) -> str:
return o.isoformat()
# Adapted from Pydantic v1
# TODO: pv2 should this return strings instead?
def decimal_encoder(dec_value: Decimal) -> int | float:
"""
Encodes a Decimal as int if there's no exponent, otherwise float
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
where an integer (but not int typed) is used. Encoding this as a float
results in failed round-tripping between encode and parse.
Our Id type is a prime example of this.
>>> decimal_encoder(Decimal("1.0"))
1.0
>>> decimal_encoder(Decimal("1"))
1
>>> decimal_encoder(Decimal("NaN"))
nan
"""
exponent = dec_value.as_tuple().exponent
if isinstance(exponent, int) and exponent >= 0:
return int(dec_value)
else:
return float(dec_value)
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str,
PyExtraColor: str,
datetime.date: isoformat,
datetime.datetime: isoformat,
datetime.time: isoformat,
datetime.timedelta: lambda td: td.total_seconds(),
Decimal: decimal_encoder,
Enum: lambda o: o.value,
frozenset: list,
deque: list,
GeneratorType: list,
IPv4Address: str,
IPv4Interface: str,
IPv4Network: str,
IPv6Address: str,
IPv6Interface: str,
IPv6Network: str,
NameEmail: str,
Path: str,
Pattern: lambda o: o.pattern,
SecretBytes: str,
SecretStr: str,
set: list,
UUID: str,
Url: str,
AnyUrl: str,
}
def generate_encoders_by_class_tuples(
type_encoder_map: dict[Any, Callable[[Any], Any]],
) -> dict[Callable[[Any], Any], tuple[Any, ...]]:
encoders_by_class_tuples: dict[Callable[[Any], Any], tuple[Any, ...]] = defaultdict(
tuple
)
for type_, encoder in type_encoder_map.items():
encoders_by_class_tuples[encoder] += (type_,)
return encoders_by_class_tuples
encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)
def jsonable_encoder(
obj: Annotated[
Any,
Doc(
"""
The input object to convert to JSON.
"""
),
],
include: Annotated[
IncEx | None,
Doc(
"""
Pydantic's `include` parameter, passed to Pydantic models to set the
fields to include.
"""
),
] = None,
exclude: Annotated[
IncEx | None,
Doc(
"""
Pydantic's `exclude` parameter, passed to Pydantic models to set the
fields to exclude.
"""
),
] = None,
by_alias: Annotated[
bool,
Doc(
"""
Pydantic's `by_alias` parameter, passed to Pydantic models to define if
the output should use the alias names (when provided) or the Python
attribute names. In an API, if you set an alias, it's probably because you
want to use it in the result, so you probably want to leave this set to
`True`.
"""
),
] = True,
exclude_unset: Annotated[
bool,
Doc(
"""
Pydantic's `exclude_unset` parameter, passed to Pydantic models to define
if it should exclude from the output the fields that were not explicitly
set (and that only had their default values).
"""
),
] = False,
exclude_defaults: Annotated[
bool,
Doc(
"""
Pydantic's `exclude_defaults` parameter, passed to Pydantic models to define
if it should exclude from the output the fields that had the same default
value, even when they were explicitly set.
"""
),
] = False,
exclude_none: Annotated[
bool,
Doc(
"""
Pydantic's `exclude_none` parameter, passed to Pydantic models to define
if it should exclude from the output any fields that have a `None` value.
"""
),
] = False,
custom_encoder: Annotated[
dict[Any, Callable[[Any], Any]] | None,
Doc(
"""
Pydantic's `custom_encoder` parameter, passed to Pydantic models to define
a custom encoder.
"""
),
] = None,
sqlalchemy_safe: Annotated[
bool,
Doc(
"""
Exclude from the output any fields that start with the name `_sa`.
This is mainly a hack for compatibility with SQLAlchemy objects, they
store internal SQLAlchemy-specific state in attributes named with `_sa`,
and those objects can't (and shouldn't be) serialized to JSON.
"""
),
] = True,
) -> Any:
"""
Convert any object to something that can be encoded in JSON.
This is used internally by FastAPI to make sure anything you return can be
encoded as JSON before it is sent to the client.
You can also use it yourself, for example to convert objects before saving them
in a database that supports only JSON.
Read more about it in the
[FastAPI docs for JSON Compatible Encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
"""
custom_encoder = custom_encoder or {}
if custom_encoder:
if type(obj) in custom_encoder:
return custom_encoder[type(obj)](obj)
else:
for encoder_type, encoder_instance in custom_encoder.items():
if isinstance(obj, encoder_type):
return encoder_instance(obj)
if include is not None and not isinstance(include, (set, dict)):
include = set(include) # type: ignore[assignment] # ty: ignore[invalid-assignment]
if exclude is not None and not isinstance(exclude, (set, dict)):
exclude = set(exclude) # type: ignore[assignment] # ty: ignore[invalid-assignment]
if isinstance(obj, BaseModel):
obj_dict = obj.model_dump(
mode="json",
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_none=exclude_none,
exclude_defaults=exclude_defaults,
)
return jsonable_encoder(
obj_dict,
exclude_none=exclude_none,
exclude_defaults=exclude_defaults,
sqlalchemy_safe=sqlalchemy_safe,
)
if dataclasses.is_dataclass(obj):
assert not isinstance(obj, type)
obj_dict = dataclasses.asdict(obj)
return jsonable_encoder(
obj_dict,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
if isinstance(obj, Enum):
return obj.value
if isinstance(obj, PurePath):
return str(obj)
if isinstance(obj, (str, int, float, type(None))):
return obj
if isinstance(obj, PydanticUndefinedType):
return None
if isinstance(obj, dict):
encoded_dict = {}
allowed_keys = set(obj.keys())
if include is not None:
allowed_keys &= set(include)
if exclude is not None:
allowed_keys -= set(exclude)
for key, value in obj.items():
if (
(
not sqlalchemy_safe
or (not isinstance(key, str))
or (not key.startswith("_sa"))
)
and (value is not None or not exclude_none)
and key in allowed_keys
):
encoded_key = jsonable_encoder(
key,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
encoded_value = jsonable_encoder(
value,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
encoded_dict[encoded_key] = encoded_value
return encoded_dict
if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)):
encoded_list = []
for item in obj:
encoded_list.append(
jsonable_encoder(
item,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
)
return encoded_list
if type(obj) in ENCODERS_BY_TYPE:
return ENCODERS_BY_TYPE[type(obj)](obj)
for encoder, classes_tuple in encoders_by_class_tuples.items():
if isinstance(obj, classes_tuple):
return encoder(obj)
if is_pydantic_v1_model_instance(obj):
raise PydanticV1NotSupportedError(
"pydantic.v1 models are no longer supported by FastAPI."
f" Please update the model {obj!r}."
)
try:
data = dict(obj)
except Exception as e:
errors: list[Exception] = []
errors.append(e)
try:
data = vars(obj)
except Exception as e:
errors.append(e)
raise ValueError(errors) from e
return jsonable_encoder(
data,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
================================================
FILE: fastapi/exception_handlers.py
================================================
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
from fastapi.utils import is_body_allowed_for_status_code
from fastapi.websockets import WebSocket
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.status import WS_1008_POLICY_VIOLATION
async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
headers = getattr(exc, "headers", None)
if not is_body_allowed_for_status_code(exc.status_code):
return Response(status_code=exc.status_code, headers=headers)
return JSONResponse(
{"detail": exc.detail}, status_code=exc.status_code, headers=headers
)
async def request_validation_exception_handler(
request: Request, exc: RequestValidationError
) -> JSONResponse:
return JSONResponse(
status_code=422,
content={"detail": jsonable_encoder(exc.errors())},
)
async def websocket_request_validation_exception_handler(
websocket: WebSocket, exc: WebSocketRequestValidationError
) -> None:
await websocket.close(
code=WS_1008_POLICY_VIOLATION, reason=jsonable_encoder(exc.errors())
)
================================================
FILE: fastapi/exceptions.py
================================================
from collections.abc import Mapping, Sequence
from typing import Annotated, Any, TypedDict
from annotated_doc import Doc
from pydantic import BaseModel, create_model
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.exceptions import WebSocketException as StarletteWebSocketException
class EndpointContext(TypedDict, total=False):
function: str
path: str
file: str
line: int
class HTTPException(StarletteHTTPException):
"""
An HTTP exception you can raise in your own code to show errors to the client.
This is for client errors, invalid authentication, invalid data, etc. Not for server
errors in your code.
Read more about it in the
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/).
## Example
```python
from fastapi import FastAPI, HTTPException
app = FastAPI()
items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}
```
"""
def __init__(
self,
status_code: Annotated[
int,
Doc(
"""
HTTP status code to send to the client.
Read more about it in the
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception)
"""
),
],
detail: Annotated[
Any,
Doc(
"""
Any data to be sent to the client in the `detail` key of the JSON
response.
Read more about it in the
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception)
"""
),
] = None,
headers: Annotated[
Mapping[str, str] | None,
Doc(
"""
Any headers to send to the client in the response.
Read more about it in the
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#add-custom-headers)
"""
),
] = None,
) -> None:
super().__init__(status_code=status_code, detail=detail, headers=headers)
class WebSocketException(StarletteWebSocketException):
"""
A WebSocket exception you can raise in your own code to show errors to the client.
This is for client errors, invalid authentication, invalid data, etc. Not for server
errors in your code.
Read more about it in the
[FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/).
## Example
```python
from typing import Annotated
from fastapi import (
Cookie,
FastAPI,
WebSocket,
WebSocketException,
status,
)
app = FastAPI()
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(
*,
websocket: WebSocket,
session: Annotated[str | None, Cookie()] = None,
item_id: str,
):
if session is None:
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Session cookie is: {session}")
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
```
"""
def __init__(
self,
code: Annotated[
int,
Doc(
"""
A closing code from the
[valid codes defined in the specification](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1).
"""
),
],
reason: Annotated[
str | None,
Doc(
"""
The reason to close the WebSocket connection.
It is UTF-8-encoded data. The interpretation of the reason is up to the
application, it is not specified by the WebSocket specification.
It could contain text that could be human-readable or interpretable
by the client code, etc.
"""
),
] = None,
) -> None:
super().__init__(code=code, reason=reason)
RequestErrorModel: type[BaseModel] = create_model("Request")
WebSocketErrorModel: type[BaseModel] = create_model("WebSocket")
class FastAPIError(RuntimeError):
"""
A generic, FastAPI-specific error.
"""
class DependencyScopeError(FastAPIError):
"""
A dependency declared that it depends on another dependency with an invalid
(narrower) scope.
"""
class ValidationException(Exception):
def __init__(
self,
errors: Sequence[Any],
*,
endpoint_ctx: EndpointContext | None = None,
) -> None:
self._errors = errors
self.endpoint_ctx = endpoint_ctx
ctx = endpoint_ctx or {}
self.endpoint_function = ctx.get("function")
self.endpoint_path = ctx.get("path")
self.endpoint_file = ctx.get("file")
self.endpoint_line = ctx.get("line")
def errors(self) -> Sequence[Any]:
return self._errors
def _format_endpoint_context(self) -> str:
if not (self.endpoint_file and self.endpoint_line and self.endpoint_function):
if self.endpoint_path:
return f"\n Endpoint: {self.endpoint_path}"
return ""
context = f'\n File "{self.endpoint_file}", line {self.endpoint_line}, in {self.endpoint_function}'
if self.endpoint_path:
context += f"\n {self.endpoint_path}"
return context
def __str__(self) -> str:
message = f"{len(self._errors)} validation error{'s' if len(self._errors) != 1 else ''}:\n"
for err in self._errors:
message += f" {err}\n"
message += self._format_endpoint_context()
return message.rstrip()
class RequestValidationError(ValidationException):
def __init__(
self,
errors: Sequence[Any],
*,
body: Any = None,
endpoint_ctx: EndpointContext | None = None,
) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx)
self.body = body
class WebSocketRequestValidationError(ValidationException):
def __init__(
self,
errors: Sequence[Any],
*,
endpoint_ctx: EndpointContext | None = None,
) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx)
class ResponseValidationError(ValidationException):
def __init__(
self,
errors: Sequence[Any],
*,
body: Any = None,
endpoint_ctx: EndpointContext | None = None,
) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx)
self.body = body
class PydanticV1NotSupportedError(FastAPIError):
"""
A pydantic.v1 model is used, which is no longer supported.
"""
class FastAPIDeprecationWarning(UserWarning):
"""
A custom deprecation warning as DeprecationWarning is ignored
Ref: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries
"""
================================================
FILE: fastapi/logger.py
================================================
import logging
logger = logging.getLogger("fastapi")
================================================
FILE: fastapi/middleware/__init__.py
================================================
from starlette.middleware import Middleware as Middleware
================================================
FILE: fastapi/middleware/asyncexitstack.py
================================================
from contextlib import AsyncExitStack
from starlette.types import ASGIApp, Receive, Scope, Send
# Used mainly to close files after the request is done, dependencies are closed
# in their own AsyncExitStack
class AsyncExitStackMiddleware:
def __init__(
self, app: ASGIApp, context_name: str = "fastapi_middleware_astack"
) -> None:
self.app = app
self.context_name = context_name
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
async with AsyncExitStack() as stack:
scope[self.context_name] = stack
await self.app(scope, receive, send)
================================================
FILE: fastapi/middleware/cors.py
================================================
from starlette.middleware.cors import CORSMiddleware as CORSMiddleware # noqa
================================================
FILE: fastapi/middleware/gzip.py
================================================
from starlette.middleware.gzip import GZipMiddleware as GZipMiddleware # noqa
================================================
FILE: fastapi/middleware/httpsredirect.py
================================================
from starlette.middleware.httpsredirect import ( # noqa
HTTPSRedirectMiddleware as HTTPSRedirectMiddleware,
)
================================================
FILE: fastapi/middleware/trustedhost.py
================================================
from starlette.middleware.trustedhost import ( # noqa
TrustedHostMiddleware as TrustedHostMiddleware,
)
================================================
FILE: fastapi/middleware/wsgi.py
================================================
from starlette.middleware.wsgi import (
WSGIMiddleware as WSGIMiddleware,
) # pragma: no cover # noqa
================================================
FILE: fastapi/openapi/__init__.py
================================================
================================================
FILE: fastapi/openapi/constants.py
================================================
METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"}
REF_PREFIX = "#/components/schemas/"
REF_TEMPLATE = "#/components/schemas/{model}"
================================================
FILE: fastapi/openapi/docs.py
================================================
import json
from typing import Annotated, Any
from annotated_doc import Doc
from fastapi.encoders import jsonable_encoder
from starlette.responses import HTMLResponse
def _html_safe_json(value: Any) -> str:
"""Serialize a value to JSON with HTML special characters escaped.
This prevents injection when the JSON is embedded inside a