* Sync with #14217
* Sync with #14359
* Sync with #13786
* Sync with #14070
* Sync with #14120
* Sync with #14211
* Sync with #14405
* "to deploy" -> "deployen"
The LLM used that translation a lot ithis convinced me that "deployen" it is the better word. "bereitstellen" (or "ausliefern") is still used for "to serve".
---------
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
Co-authored-by: Yurii Motov <yurii.motov.monte@gmail.com>
* 🌐 Add Persian translation for docs/fa/docs/python-types.md
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* 🌐 Add Persian translation for docs/fa/docs/async.md
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* Add Russian Translation for `docs/ru/docs/advanced/index.md`
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* 🌐 Add Persian translation for docs/fa/docs/learn/index.md
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* ♻️ Add support for `pip install "fastapi[standard]"` and make `fastapi` not include the optional standard dependencies
* 📝 Update docs to include new fastapi[standard]
* ✨ Add new stub fastapi command that tells people to install fastapi[standard]
* ✅ Add tests for new stub CLI
* 🔧 Add new command fastapi in main fastapi project, for when fastapi-cli is not installed
* ✏️ Fix types
* 📝 Add note about quotes when installing fastapi[standard]
* 📝 Update docs about standard extra dependencies
* ⬆️ Upgrade fastapi-cli
Fix mistake in Ruff configuration unintentionally enabling mccabe complexity check
Enabling "C" turns on complexity checks (C90, mccabe), which is unintended
Instead, enable "C4" to get flake8-comprehensions checks
See docs at https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ♻️ Refactor dependency AsyncExitStack logic, exit dependencies after creating the response, before sending it
* ✅ Update tests for dependencies exit, check they are finished before the response is sent
* 🔥 Remove ExitAsyncStackMiddleware as it's no longer needed
* 📝 Update docs for dependencies with yield
* 📝 Update release notes
* 📝 Add source examples for new dependencies with yield raising
* ✅ Add tests for new dependencies raising after yield
* 📝 Update release notes
* 🔧 Update pre-commit, use ruff format
* ⬆️ Upgrade dependencies, use Ruff for formatting
* 🔧 Update Ruff config
* 🔨 Update lint and format scripts, use Ruff
* 🎨 Format internals with Ruff
* 🎨 Format docs scripts
* 🎨 Format tests
* 🎨 Format extra commas in src for docs
* 📝 Update docs mentioning `@lru_cache()`, use `@lru_cache` instead to keep consistency with the format
* 🎨 Update src for docs, use plain `@lru_cache`
* 🎨 Update src for docs format and docs references
* ➕ Add mkdocstrings and griffe-typingdoc to dependencies
* 🔧 Add mkdocstrings configs to MkDocs
* 📝 Add first WIP reference page
* ⬆️ Upgrade typing-extensions to the minimum version including Doc()
* 📝 Add docs to FastAPI parameters
* 📝 Add docstrings for OpenAPI docs utils
* 📝 Add docstrings for security utils
* 📝 Add docstrings for UploadFile
* 📝 Update docstrings in FastAPI class
* 📝 Add docstrings for path operation methods
* 📝 Add docstring for jsonable_encoder
* 📝 Add docstrings for exceptions
* 📝 Add docstsrings for parameter functions
* 📝 Add docstrings for responses
* 📝 Add docstrings for APIRouter
* ♻️ Sub-class BackgroundTasks to document it with docstrings
* 📝 Update usage of background tasks in dependencies
* ✅ Update tests with new deprecation warnings
* 📝 Add new reference docs
* 🔧 Update MkDocs with new reference docs
* ✅ Update pytest fixture, deprecation is raised only once
* 🎨 Update format for types in exceptions.py
* ♻️ Update annotations in BackgroundTask, `Annotated` can't take ParamSpec's P.args or P.kwargs
* ✏️ Fix typos caught by @pawamoy
* 🔧 Update and fix MkDocstrings configs from @pawamoy tips
* 📝 Update reference docs
* ✏️ Fix typos found by @pawamoy
* ➕ Add HTTPX as a dependency for docs, for the TestClient
* 🔧 Update MkDocs config, rename websockets reference
* 🔇 Add type-ignores for Doc as the stubs haven't been released for mypy
* 🔥 Remove duplicated deprecated notice
* 🔇 Remove typing error for unreleased stub in openapi/docs.py
* ✅ Add tests for UploadFile for coverage
* ⬆️ Upgrade griffe-typingdoc==0.2.2
* 📝 Refactor docs structure
* 🔨 Update README generation with new index frontmatter and style
* 🔨 Update generation of languages, remove from top menu, keep in lang menu
* 📝 Add OpenAPI Pydantic models
* 🔨 Update docs script to not translate Reference and Release Notes
* 🔧 Add reference for OpenAPI models
* 🔧 Update MkDocs config for mkdocstrings insiders
* 👷 Install mkdocstring insiders in CI for docs
* 🐛 Fix MkDocstrings insiders install URL
* ➕ Move dependencies shared by docs and tests to its own requirements file
* 👷 Update cache keys for test and docs dependencies
* 📝 Remove no longer needed __init__ placeholder docstrings
* 📝 Move docstring for APIRouter to the class level (not __init__ level)
* 🔥 Remove no longer needed dummy placeholder __init__ docstring
* 📝 Update docs, require Python 3.8+, drop 3.7
* 🔧 Update pyproject.toml, drop support for Python 3.7, require Python 3.8+
* 👷 Update CI GitHub Actions, drop support for Python 3.7, require 3.8+
* 📝 Update docs' references to Python 3.6 and 3.7, use Python 3.8
* ♻️ Refactor model for OpenAPI Examples to use a reusable TypedDict
* ✨ Add support for openapi_examples in parameters
* 📝 Add new docs examples for new parameter openapi_examples
* 📝 Update docs for Schema Extra to include OpenAPI examples
* ✅ Add tests for new source examples, for openapi_examples
* ✅ Add tests for openapi_examples corner cases and all parameters
* 💡 Tweak and ignore type annotation checks for custom TypedDict
* 📝 Add docs for Separate OpenAPI Schemas for Input and Output
* 🔧 Add new docs page to MkDocs config
* ✨ Add separate_input_output_schemas parameter to FastAPI class
* 📝 Add source examples for separating OpenAPI schemas
* ✅ Add tests for separated OpenAPI schemas
* 📝 Add source examples for Python 3.10, 3.9, and 3.7+
* 📝 Update docs for Separate OpenAPI Schemas with new multi-version examples
* ✅ Add and update tests for different Python versions
* ✅ Add tests for corner cases with separate_input_output_schemas
* 📝 Update tutorial to use Union instead of Optional
* 🐛 Fix type annotations
* 🐛 Fix correct import in test
* 💄 Add CSS to simulate browser windows for screenshots
* ➕ Add playwright as a dev dependency to automate generating screenshots
* 🔨 Add Playwright scripts to generate screenshots for new docs
* 📝 Update docs, tweak text to match screenshots
* 🍱 Add screenshots for new docs
* 📝 Start How To docs section, move Peewee, remove Peewee from dependencies
* 🚚 Move em files to new locations
* 🚚 Move and re-structure advanced docs, move relevant to How To
* 🔧 Update MkDocs config, new files in How To
* 📝 Move docs for Conditional OpenAPI for Japanese to How To
* 📝 Move example source files for Extending OpenAPI into each of the new sections
* ✅ Update tests with new locations for source files
* 🔥 Remove init from Peewee examples
* ✨ Enable Pydantic's serialization mode for responses
* ✅ Update tests with new Pydantic v2 serialization mode
* ✅ Add a test for Pydantic v2's computed_field
* ✨ Pydantic v2 migration, initial implementation (#9500)
* ✨ Add compat layer, for Pydantic v1 and v2
* ✨ Re-export Pydantic needed internals from compat, to later patch them for v1
* ♻️ Refactor internals to use new compatibility layers and run with Pydantic v2
* 📝 Update examples to run with Pydantic v2
* ✅ Update tests to use Pydantic v2
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
* ✅ Temporarily disable Peewee tests, afterwards I'll enable them only for Pydantic v1
* 🐛 Fix JSON Schema generation and OpenAPI ref template
* 🐛 Fix model field creation with defaults from Pydantic v2
* 🐛 Fix body field creation, with new FieldInfo
* ✨ Use and check new ResponseValidationError for server validation errors
* ✅ Fix test_schema_extra_examples tests with ResponseValidationError
* ✅ Add dirty-equals to tests for compatibility with Pydantic v1 and v2
* ✨ Add util to regenerate errors with custom loc
* ✨ Generate validation errors with loc
* ✅ Update tests for compatibility with Pydantic v1 and v2
* ✅ Update tests for Pydantic v2 in tests/test_filter_pydantic_sub_model.py
* ✅ Refactor tests in tests/test_dependency_overrides.py for Pydantic v2, separate parameterized into independent tests to use insert_assert
* ✅ Refactor OpenAPI test for tests/test_infer_param_optionality.py for consistency, and make it compatible with Pydantic v1 and v2
* ✅ Update tests for tests/test_multi_query_errors.py for Pydantic v1 and v2
* ✅ Update tests for tests/test_multi_body_errors.py for Pydantic v1 and v2
* ✅ Update tests for tests/test_multi_body_errors.py for Pydantic v1 and v2
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
* ♻️ Refactor tests for tests/test_path.py to inline pytest parameters, to make it easier to make them compatible with Pydantic v2
* ✅ Refactor and udpate tests for tests/test_path.py for Pydantic v1 and v2
* ♻️ Refactor and update tests for tests/test_query.py with compatibility for Pydantic v1 and v2
* ✅ Fix test with optional field without default None
* ✅ Update tests for compatibility with Pydantic v2
* ✅ Update tutorial tests for Pydantic v2
* ♻️ Update OAuth2 dependencies for Pydantic v2
* ♻️ Refactor str check when checking for sequence types
* ♻️ Rename regex to pattern to keep in sync with Pydantic v2
* ♻️ Refactor _compat.py, start moving conditional imports and declarations to specifics of Pydantic v1 or v2
* ✅ Update tests for OAuth2 security optional
* ✅ Refactor tests for OAuth2 optional for Pydantic v2
* ✅ Refactor tests for OAuth2 security for compatibility with Pydantic v2
* 🐛 Fix location in compat layer for Pydantic v2 ModelField
* ✅ Refactor tests for Pydantic v2 in tests/test_tutorial/test_bigger_applications/test_main_an_py39.py
* 🐛 Add missing markers in Python 3.9 tests
* ✅ Refactor tests for bigger apps for consistency with annotated ones and with support for Pydantic v2
* 🐛 Fix jsonable_encoder with new Pydantic v2 data types and Url
* 🐛 Fix invalid JSON error for compatibility with Pydantic v2
* ✅ Update tests for behind_a_proxy for Pydantic v2
* ✅ Update tests for tests/test_tutorial/test_body/test_tutorial001_py310.py for Pydantic v2
* ✅ Update tests for tests/test_tutorial/test_body/test_tutorial001.py with Pydantic v2 and consistency with Python 3.10 tests
* ✅ Fix tests for tutorial/body_fields for Pydantic v2
* ✅ Refactor tests for tutorial/body_multiple_params with Pydantic v2
* ✅ Update tests for tutorial/body_nested_models for Pydantic v2
* ✅ Update tests for tutorial/body_updates for Pydantic v2
* ✅ Update test for tutorial/cookie_params for Pydantic v2
* ✅ Fix tests for tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py for Pydantic v2
* ✅ Update tests for tutorial/dataclasses for Pydantic v2
* ✅ Update tests for tutorial/dependencies for Pydantic v2
* ✅ Update tests for tutorial/extra_data_types for Pydantic v2
* ✅ Update tests for tutorial/handling_errors for Pydantic v2
* ✅ Fix test markers for Python 3.9
* ✅ Update tests for tutorial/header_params for Pydantic v2
* ✅ Update tests for Pydantic v2 in tests/test_tutorial/test_openapi_callbacks/test_tutorial001.py
* ✅ Fix extra tests for Pydantic v2
* ✅ Refactor test for parameters, to later fix Pydantic v2
* ✅ Update tests for tutorial/query_params for Pydantic v2
* ♻️ Update examples in docs to use new pattern instead of the old regex
* ✅ Fix several tests for Pydantic v2
* ✅ Update and fix test for ResponseValidationError
* 🐛 Fix check for sequences vs scalars, include bytes as scalar
* 🐛 Fix check for complex data types, include UploadFile
* 🐛 Add list to sequence annotation types
* 🐛 Fix checks for uploads and add utils to find if an annotation is an upload (or bytes)
* ✨ Add UnionType and NoneType to compat layer
* ✅ Update tests for request_files for compatibility with Pydantic v2 and consistency with other tests
* ✅ Fix testsw for request_forms for Pydantic v2
* ✅ Fix tests for request_forms_and_files for Pydantic v2
* ✅ Fix tests in tutorial/security for compatibility with Pydantic v2
* ⬆️ Upgrade required version of email_validator
* ✅ Fix tests for params repr
* ✅ Add Pydantic v2 pytest markers
* Use match_pydantic_error_url
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
* Use field_serializer instead of encoders in some tests
* Show Undefined as ... in repr
* Mark custom encoders test with xfail
* Update test to reflect new serialization of Decimal as str
* Use `model_validate` instead of `from_orm`
* Update JSON schema to reflect required nullable
* Add dirty-equals to pyproject.toml
* Fix locs and error creation for use with pydantic 2.0a4
* Use the type adapter for serialization. This is hacky.
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
* ✅ Refactor test_multi_body_errors for compatibility with Pydantic v1 and v2
* ✅ Refactor test_custom_encoder for Pydantic v1 and v2
* ✅ Set input to None for now, for compatibility with current tests
* 🐛 Fix passing serialization params to model field when handling the response
* ♻️ Refactor exceptions to not depend on Pydantic ValidationError class
* ♻️ Revert/refactor params to simplify repr
* ✅ Tweak tests for custom class encoders for Pydantic v1 and v2
* ✅ Tweak tests for jsonable_encoder for Pydantic v1 and v2
* ✅ Tweak test for compatibility with Pydantic v1 and v2
* 🐛 Fix filtering data with subclasses
* 🐛 Workaround examples in OpenAPI schema
* ✅ Add skip marker for SQL tutorial, needs to be updated either way
* ✅ Update test for broken JSON
* ✅ Fix test for broken JSON
* ✅ Update tests for timedeltas
* ✅ Fix test for plain text validation errors
* ✅ Add markers for Pydantic v1 exclusive tests (for now)
* ✅ Update test for path_params with enums for compatibility with Pydantic v1 and v2
* ✅ Update tests for extra examples in OpenAPI
* ✅ Fix tests for response_model with compatibility with Pydantic v1 and v2
* 🐛 Fix required double serialization for different types of models
* ✅ Fix tests for response model with compatibility with new Pydantic v2
* 🐛 Import Undefined from compat layer
* ✅ Fix tests for response_model for Pydantic v2
* ✅ Fix tests for schema_extra for Pydantic v2
* ✅ Add markers and update tests for Pydantic v2
* 💡 Comment out logic for double encoding that breaks other usecases
* ✅ Update errors for int parsing
* ♻️ Refactor re-enabling compatibility for Pydantic v1
* ♻️ Refactor OpenAPI utils to re-enable support for Pydantic v1
* ♻️ Refactor dependencies/utils and _compat for compatibility with Pydantic v1
* 🐛 Fix and tweak compatibility with Pydantic v1 and v2 in dependencies/utils
* ✅ Tweak tests and examples for Pydantic v1
* ♻️ Tweak call to ModelField.validate for compatibility with Pydantic v1
* ✨ Use new global override TypeAdapter from_attributes
* ✅ Update tests after updating from_attributes
* 🔧 Update pytest config to avoid collecting tests from docs, useful for editor-integrated tests
* ✅ Add test for data filtering, including inheritance and models in fields or lists of models
* ♻️ Make OpenAPI models compatible with both Pydantic v1 and v2
* ♻️ Fix compatibility for Pydantic v1 and v2 in jsonable_encoder
* ♻️ Fix compatibility in params with Pydantic v1 and v2
* ♻️ Fix compatibility when creating a FieldInfo in Pydantic v1 and v2 in utils.py
* ♻️ Fix generation of flat_models and JSON Schema definitions in _compat.py for Pydantic v1 and v2
* ♻️ Update handling of ErrorWrappers for Pydantic v1
* ♻️ Refactor checks and handling of types an sequences
* ♻️ Refactor and cleanup comments with compatibility for Pydantic v1 and v2
* ♻️ Update UploadFile for compatibility with both Pydantic v1 and v2
* 🔥 Remove commented out unneeded code
* 🐛 Fix mock of get_annotation_from_field_info for Pydantic v1 and v2
* 🐛 Fix params with compatibility for Pydantic v1 and v2, with schemas and new pattern vs regex
* 🐛 Fix check if field is sequence for Pydantic v1
* ✅ Fix tests for custom_schema_fields, for compatibility with Pydantic v1 and v2
* ✅ Simplify and fix tests for jsonable_encoder with compatibility for Pydantic v1 and v2
* ✅ Fix tests for orm_mode with Pydantic v1 and compatibility with Pydantic v2
* ♻️ Refactor logic for normalizing Pydantic v1 ErrorWrappers
* ♻️ Workaround for params with examples, before defining what to deprecate in Pydantic v1 and v2 for examples with JSON Schema vs OpenAPI
* ✅ Fix tests for Pydantic v1 and v2 for response_by_alias
* ✅ Fix test for schema_extra with compatibility with Pydantic v1 and v2
* ♻️ Tweak error regeneration with loc
* ♻️ Update error handling and serializationwith compatibility for Pydantic v1 and v2
* ♻️ Re-enable custom encoders for Pydantic v1
* ♻️ Update ErrorWrapper reserialization in Pydantic v1, do it outside of FastAPI ValidationExceptions
* ✅ Update test for filter_submodel, re-structure to simplify testing while keeping division of Pydantic v1 and v2
* ✅ Refactor Pydantic v1 only test that requires modifying environment variables
* 🔥 Update test for plaintext error responses, for Pydantic v1 and v2
* ⏪️ Revert changes in DB tutorial to use Pydantic v1 (the new guide will have SQLModel)
* ✅ Mark current SQL DB tutorial tests as Pydantic only
* ♻️ Update datastructures for compatibility with Pydantic v1, not requiring pydantic-core
* ♻️ Update encoders.py for compatibility with Pydantic v1
* ⏪️ Revert changes to Peewee, the docs for that are gonna live in a new HowTo section, not in the main tutorials
* ♻️ Simplify response body kwargs generation
* 🔥 Clean up comments
* 🔥 Clean some tests and comments
* ✅ Refactor tests to match new Pydantic error string URLs
* ✅ Refactor tests for recursive models for Pydantic v1 and v2
* ✅ Update tests for Peewee, re-enable, Pydantic-v1-only
* ♻️ Update FastAPI params to take regex and pattern arguments
* ⏪️ Revert tutorial examples for pattern, it will be done in a subsequent PR
* ⏪️ Revert changes in schema extra examples, it will be added later in a docs-specific PR
* 💡 Add TODO comment to document str validations with pattern
* 🔥 Remove unneeded comment
* 📌 Upgrade Pydantic pin dependency
* ⬆️ Upgrade email_validator dependency
* 🐛 Tweak type annotations in _compat.py
* 🔇 Tweak mypy errors for compat, for Pydantic v1 re-imports
* 🐛 Tweak and fix type annotations
* ➕ Update requirements-test.txt, re-add dirty-equals
* 🔥 Remove unnecessary config
* 🐛 Tweak type annotations
* 🔥 Remove unnecessary type in dependencies/utils.py
* 💡 Update comment in routing.py
---------
Co-authored-by: David Montague <35119617+dmontagu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* 👷 Add CI for both Pydantic v1 and v2 (#9688)
* 👷 Test and install Pydantic v1 and v2 in CI
* 💚 Tweak CI config for Pydantic v1 and v2
* 💚 Fix Pydantic v2 specification in CI
* 🐛 Fix type annotations for compatibility with Python 3.7
* 💚 Install Pydantic v2 for lints
* 🐛 Fix type annotations for Pydantic v2
* 💚 Re-use test cache for lint
* ♻️ Refactor internals for test coverage and performance (#9691)
* ♻️ Tweak import of Annotated from typing_extensions, they are installed anyway
* ♻️ Refactor _compat to define functions for Pydantic v1 or v2 once instead of checking inside
* ✅ Add test for UploadFile for Pydantic v2
* ♻️ Refactor types and remove logic for impossible cases
* ✅ Add missing tests from test refactor for path params
* ✅ Add tests for new decimal encoder
* 💡 Add TODO comment for decimals in encoders
* 🔥 Remove unneeded dummy function
* 🔥 Remove section of code in field_annotation_is_scalar covered by sub-call to field_annotation_is_complex
* ♻️ Refactor and tweak variables and types in _compat
* ✅ Add tests for corner cases and compat with Pydantic v1 and v2
* ♻️ Refactor type annotations
* 🔖 Release version 0.100.0-beta1
* ♻️ Refactor parts that use optional requirements to make them compatible with installations without them (#9707)
* ♻️ Refactor parts that use optional requirements to make them compatible with installations without them
* ♻️ Update JSON Schema for email field without email-validator installed
* 🐛 Fix support for Pydantic v2.0, small changes in their final release (#9771)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 🔖 Release version 0.100.0-beta2
* ✨ OpenAPI 3.1.0 with Pydantic v2, merge `master` (#9773)
* ➕ Add dirty-equals as a testing dependency (#9778)
➕ Add dirty-equals as a testing dependency, it seems it got lsot at some point
* 🔀 Merge master, fix valid JSON Schema accepting bools (#9782)
* ⏪️ Revert usage of custom logic for TypeAdapter JSON Schema, solved on the Pydantic side (#9787)
⏪️ Revert usage of custom logic for TypeAdapter JSON Schema, solved on Pydantic side
* ♻️ Deprecate parameter `regex`, use `pattern` instead (#9786)
* 📝 Update docs to deprecate regex, recommend pattern
* ♻️ Update examples to use new pattern instead of regex
* 📝 Add new example with deprecated regex
* ♻️ Add deprecation notes and warnings for regex
* ✅ Add tests for regex deprecation
* ✅ Update tests for compatibility with Pydantic v1
* ✨ Update docs to use Pydantic v2 settings and add note and example about v1 (#9788)
* ➕ Add pydantic-settings to all extras
* 📝 Update docs for Pydantic settings
* 📝 Update Settings source examples to use Pydantic v2, and add a Pydantic v1 version
* ✅ Add tests for settings with Pydantic v1 and v2
* 🔥 Remove solved TODO comment
* ♻️ Update conditional OpenAPI to use new Pydantic v2 settings
* ✅ Update tests to import Annotated from typing_extensions for Python < 3.9 (#9795)
* ➕ Add pydantic-extra-types to fastapi[extra]
* ➕ temp: Install Pydantic from source to test JSON Schema metadata fixes (#9777)
* ➕ Install Pydantic from source, from branch for JSON Schema with metadata
* ➕ Update dependencies, install Pydantic main
* ➕ Fix dependency URL for Pydantic from source
* ➕ Add pydantic-settings for test requirements
* 💡 Add TODO comments to re-enable Pydantic main (not from source) (#9796)
* ✨ Add new Pydantic Field param options to Query, Cookie, Body, etc. (#9797)
* 📝 Add docs for Pydantic v2 for `docs/en/docs/advanced/path-operation-advanced-configuration.md` (#9798)
* 📝 Update docs in examples for settings with Pydantic v2 (#9799)
* 📝 Update JSON Schema `examples` docs with Pydantic v2 (#9800)
* ♻️ Use new Pydantic v2 JSON Schema generator (#9813)
Co-authored-by: David Montague <35119617+dmontagu@users.noreply.github.com>
* ♻️ Tweak type annotations and Pydantic version range (#9801)
* 📌 Re-enable GA Pydantic, for v2, require minimum 2.0.2 (#9814)
* 🔖 Release version 0.100.0-beta3
* 🔥 Remove duplicate type declaration from merge conflicts (#9832)
* 👷♂️ Run tests with Pydantic v2 GA (#9830)
👷 Run tests for Pydantic v2 GA
* 📝 Add notes to docs expecting Pydantic v2 and future updates (#9833)
* 📝 Update index with new extras
* 📝 Update release notes
---------
Co-authored-by: David Montague <35119617+dmontagu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Pastukhov Nikita <diementros@yandex.ru>
* 🐛 Fix JSON Schema accepting bools as valid JSON Schemas, e.g. additionalProperties: false
* ✅ Add test to ensure additionalProperties can be false
* ♻️ Tweak OpenAPI models to support Pydantic v1's JSON Schema for tuples
* 📝 Update source examples to use new JSON Schema examples field
* ✅ Update tests for JSON Schema examples
* 📝 Update highlights in JSON Schema examples
* ✨ Update OpenAPI models for JSON Schema 2020-12 and OpenAPI 3.1.0
* ✨ Add support for summary and webhooks
* ✨ Update JSON Schema for UploadFiles
* ⏪️ Revert making paths optional, to ensure always correctness
* ⏪️ Keep UploadFile as format: binary for compatibility with the rest of Pydantic bytes fields in v1
* ✨ Update version of OpenAPI generated to 3.1.0
* ✨ Update the version of Swagger UI
* 📝 Update docs about extending OpenAPI
* 📝 Update docs and links to refer to OpenAPI 3.1.0
* ✨ Update logic for handling webhooks
* ♻️ Update parameter functions and classes, deprecate example and make examples the main field
* ✅ Update tests for OpenAPI 3.1.0
* 📝 Update examples for OpenAPI metadata
* ✅ Add and update tests for OpenAPI metadata
* 📝 Add source example for webhooks
* 📝 Update docs for metadata
* 📝 Update docs for Schema extra
* 📝 Add docs for webhooks
* 🔧 Add webhooks docs to MkDocs
* ✅ Update tests for extending OpenAPI
* ✅ Add tests for webhooks
* ♻️ Refactor generation of OpenAPI and JSON Schema with params
* 📝 Update source examples for field examples
* ✅ Update tests for examples
* ➕ Make sure the minimum version of typing-extensions installed has deprecated() (already a dependency of Pydantic)
* ✏️ Fix typo in Webhooks example code
* 🔥 Remove commented out code of removed nullable field
* 🗑️ Add deprecation warnings for example argument
* ✅ Update tests to check for deprecation warnings
* ✅ Add test for webhooks with security schemes, for coverage
* 🍱 Update image for metadata, with new summary
* 🍱 Add docs image for Webhooks
* 📝 Update docs for webhooks, add docs UI image
* ➕ Add dependencies for MkDocs Insiders
* 🙈 Add Insider's .cache to .gitignore
* 🔧 Update MkDocs configs for Insiders
* 💄 Add custom Insiders card layout, while the custom logo is provided from upstream
* 🔨 Update docs.py script to dynamically enable insiders if it's installed
* 👷 Add cache for MkDocs Material Insiders' cards
* 🔊 Add a small log to the docs CLI
* 🔊 Tweak logs, only after exporting languages
* 🐛 Fix accessing non existing env var
* 🔧 Invalidate deps cache
* 🔧 Tweak cache IDs
* 👷 Update cache for installing insiders
* 🔊 Log insiders
* 💚 Invalidate cache
* 👷 Tweak cache keys
* 👷 Trigger CI and test cache
* 🔥 Remove cache comment
* ⚡️ Optimize cache usage for first runs of docs
* 👷 Tweak cache for MkDocs Material cards
* 💚 Trigger CI to test cache
* ✨ Add MkDocs hooks to re-use all config from en, and auto-generate missing docs files form en
* 🔧 Update MkDocs config for es
* 🔧 Simplify configs for all languages
* ✨ Compute available languages from MkDocs Material for config overrides in hooks
* 🔧 Update config for MkDocs for en, to make paths compatible for other languages
* ♻️ Refactor scripts/docs.py to remove all custom logic that is now handled by the MkDocs hooks
* 🔧 Remove ta language as it's incomplete (no translations and causing errors)
* 🔥 Remove ta lang, no translations available
* 🔥 Remove dummy overrides directories, no longer needed
* ✨ Use the same missing-translation.md file contents for hooks
* ⏪️ Restore and refactor new-lang command
* 📝 Update docs for contributing with new simplified workflow for translations
* 🔊 Enable logs so that MkDocs can show its standard output on the docs.py script
Update for docs/tutorial/schema-extra-example.md
When working on the translation, I noticed that this page is missing the annotated tips that can be found in the rest of the documentation (I checked, and it's the only page where they're missing).
Set minimal hatchling version needed to build the package
Set the minimal hatchling version that is needed to build fastapi to
1.13.0. Older versions fail to build because they do not recognize
the trove classifiers used, e.g. 1.12.2 yields:
ValueError: Unknown classifier in field `project.classifiers`: Framework :: Pydantic
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Fix: copy FieldInfo from Annotated arguments
We need to copy the field_info to prevent ourselves from
mutating it. This allows multiple path or nested routers ,etc.
* 📝 Add comment in fastapi/dependencies/utils.py
Co-authored-by: Nadav Zingerman <7372858+nzig@users.noreply.github.com>
* ✅ Extend and tweak tests for Annotated
* ✅ Tweak coverage, it's probably covered by a different version of Python
---------
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
Co-authored-by: Nadav Zingerman <7372858+nzig@users.noreply.github.com>
* 🌐💬🩺 🦲
* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
* 🛠️😊
* ♻️ Rename emoji lang from emj to em, and main docs name as 😉
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Axd1x8a <26704473+FeeeeK@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 🍱 Add new source examples with Annotated for Query Params and String Validations
* 📝 Add new docs with Annotated for Query Params and String Validations
* 🚚 Rename incorrectly named tests for Query Params and str validations
* ✅ Add new tests with Annotated for Query Params and Sring Validations examples
* 🍱 Add new examples with Annotated for Intro to Python Types
* 📝 Update Python Types Intro, include Annotated
* 🎨 Fix formatting in Query params and string validation, and highlight
* 🍱 Add new Annotated source examples for Path Params and Numeric Validations
* 📝 Update docs for Path Params and Numeric Validations with Annotated
* 🍱 Add new source examples with Annotated for Body - Multiple Params
* 📝 Update docs with Annotated for Body - Multiple Parameters
* ✅ Add test for new Annotated examples in Body - Multiple Parameters
* 🍱 Add new Annotated source examples for Body Fields
* 📝 Update docs for Body Fields with new Annotated examples
* ✅ Add new tests for new Annotated examples for Body Fields
* 🍱 Add new Annotated source examples for Schema Extra (Example Data)
* 📝 Update docs for Schema Extra with Annotated
* ✅ Add tests for new Annotated examples for Schema Extra
* 🍱 Add new Annnotated source examples for Extra Data Types
* 📝 Update docs with Annotated for Extra Data Types
* ✅ Add tests for new Annotated examples for Extra Data Types
* 🍱 Add new Annotated source examples for Cookie Parameters
* 📝 Update docs for Cookie Parameters with Annotated examples
* ✅ Add tests for new Annotated source examples in Cookie Parameters
* 🍱 Add new Annotated examples for Header Params
* 📝 Update docs with Annotated examples for Header Parameters
* ✅ Add tests for new Annotated examples for Header Params
* 🍱 Add new Annotated examples for Form Data
* 📝 Update Annotated docs for Form Data
* ✅ Add tests for new Annotated examples in Form Data
* 🍱 Add new Annotated source examples for Request Files
* 📝 Update Annotated docs for Request Files
* ✅ Test new Annotated examples for Request Files
* 🍱 Add new Annotated source examples for Request Forms and Files
* ✅ Add tests for new Anotated examples for Request Forms and Files
* 🍱 Add new Annotated source examples for Dependencies and Advanced Dependencies
* ✅ Add tests for new Annotated dependencies
* 📝 Add new docs for using Annotated with dependencies including type aliases
* 📝 Update docs for Classes as Dependencies with Annotated
* 📝 Update docs for Sub-dependencies with Annotated
* 📝 Update docs for Dependencies in path operation decorators with Annotated
* 📝 Update docs for Global Dependencies with Annotated
* 📝 Update docs for Dependencies with yield with Annotated
* 🎨 Update format in example for dependencies with Annotated
* 🍱 Add source examples with Annotated for Security
* ✅ Add tests for new Annotated examples for security
* 📝 Update docs for Security - First Steps with Annotated
* 📝 Update docs for Security: Get Current User with Annotated
* 📝 Update docs for Simple OAuth2 with Password and Bearer with Annotated
* 📝 Update docs for OAuth2 with Password (and hashing), Bearer with JWT tokens with Annotated
* 📝 Update docs for Request Forms and Files with Annotated
* 🍱 Add new source examples for Bigger Applications with Annotated
* ✅ Add new tests for Bigger Applications with Annotated
* 📝 Update docs for Bigger Applications - Multiple Files with Annotated
* 🍱 Add source examples for background tasks with Annotated
* 📝 Update docs for Background Tasks with Annotated
* ✅ Add test for Background Tasks with Anotated
* 🍱 Add new source examples for docs for Testing with Annotated
* 📝 Update docs for Testing with Annotated
* ✅ Add tests for Annotated examples for Testing
* 🍱 Add new source examples for Additional Status Codes with Annotated
* ✅ Add tests for new Annotated examples for Additional Status Codes
* 📝 Update docs for Additional Status Codes with Annotated
* 📝 Update docs for Advanced Dependencies with Annotated
* 📝 Update docs for OAuth2 scopes with Annotated
* 📝 Update docs for HTTP Basic Auth with Annotated
* 🍱 Add source examples with Annotated for WebSockets
* ✅ Add tests for new Annotated examples for WebSockets
* 📝 Update docs for WebSockets with new Annotated examples
* 🍱 Add source examples with Annotated for Settings and Environment Variables
* 📝 Update docs for Settings and Environment Variables with Annotated
* 🍱 Add new source examples for testing dependencies with Annotated
* ✅ Add tests for new examples for testing dependencies
* 📝 Update docs for testing dependencies with new Annotated examples
* ✅ Update and fix marker for Python 3.9 test
* 🔧 Update Ruff ignores for source examples in docs
* ✅ Fix some tests in the grid for Python 3.9 (incorrectly testing 3.10)
* 🔥 Remove source examples and tests for (non existent) docs section about Annotated, as it's covered in all the rest of the docs
* 📝 Add docs recommending Union over Optional
* 📝 Update docs recommending Union over Optional
* 📝 Update source examples for docs, recommend Union over Optional
* 📝 Update highlighted lines with updated source examples
* 📝 Update highlighted lines in Markdown with recent code changes
* 📝 Update docs, use Union instead of Optional
* ♻️ Update source examples to recommend Union over Optional
* 🎨 Update highlighted code in Markdown after moving from Optional to Union
* ✨ Do not require default value in Query(), Path(), Header(), etc
* 📝 Update source examples for docs with default and required values
* ✅ Update tests with new default values and not required Ellipsis
* 📝 Update docs for Query params and update info about default value, required, Ellipsis
* 🔧 Add first pre-commit config
* 🎨 Format YAML files with pre-commit
* 🎨 Format Markdown with pre-commit
* 🎨 Format SVGs, drawio, JS, HTML with pre-commit
* ➕ Add pre-commit to dev dependencies
* ⬇️ Extend pre-commit range to support Python 3.6
A beginner article in German to get started with the FastAPI with a small Todo API as an example.
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ⬆️ Upgrade Uvicorn when installing fastapi[all] to the latest version, including uvloop
* ⬆️ Relax Uvicorn version range to the minimum supported
in case anyone depends older versions somehow installed with FastAPI extras
* ✨ Add language selector widget
* 🔧 Update script to re-generate MkDocs configs, including langs widget
* 🔧 Update languages MkDocs configs, with lang selector widget
* 🙈 Add .gitignore files to keep overrides directories for translations to fix serving live locally during translations
* ♻️ Refactor docs scripts to handle language overrides (newsletter notification)
* ✨ Add Default and DefaultPlaceholder data structures
to handle defaults and overrides
* ✨ Add utils to get values by priority handling DefaultPlaceholders
* ✨ Add support for top-level parameters in FastAPI, APIRouter, include_router
including: prefix, tags, dependencies, deprecated, include_in_schema, responses, default_response_class, callbacks
* ♻️ Update openapi utils to handle DefaultPlaceholder for response_class
* 📝 Update bigger-application example code to use top-level params
and showcase them in APIRouter, FastAPI, include_router
* 📝 Update docs for Bigger Applications, include diagrams, top-level params
* 🔥 Simplify code and docs for callbacks as default_response_class is no longer required
* 📝 Add docs for top-level dependencies, in FastAPI()
* 📝 Add docs reference to top-level dependencies in docs for decorator
* ✅ Update/increase tests for Bigger Applications including shared parameters
* ✅ Add tests for top-level dependencies in FastAPI()
* ✅ Add tests for internal DefaultPlaceholder
* ✅ Update/increase tests for callbacks with top-level parameters
* ✅ Add LOTS of tests covering branches and cases for shared parameters
in top-level FastAPI, path operations, include_router, APIRouter, its path operations, nested include_router, nested APIRouter, and its path operations
* 🎨 Format/reorder parameters for consistency in FastAPI, APIRouter, include_router
* ⬆️ Upgrade Material for MkDocs
* ⬆️ Install Material for MkDocs Insiders on CI
* 🔧 Update MkDocs configs to use Material for MkDocs Insiders
* ✨ Use the lightbulb because it looks nice 💡
* 💚 Fix GitHub Action workflow syntax for building docs
* 🐛 Fix GitHub Actions workflow syntax, strike one ⚾
* docs: fix typo in chapter "Request Body" in "Tutorial - User Guide"
* docs: modify a sentence in chapter "Query Parameters and String Validations" in "Tutorial - User Guide"
* docs: fix two grammatical mistakes in chapter "Path Parameters and Numeric Validations" in "Tutorial - User Guide"
* 🔥 Disable action Watch Docs Previews
* 🔧 Use predefined name for docs artifacts for previews
* ✨ Add new GitHub Action Comment Docs Preview in PR
* 🔧 Refactor GitHub Action Preview Docs to work as workflow_run using new action to extract where to comment
* 👥 Update FastAPI People
* ✨ Add first version of FastAPI People GitHub action code
* 🐳 Add Docker and configs for the FastAPI People GitHub Action
* 👷 Add GitHub Action workflow for FastAPI People
* 📝 Add FastAPI People to docs
* 💄 Add custom CSSs for FastAPI People
* 🔥 Remove support for Pydantic < 1.0
* 🔥 Remove deprecated skip_defaults from jsonable_encoder and set default for exclude to None, as in Pydantic
* ♻️ Set default of response_model_exclude=None as in Pydantic
* ⬆️ Require Pydantic >=1.0.0 in requirements
* Check if Form exists and multipart is in virtual environment
* Remove unused import
* Move BodyFieldInfo check to separate helper function
* Fix type UploadFile to File for BodyFieldInfo check
* Working solution. Kind of nasty though.
* Use better method of determing if correct package imported
* Use better method of determing if correct package imported
* Add raising exceptions, update error messages
* Check if Form exists and multipart is in virtual environment
* Move BodyFieldInfo check to separate helper function
* Fix type UploadFile to File for BodyFieldInfo check
* Use better method of determing if correct package imported
* Add raising exceptions, update error messages
* Removed unused import, added comments
Co-authored-by: Christopher Nguyen <chrisngyn99@gmail.com>
* Updated what kind of exception will be thrown
* Add type annotations
Adds annotations to is_form_data
* Fix import order
* Add basic tests
* Fixed Travis tests
* Replace logging with fastapi logger
* Change AttributeError to ImportError to fix exception handling
* Fixing tests
* Catch ModuleNotFoundError first
Fix code coverage
* Update fastapi/dependencies/utils.py
Remove error spaces when printing
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
* Update fastapi/dependencies/utils.py
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
* Removed spaces in error printing
* ♻️ Refactor form data detection
* ✅ Update/increase tests for incorrect multipart install
* 🔥 Remove deprecated Travis (moved to GitHub Actions)
Co-authored-by: yk396 <yk396@cornell.edu>
Co-authored-by: Christopher Nguyen <chrisngyn99@gmail.com>
Co-authored-by: Kai Chen <kaichen120@gmail.com>
Co-authored-by: Chris N <hello@chris-nguyen.me>
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
* Add link in sql-databases.md tutorial section to async-sql-databases.md in advanced section.
* 🎨 Update note format
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 🔥 Remove deploy badge that won't show correctly until next release
after the fixes to the Gitter bot
* 🐛 Fix GitHub Action to upload docs artifacts with commit from PR, not pre-merge
* ♻️ Run zip docs and artifact upload only on PRs
* 🐛 Fix Gitter notification, use development gitter room until next release
* 🔥 Remove trigger docs preview step from build-docs workflow
as it requires a more privileged token, so it's now triggered by the preview docs watcher
* 🔊 Dump context when building to allow debugging how to refactor the Gitter bot
* add test for get request body's openapi schema
* 📝 Update docs note for GET requests with body
* ✅ Update test for GET request with body, test it receives the body
* 🔇 Temporary type ignore while it's handled in Pydantic
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 📝 Update JWT docs with python-jose
* 📝 Update format and use python-jose in docs
* ➕ Add Python-jose to dependencies
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* root_path included in servers object instead of path prefix
* ♻️ Refactor implementation of auto-including root_path in OpenAPI servers
* 📝 Update docs and examples for Behind a Proxy, including servers
* 📝 Update Extending OpenAPI as openapi_prefix is no longer needed
* ✅ Add extra tests for root_path in servers and root_path_in_servers=False
* 🍱 Update security docs images with relative token URL
* 📝 Update security docs with relative token URL
* 📝 Update example sources with relative token URLs
* ✅ Update tests with relative tokens
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ➕ Add mkdocs-markdownextradata-plugin for docs
* 🔧 Update MkDocs config file(s) to include external data
* ✨ Add external links data file
* 📝 Use external data file in External Links
* ♻️ Update data files for langs
The cost is some duplication 😔, these files are updated by the script, but to be able to serve locally they have to be duplicated
* ✨ Update docs script to copy data files
* 🔥 Remove needed duplication of data files for live docs in translations
* ♻️ Remove required extra steps to test in editor
* 🎨 Format lint script
* 📝 Remove obsolete extra steps required to test in editor from docs
* 🐛 Fix coverage
* Update isort script to match changes in the new release, isort v5.0.2
* Downgrade isort to version v4.3.21
* Add an alternative flag to --recursive in isort v5.0.2
* Add isort config file
* 🚚 Import from docs_src for tests
* 🎨 Format dependencies.utils
* 🎨 Remove isort combine_as_imports, keep black profile
* 🔧 Update isort config, use pyproject.toml, Black profile
* 🔧 Update format scripts to use explicit directories to format
otherwise it would try to format venv env directories, I have several with different Python versions
* 🎨 Format NoSQL tutorial after re-sorting imports
* 🎨 Fix format for __init__.py
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Updated .py files with Optional tag (up to body_nested_models)
* Update optionals
* docs_src/ all updates, few I was unsure of
* Updated markdown files with Optional param
* es: Add Optional typing to index.md
* Last of markdown files updated with Optional param
* Update highlight lines
* it: Add Optional typings
* README.md: Update with Optional typings
* Update more highlight increments
* Update highlights
* schema-extra-example.md: Update highlights
* updating highlighting on website to reflect .py changes
* Update highlighting for query-params & response-directly
* Address PR comments
* Get rid of unnecessary comment
* ⏪ Revert Optional in Chinese docs as it probably also requires changes in text
* 🎨 Apply format
* ⏪ Revert modified example
* ♻️ Simplify example in docs
* 📝 Update OpenAPI callback example to use Optional
* ✨ Add Optional types to tests
* 📝 Update docs about query params, default to using Optional
* 🎨 Update code examples line highlighting
* 📝 Update nested models docs to use "type parameters" instead of "subtypes"
* 📝 Add notes about FastAPI usage of None
including:
= None
and
= Query(None)
and clarify relationship with Optional[str]
* 📝 Add note about response_model_by_alias
* ♻️ Simplify query param list example
* 🔥 Remove test for removed example
* ✅ Update test for updated example
Co-authored-by: Christopher Nguyen <chrisngyn99@gmail.com>
Co-authored-by: yk396 <yk396@cornell.edu>
Co-authored-by: Kai Chen <kaichen120@gmail.com>
* Make openapi models honor response_model_by_alias
* Add test for response_model_by_alias working with openapi models
* ⏪ Revert changes
* ✅ Update and extend tests for response_model_by_alias
* ⏪ Revert test name change
* 📌 Pin Pytest and Pytest-Cov
Co-authored-by: Martin Zaťko <martin.zatko@kiwi.com>
* feat: add servers option for OpenAPI
Closes#872
* ✨ Use dicts for OpenAPI servers
* ♻️ Update OpenAPI Server model to support relative URLs
* ✅ Add tests for OpenAPI servers
* ♻️ Re-order parameter location of servers for OpenAPI
* 🎨 Format code
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* fix websockets/tutorial002.py
* fix tutorial002 in ws to correspond with test case
* reformat websocket tutorial002
* fix websocket tutorial002 coverage
* 📝 Update example for WebSockets with Depends
* ✅ Update and refactor tests for WebSockets with dependencies
* 👷 Trigger Travis, as it's not reporting to Codecov
* ✅ Update WebSocket tests to raise coverage
Co-authored-by: Chih Sean Hsu <Sean@Sean-Mac.local>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Normalise IntEnums to ints for route status codes
Closes#1349
* add tests for status code enum support
* add docs for status code enum support
* add endpoint test for enum status code
* 📝 Update note about http.HTTPStatus
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ➕ Add docs to default_response_class
* ✅ create a tip
* ✅ fixing the tip
* 🚑 grammar
* 📝 Update docs for default response class
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Documented additional parameters
These are included in a recent PR (https://github.com/tiangolo/fastapi/pull/1166) but not in the docs yet.
* response_model_exclude_none
* response_model_exclude_defaults
* 📝 Update note about response_model_exclude_defaults and response_model_exclude_none
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Added test for repeating cookies in response headers
* update `response` headers, status code to match `sub_response` in `solve_dependencies` only if necessary; fix formatting of scottsmith2gmail's test
* restore code coverage, remove dead code from `solve_dependencies`
Co-authored-by: Scott Smith <scott.smith.2@gmail.com>
* Request body error, raise RequestValidationError instead of HTTPException in case JSON decode failure
* add missing test case for body general exception
* Allow to add OpenAPI tag descriptions
* fix type hint
* fix type hint 2
* refactor test to assure 100% coverage
* 📝 Update tags metadata example
* 📝 Update docs for tags metadata
* ✅ Move tags metadata test to tutorial subdir
* 🎨 Update format in applications
* 🍱 Update docs UI image based on new example
* 🎨 Apply formatting after solving conflicts
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* repr description added to Depends class
* repr description added to Security subclass
* get rid of __repr__ in security since it will inherit from super
* make code format consistent with rest
* add desc for rest of the classes
* Update fastapi/params.py
remove trailing whitespace
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
* Implement __repr__
* fix formatting
* formatting again
* ran formatting
* added basic testing
* basic tests added to rest of the classes
* added more test coverage and simplified test file
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Jayati Shrivastava <gaurijove@gmail.com>
* docs: Fix pydantic example in python-types.md
* 📝 Update Python Types Intro to include Optional
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Fix callable class generator dependencies
* workaround to support asynccontextmanager backfill for pre python3.7
Co-authored-by: Micah Rosales <mrosales@users.noreply.github.com>
* WIP:add Chinese translation for first steps doc
* add Chinese translation for first steps doc
* improve translations
Co-authored-by: Waynerv <wei.xie@woqutech.com>
* drop model class from additional responses when generating openapi
* ♻️ Copy response to be mutated early in get_openapi_path
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Generated new translation directory to support Italian docs
* ⬆️ Upgrade/pin pytest to >= 5.4.3
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Use ASGI root_path when it is provided and openapi_prefix is empty.
* Strip trailing slashes from root_path.
* Please mypy.
* Fix extending openapi test.
* 📝 Add docs and tutorial for using root_path behind a proxy
* ♻️ Refactor application root_path logic, use root_path, deprecate openapi_prefix
* ✅ Add tests for Behind a Proxy with root_path
* ♻️ Refactor test
* 📝 Update/add docs for Sub-applications and Behind a Proxy
* 📝 Update Extending OpenAPI with openapi_prefix parameter
* ✅ Add test for deprecated openapi_prefix
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 📝 Update help docs: Gitter, issues, links
also fix Gitter tab padding
* 📝 Update new GitHub issue templates
* 📝 Add note about extra help required for new issues
* 🐛 Fix extra support for enum with its own schema
* ✅ Fix/update test for enum with its own schema
* 🐛 Fix type declarations
* 🔧 Update format and lint scripts to support locally installed Pydantic and Starlette
* 🐛 Add temporary type ignores while enum schemas are merged
* Fixed Typo in [EN] tutorial: body-fields
- remove duplicate of examples text
* ✏️ Re-word and clarify extra info docs
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ✨ Allow disabling docs UIs by disabling openapi_url
* 📝 Add docs for disabling OpenAPI and docs in prod or other environments
* ✅ Add tests for disabling OpenAPI and docs
* Translate features.md file to Portuguese
* Changes word of features.md translation to Portuguese
* Fixing typos and bad wording
Thanks @Serrones for the kind review
* Spanish translation for the tutorial-user-guide index page
* Improve some parts of the text in terms of writing
* Change the wording to keep the documentation consistent.
* 📝 Add small wording and consistency changes
* 🎨 Apply the same consistency changes to EN 🤷
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Add new language of docs: zh
* Add deployment.md Chinese trans
* add "or"
* rm index.md
* updates Chinese translations of deployement.md
* update translations of deployment.md
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* translation features.md to Chinese
* update review data
* :DOCS: update with review
* 🔥 Remove double link in build mkdocs.yml for other languages
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Update sql-databases alembic docs
Was helpful to refer to the full-stack project when integrating alembic into my own project
* 📝 Update Alembic note in docs
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Implemented response_model_exclude_defaults and response_model_exclude_none to be compatible pydantic options.
* 🚚 Rename and invert include_none to exclude_none to keep in sync with Pydantic
Co-authored-by: Lukas Voegtle <lukas.voegtle@sick.de>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Add an example of setting up a test database.
* 📝 Add/update docs for testing a DB with dependency overrides
* 🔧 Update test script, remove line removing test file as it is removed during testing
* ✅ Update testing coverage pragma
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* ✨ Update settings examples to use lru_cache
* 📝 Update docs for Settings, using @lru_cache
* 🎨 Update lru_cache colors to show difference in stored values
* Add doc and example for env var config
* Syntax highlight for .env file
* Add test for configuration docs
* 📝 Update settings docs, add more examples
* ✅ Add tests for settings
* 🚚 Rename "Application Configuration" to "Metadata and Docs URLs"
to disambiguate between that and settings
* 🔥 Remove replaced example file
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Handle automatic embedding with Depends
* 🐛 Fix body embeds for sub-dependencies and simplify implementation
* ✅ Add/update tests for body embeds in dependencies
* 👷 Trigger Travis
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* Add documentation of example kwarg of Field
* 📝 Update info about schema examples
* 🚚 Move example file to new directory
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* FIX: #894
Include recursion check for create_cloned_field.
Added test for recursive model.
* ♻️ Refactor and format create_cloned_field()
Co-authored-by: Lukas Voegtle <lukas.voegtle@sick.de>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* add example of pycharm in tutorial/debugging.md
* 📝 Update PyCharm debug instructions and screenshot
* 🚚 Move image to new location in docs
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 🏁 Fix ./scripts/docs.py encoding for Windows
* 🔥 Remove ujson from tests as it prevents Windows development
It's still tested by Starlette anyway
* 📝 Update development instructions for Windows
* 🎨 Update format for WSGIMiddleware example
* ✅ Update tests to run on Windows
* Added new external link
I added an article in which I briefly explain how to build an Apache Kafka producer / consumer with FastAPI and aiokafka.
* 📝 Update format
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
* 🌐 Refactor file structure to support internationalization
* ✅ Update tests changed after i18n
* 🔀 Merge Typer style from master
* 🔧 Update MkConfig with Typer-styles
* 🎨 Format mkdocs.yml with cannonical form
* 🎨 Format mkdocs.yml
* 🔧 Update MkDocs config
* ➕ Add docs translation scripts dependencies
* ✨ Add Typer scripts to handle translations
* ✨ Add missing translation snippet to include
* ✨ Update contributing docs, add docs for translations
* 🙈 Add docs_build to gitignore
* 🔧 Update scripts with new locations and docs scripts
* 👷 Update docs deploy action with translations
* 📝 Add note about languages not supported in the theme
* ✨ Add first translation, for Spanish
* ✨ Re-export main features used from Starlette to simplify developer's code
* ♻️ Refactor Starlette exports
* ♻️ Refactor tutorial examples to use re-exported utils from Starlette
* 📝 Add examples for all middlewares
* 📝 Add new docs for middlewares
* 📝 Add examples for custom responses
* 📝 Extend docs for custom responses
* 📝 Update docs and add notes explaining re-exports from Starlette everywhere
* 🍱 Update screenshot for HTTP status
* 🔧 Update MkDocs config with new content
* ♻️ Refactor tests to use re-exported utils from Starlette
* ✨ Re-export WebSocketDisconnect from Starlette for tests
* ✅ Add extra tests for extra re-exported middleware
* ✅ Add tests for re-exported responses from Starlette
* ✨ Add docs about mounting WSGI apps
* ➕ Add Flask as a dependency to test WSGIMiddleware
* ✅ Test WSGIMiddleware example
Please follow these instructions, fill every question, and do every step. 🙏
I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time.
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions.
All that, on top of all the incredible help provided by a bunch of community members, the [FastAPI Experts](https://fastapi.tiangolo.com/fastapi-people/#experts), that give a lot of their time to come here and help others.
That's a lot of work they are doing, but if more FastAPI users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
By asking questions in a structured way (following this) it will be much easier to help you.
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
- type:checkboxes
id:checks
attributes:
label:First Check
description:Please confirm and check all the following options.
options:
- label:I added a very descriptive title here.
required:true
- label:I used the GitHub search to find a similar question and didn't find it.
required:true
- label:I searched the FastAPI documentation, with the integrated search.
required:true
- label:I already searched in Google "How to X in FastAPI" and didn't find any information.
required:true
- label:I already read and followed all the tutorial in the docs and didn't find an answer.
required:true
- label:I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/pydantic/pydantic).
required:true
- label:I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
required:true
- label:I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).
required:true
- type:checkboxes
id:help
attributes:
label:Commit to Help
description:|
After submitting this, I commit to one of:
* Read open questions until I find 2 where I can help someone and add a comment to help there.
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
* Review one Pull Request by downloading the code and following [all the review process](https://fastapi.tiangolo.com/help-fastapi/#review-pull-requests).
options:
- label:I commit to help with one of those options 👆
required:true
- type:textarea
id:example
attributes:
label:Example Code
description:|
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
placeholder:|
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
render:python
validations:
required:true
- type:textarea
id:description
attributes:
label:Description
description:|
What is the problem, question, or error?
Write a short description telling me what you are doing, what you expect to happen, and what is currently happening.
placeholder:|
* Open the browser and call the endpoint `/`.
* It returns a JSON with `{"Hello": "World"}`.
* But I expected it to return `{"Hello": "Sara"}`.
validations:
required:true
- type:dropdown
id:os
attributes:
label:Operating System
description:What operating system are you on?
multiple:true
options:
- Linux
- Windows
- macOS
- Other
validations:
required:true
- type:textarea
id:os-details
attributes:
label:Operating System Details
description:You can add more details about your operating system here, in particular if you chose "Other".
about:To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already.
description:You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. 👇
body:
- type:markdown
attributes:
value:|
Thanks for your interest in FastAPI! 🚀
If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions) instead.
- type:checkboxes
id:privileged
attributes:
label:Privileged issue
description:Confirm that you are allowed to create an issue here.
options:
- label:I'm @tiangolo or he asked me directly to create an issue here.
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
},
"waiting": {
"delay": 2628000,
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.",
"reminder": {
"before": "P3D",
"message": "Heads-up: this will be closed in 3 days unless there’s new activity."
}
},
"invalid": {
"delay": 0,
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details."
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
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% *.
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Robust**: Get production-ready code. With automatic interactive documentation.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="https://json-schema.org/" class="external-link" target="_blank">JSON Schema</a>.
<small>* estimation based on tests on an internal development team, building production applications.</small>
<small>* estimation based on tests conducted by an internal development team, building production applications.</small>
## Sponsors
<!-- sponsors -->
### Keystone Sponsor
<a href="https://fastapicloud.com" target="_blank" title="FastAPI Cloud. By the same team behind FastAPI. You code. We Cloud."><img src="https://fastapi.tiangolo.com/img/sponsors/fastapicloud.png"></a>
<a href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"><img src="https://fastapi.tiangolo.com/img/sponsors/scalar.svg"></a>
<a href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge" target="_blank" title="Auth, user management and more for your B2B product"><img src="https://fastapi.tiangolo.com/img/sponsors/propelauth.png"></a>
<a href="https://zuplo.link/fastapi-gh" target="_blank" title="Zuplo: Deploy, Secure, Document, and Monetize your FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/zuplo.png"></a>
<a href="https://liblab.com?utm_source=fastapi" target="_blank" title="liblab - Generate SDKs from FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/liblab.png"></a>
<a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Deploy & scale any full-stack web app on Render. Focus on building apps, not infra."><img src="https://fastapi.tiangolo.com/img/sponsors/render.svg"></a>
<a href="https://www.coderabbit.ai/?utm_source=fastapi&utm_medium=badge&utm_campaign=fastapi" target="_blank" title="Cut Code Review Time & Bugs in Half with CodeRabbit"><img src="https://fastapi.tiangolo.com/img/sponsors/coderabbit.png"></a>
<a href="https://subtotal.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=open-source" target="_blank" title="The Gold Standard in Retail Account Linking"><img src="https://fastapi.tiangolo.com/img/sponsors/subtotal.svg"></a>
<a href="https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi" target="_blank" title="Deploy enterprise applications at startup speed"><img src="https://fastapi.tiangolo.com/img/sponsors/railway.png"></a>
<a href="https://serpapi.com/?utm_source=fastapi_website" target="_blank" title="SerpApi: Web Search API"><img src="https://fastapi.tiangolo.com/img/sponsors/serpapi.png"></a>
<a href="https://www.greptile.com/?utm_source=fastapi&utm_medium=sponsorship&utm_campaign=fastapi_sponsor_page" target="_blank" title="Greptile: The AI Code Reviewer"><img src="https://fastapi.tiangolo.com/img/sponsors/greptile.png"></a>
<a href="https://databento.com/?utm_source=fastapi&utm_medium=sponsor&utm_content=display" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
<a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
<a href="https://www.svix.com/" target="_blank" title="Svix - Webhooks as a service"><img src="https://fastapi.tiangolo.com/img/sponsors/svix.svg"></a>
<a href="https://www.permit.io/blog/implement-authorization-in-fastapi?utm_source=github&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Fine-Grained Authorization for FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/permit.png"></a>
<a href="https://www.interviewpal.com/?utm_source=fastapi&utm_medium=open-source&utm_campaign=dev-hiring" target="_blank" title="InterviewPal - AI Interview Coach for Engineers and Devs"><img src="https://fastapi.tiangolo.com/img/sponsors/interviewpal.png"></a>
<a href="https://dribia.com/en/" target="_blank" title="Dribia - Data Science within your reach"><img src="https://fastapi.tiangolo.com/img/sponsors/dribia.png"></a>
"*[...] 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.*"
"_[...] 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._"
"*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 [...]*"
"*We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]*"
"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_"
<div style="text-align: right; margin-right: 10%;">Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - <strong>Uber</strong> <a href="https://eng.uber.com/ludwig-v0-2/" target="_blank"><small>(ref)</small></a></div>
---
## Requirements
"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_"
"_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._"
If you are building a <abbr title="Command Line Interface">CLI</abbr> app to be used in the terminal instead of a web API, check out <a href="https://typer.tiangolo.com/" class="external-link" target="_blank">**Typer**</a>.
**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀
## Requirements
FastAPI stands on the shoulders of giants:
* <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> for the data parts.
* <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> for the web parts.
* <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> for the data parts.
## Installation
```bash
pip install fastapi
Create and activate a <a href="https://fastapi.tiangolo.com/virtual-environments/" class="external-link" target="_blank">virtual environment</a> and then install FastAPI:
<div class="termy">
```console
$ pip install "fastapi[standard]"
---> 100%
```
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" class="external-link" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" class="external-link" target="_blank">Hypercorn</a>.
</div>
```bash
pip install uvicorn
```
**Note**: Make sure you put `"fastapi[standard]"` in quotes to ensure it works in all terminals.
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.
```
</div>
<details markdown="1">
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
<summary>About the command <code>fastapi dev main.py</code>...</summary>
The command `uvicorn main:app` refers to:
The command `fastapi dev` reads your `main.py` file, detects the **FastAPI** app in it, and starts a server using <a href="https://www.uvicorn.dev" class="external-link" target="_blank">Uvicorn</a>.
* `main`: the file `main.py` (the Python "module").
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
* `--reload`: make the server restart after code changes. Only do this for development.
By default, `fastapi dev` will start with auto-reload enabled for local development.
You can read more about it in the <a href="https://fastapi.tiangolo.com/fastapi-cli/" target="_blank">FastAPI CLI docs</a>.
</details>
@@ -205,7 +285,9 @@ Now modify the file `main.py` to receive a body from a `PUT` request.
Declare the body using standard Python types, thanks to Pydantic.
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
The `fastapi dev` server should reload automatically.
### Interactive API docs upgrade
@@ -261,13 +343,13 @@ And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" targe
### Recap
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard **Python 3.6+**.
Just standard **Python**.
For example, for an `int`:
@@ -317,8 +399,8 @@ Coming back to the previous code example, **FastAPI** will:
* 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`.
* 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.
@@ -363,44 +445,118 @@ For a more complete example including more features, see the <a href="https://fa
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
* Many extra features (thanks to Starlette) as:
* **WebSockets**
* **GraphQL**
* extremely easy tests based on `requests` and `pytest`
* 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 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>, 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:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
Then deploy your app:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
That's it! Now you can access your app at that URL. ✨
#### About FastAPI Cloud
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** 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 <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
To understand more about it, see the section <a href="https://fastapi.tiangolo.com/benchmarks/" class="internal-link" target="_blank">Benchmarks</a>.
## Optional Dependencies
## 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:
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - for faster JSON <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - for email validation.
Used by Starlette:
* <a href="http://docs.python-requests.org" target="_blank"><code>requests</code></a> - Required if you want to use the `TestClient`.
* <a href="https://github.com/Tinche/aiofiles" target="_blank"><code>aiofiles</code></a> - Required if you want to use `FileResponse` or `StaticFiles`.
* <a href="http://jinja.pocoo.org" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://andrew-d.github.io/python-multipart/" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
* <a href="https://pythonhosted.org/itsdangerous/" target="_blank"><code>itsdangerous</code></a> - Required for `SessionMiddleware` support.
* <a href="https://pyyaml.org/wiki/PyYAMLDocumentation" target="_blank"><code>pyyaml</code></a> - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI).
* <a href="https://graphene-python.org/" target="_blank"><code>graphene</code></a> - Required for `GraphQLApp` support.
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
Used by FastAPI:
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> - 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 <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
### 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:
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - for settings management.
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - for extra types to be used with Pydantic.
Additional optional FastAPI dependencies:
* <a href="https://github.com/ijl/orjson" target="_blank"><code>orjson</code></a> - Required if you want to use `ORJSONResponse`.
* <a href="https://github.com/esnme/ultrajson" target="_blank"><code>ujson</code></a> - Required if you want to use `UJSONResponse`.
Used by FastAPI / Starlette:
* <a href="http://www.uvicorn.org" target="_blank"><code>uvicorn</code></a> - for the server that loads and serves your application.
You can install all of these with `pip install fastapi[all]`.
## License
This project is licensed under the terms of the MIT license.
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.
By default, **FastAPI** will return the responses using Starlette's `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
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:
```Python hl_lines="2 20"
{!./src/additional_status_codes/tutorial001.py!}
```
!!! 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`).
## 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 before hand what you are going to return.
But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}.
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
## 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
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__`:
```Python hl_lines="10"
{!./src/dependencies/tutorial011.py!}
```
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
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
```Python hl_lines="7"
{!./src/dependencies/tutorial011.py!}
```
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
## Create an instance
We could create an instance of this class with:
```Python hl_lines="16"
{!./src/dependencies/tutorial011.py!}
```
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
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`:
```Python hl_lines="20"
{!./src/dependencies/tutorial011.py!}
```
!!! 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.
You can also use <a href="https://github.com/encode/databases" class="external-link" target="_blank">`encode/databases`</a> with **FastAPI** to connect to databases using `async` and `await`.
It is compatible with:
* PostgreSQL
* MySQL
* SQLite
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
You could adopt ideas from the section about SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), like using utility functions to perform operations in the database, independent of your **FastAPI** code.
This section doesn't apply those ideas, to be equivalent to the counterpart in <a href="https://www.starlette.io/database/" class="external-link" target="_blank">Starlette</a>.
## Import and set up `SQLAlchemy`
* Import `SQLAlchemy`.
* Create a `metadata` object.
* Create a table `notes` using the `metadata` object.
```Python hl_lines="4 14 16 17 18 19 20 21 22"
{!./src/async_sql_databases/tutorial001.py!}
```
!!! tip
Notice that all this code is pure SQLAlchemy Core.
`databases` is not doing anything here yet.
## Import and set up `databases`
* Import `databases`.
* Create a `DATABASE_URL`.
* Create a `database` object.
```Python hl_lines="3 9 12"
{!./src/async_sql_databases/tutorial001.py!}
```
!!! tip
If you were connecting to a different database (e.g. PostgreSQL), you would need to change the `DATABASE_URL`.
## Create the tables
In this case, we are creating the tables in the same Python file, but in production, you would probably want to create them with Alembic, integrated with migrations, etc.
Here, this section would run directly, right before starting your **FastAPI** application.
* Create an `engine`.
* Create all the tables from the `metadata` object.
```Python hl_lines="25 26 27 28"
{!./src/async_sql_databases/tutorial001.py!}
```
## Create models
Create Pydantic models for:
* Notes to be created (`NoteIn`).
* Notes to be returned (`Note`).
```Python hl_lines="31 32 33 36 37 38 39"
{!./src/async_sql_databases/tutorial001.py!}
```
By creating these Pydantic models, the input data will be validated, serialized (converted), and annotated (documented).
So, you will be able to see it all in the interactive API docs.
## Connect and disconnect
* Create your `FastAPI` application.
* Create event handlers to connect and disconnect from the database.
```Python hl_lines="42 45 46 47 50 51 52"
{!./src/async_sql_databases/tutorial001.py!}
```
## Read notes
Create the *path operation function* to read notes:
```Python hl_lines="55 56 57 58"
{!./src/async_sql_databases/tutorial001.py!}
```
!!! Note
Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`.
### Notice the `response_model=List[Note]`
It uses `typing.List`.
That documents (and validates, serializes, filters) the output data, as a `list` of `Note`s.
## Create notes
Create the *path operation function* to create notes:
```Python hl_lines="61 62 63 64 65"
{!./src/async_sql_databases/tutorial001.py!}
```
!!! Note
Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`.
### About `{**note.dict(), "id": last_record_id}`
`note` is a Pydantic `Note` object.
`note.dict()` returns a `dict` with its data, something like:
```Python
{
"text": "Some note",
"completed": False,
}
```
but it doesn't have the `id` field.
So we create a new `dict`, that contains the key-value pairs from `note.dict()` with:
```Python
{**note.dict()}
```
`**note.dict()` "unpacks" the key value pairs directly, so, `{**note.dict()}` would be, more or less, a copy of `note.dict()`.
And then, we extend that copy `dict`, adding another key-value pair: `"id": last_record_id`:
```Python
{**note.dict(), "id": last_record_id}
```
So, the final result returned would be something like:
```Python
{
"id": 1,
"text": "Some note",
"completed": False,
}
```
## Check it
You can copy this code as is, and see the docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
There you can see all your API documented and interact with it:
If you are starting with **FastAPI**, you might not need this.
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`.
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type`).
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
The contents that you return from your *path operation function* will be put inside of that `Response`.
And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
!!! 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.
## Use `UJSONResponse`
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be Starlette's `UJSONResponse`.
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
```Python hl_lines="2 7"
{!./src/custom_response/tutorial001.py!}
```
!!! note
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
!!! 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 `application/json`.
And it will be documented as such in OpenAPI.
## HTML Response
To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
* Import `HTMLResponse`.
* Pass `HTMLResponse` as the parameter `content_type` of your *path operation*.
```Python hl_lines="2 7"
{!./src/custom_response/tutorial002.py!}
```
!!! note
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
!!! 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 Starlette `Response`
As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, 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:
```Python hl_lines="2 7 19"
{!./src/custom_response/tutorial003.py!}
```
!!! 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 your returned.
### 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
For example, it could be something like:
```Python hl_lines="7 23 21"
{!./src/custom_response/tutorial004.py!}
```
In this example, the function `generate_html_response()` already generates a Starlette `Response` instead of 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`, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
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
To add a function that should be run before the application starts, declare it with the event `"startup"`:
```Python hl_lines="8"
{!./src/events/tutorial001.py!}
```
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
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
```Python hl_lines="6"
{!./src/events/tutorial002.py!}
```
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`.
!!! info
You can read more about these event handlers in <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette's Events' docs</a>.
This is a rather advanced feature. You probably can skip it.
If you are just following the tutorial - user guide, you can probably skip this section.
If you already know that you need to modify the generated OpenAPI schema, continue reading.
There are some cases where you might need to modify the generated OpenAPI schema.
In this section you will see how.
## The normal process
The normal (default) process, is as follows.
A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema.
As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered.
It just returns a JSON response with the result of the application's `.openapi()` method.
By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them.
If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`.
And that function `get_openapi()` receives as parameters:
*`title`: The OpenAPI title, shown in the docs.
*`version`: The version of your API, e.g. `2.5.0`.
*`openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.0.2`.
*`description`: The description of your API.
*`routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`.
*`openapi_prefix`: The URL prefix to be used in your OpenAPI.
## Overriding the defaults
Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need.
For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo" class="external-link" target="_blank">ReDoc's OpenAPI extension to include a custom logo</a>.
### Normal **FastAPI**
First, write all your **FastAPI** application as normally:
```Python hl_lines="1 4 7 8 9"
{!./src/extending_openapi/tutorial001.py!}
```
### Generate the OpenAPI schema
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
```Python hl_lines="2 15 16 17 18 19 20"
{!./src/extending_openapi/tutorial001.py!}
```
### Modify the OpenAPI schema
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
```Python hl_lines="21 22 23"
{!./src/extending_openapi/tutorial001.py!}
```
### Cache the OpenAPI schema
You can use the property `.openapi_schema` as a "cache", to store your generated schema.
That way, your application won't have to generate the schema every time a user opens your API docs.
It will be generated only once, and then the same cached schema will be used for the next requests.
```Python hl_lines="13 14 24 25"
{!./src/extending_openapi/tutorial001.py!}
```
### Override the method
Now you can replace the `.openapi()` method with your new function.
```Python hl_lines="28"
{!./src/extending_openapi/tutorial001.py!}
```
### Check it
Once you go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> you will see that you are using your custom logo (in this example, **FastAPI**'s logo):
The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files.
By default, those files are served from a <abbr title="Content Delivery Network: A service, normally composed of several servers, that provides static files, like JavaScript and CSS. It's commonly used to serve those files from the server closer to the client, improving performance.">CDN</abbr>.
But it's possible to customize it, you can set a specific CDN, or serve the files yourself.
That's useful, for example, if you need your app to keep working even while offline, without open Internet access, or in a local network.
Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them.
### Project file structure
Let's say your project file structure looks like this:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
```
Now create a directory to store those static files.
Your new file structure could look like this:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
└── static/
```
### Download the files
Download the static files needed for the docs and put them on that `static/` directory.
You can probably right-click each link and select an option similar to `Save link as...`.
* "Mount" a `StaticFiles()` instance in a specific path.
```Python hl_lines="7 11"
{!./src/extending_openapi/tutorial002.py!}
```
### Test the static files
Start your application and go to <a href="http://127.0.0.1:8000/static/redoc.standalone.js" class="external-link" target="_blank">http://127.0.0.1:8000/static/redoc.standalone.js</a>.
You should see a very long JavaScript file for **ReDoc**.
It could start with something like:
```JavaScript
/*!
* ReDoc - OpenAPI/Swagger-generated API Reference Documentation
The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2.
If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication.
Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper.
### Create a *path operation* to test it
Now, to be able to test that everything works, create a *path operation*:
```Python hl_lines="39 40 41"
{!./src/extending_openapi/tutorial002.py!}
```
### Test it
Now, you should be able to disconnect your WiFi, go to your docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, and reload the page.
And even without Internet, you would be able to see the docs for your API and interact with it.
**FastAPI** has optional support for GraphQL (provided by Starlette directly), using the `graphene` library.
You can combine normal FastAPI *path operations* with GraphQL on the same application.
## Import and use `graphene`
GraphQL is implemented with Graphene, you can check <a href="https://docs.graphene-python.org/en/latest/quickstart/" class="external-link" target="_blank">Graphene's docs</a> for more details.
Import `graphene` and define your GraphQL data:
```Python hl_lines="1 6 7 8 9 10"
{!./src/graphql/tutorial001.py!}
```
## Add Starlette's `GraphQLApp`
Then import and add Starlette's `GraphQLApp`:
```Python hl_lines="3 14"
{!./src/graphql/tutorial001.py!}
```
!!! info
Here we are using `.add_route`, that is the way to add a route in Starlette (inherited by FastAPI) without declaring the specific operation (as would be with `.get()`, `.post()`, etc).
## Check it
Run it with Uvicorn and open your browser at <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
You will see GraphiQL web user interface:
<img src="/img/tutorial/graphql/image01.png">
## More details
For more details, including:
* Accessing request information
* Adding background tasks
* Using normal or async functions
check the official <a href="https://www.starlette.io/graphql/" class="external-link" target="_blank">Starlette GraphQL docs</a>.
The main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank} 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
You could still use most of the features in **FastAPI** with the knowledge from the main [Tutorial - User Guide](../tutorial/){.internal-link target=_blank}.
And the next sections assume you already read it, and assume that you know those main ideas.
**FastAPI** can also be integrated with any <abbr title="Distributed database (Big Data), also 'Not Only SQL'">NoSQL</abbr>.
Here we'll see an example using **<a href="https://www.couchbase.com/" class="external-link" target="_blank">Couchbase</a>**, a <abbr title="Document here refers to a JSON object (a dict), with keys and values, and those values can also be other JSON objects, arrays (lists), numbers, strings, booleans, etc.">document</abbr> based NoSQL database.
You can adapt it to any other NoSQL database like:
* **MongoDB**
* **Cassandra**
* **CouchDB**
* **ArangoDB**
* **ElasticSearch**, etc.
!!! tip
There is an official project generator with **FastAPI** and **Couchbase**, all based on **Docker**, including a frontend and more tools: <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" class="external-link" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
## Import Couchbase components
For now, don't pay attention to the rest, only the imports:
```Python hl_lines="6 7 8"
{!./src/nosql_databases/tutorial001.py!}
```
## Define a constant to use as a "document type"
We will use it later as a fixed field `type` in our documents.
This is not required by Couchbase, but is a good practice that will help you afterwards.
```Python hl_lines="10"
{!./src/nosql_databases/tutorial001.py!}
```
## Add a function to get a `Bucket`
In **Couchbase**, a bucket is a set of documents, that can be of different types.
They are generally all related to the same application.
The analogy in the relational database world would be a "database" (a specific database, not the database server).
The analogy in **MongoDB** would be a "collection".
In the code, a `Bucket` represents the main entrypoint of communication with the database.
This utility function will:
* Connect to a **Couchbase** cluster (that might be a single machine).
As **Couchbase** "documents" are actually just "JSON objects", we can model them with Pydantic.
### `User` model
First, let's create a `User` model:
```Python hl_lines="25 26 27 28 29"
{!./src/nosql_databases/tutorial001.py!}
```
We will use this model in our *path operation function*, so, we don't include in it the `hashed_password`.
### `UserInDB` model
Now, let's create a `UserInDB` model.
This will have the data that is actually stored in the database.
We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more:
```Python hl_lines="32 33 34"
{!./src/nosql_databases/tutorial001.py!}
```
!!! note
Notice that we have a `hashed_password` and a `type` field that will be stored in the database.
But it is not part of the general `User` model (the one we will return in the *path operation*).
## Get the user
Now create a function that will:
* Take a username.
* Generate a document ID from it.
* Get the document with that ID.
* Put the contents of the document in a `UserInDB` model.
By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your *path operation function*, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
```Python hl_lines="37 38 39 40 41 42 43"
{!./src/nosql_databases/tutorial001.py!}
```
### f-strings
If you are not familiar with the `f"userprofile::{username}"`, it is a Python "<a href="https://docs.python.org/3/glossary.html#term-f-string" class="external-link" target="_blank">f-string</a>".
Any variable that is put inside of `{}` in an f-string will be expanded / injected in the string.
### `dict` unpacking
If you are not familiar with the `UserInDB(**result.value)`, <a href="https://docs.python.org/3/glossary.html#term-argument" class="external-link" target="_blank">it is using `dict` "unpacking"</a>.
It will take the `dict` at `result.value`, and take each of its keys and values and pass them as key-values to `UserInDB` as keyword arguments.
As our code is calling Couchbase and we are not using the <a href="https://docs.couchbase.com/python-sdk/2.5/async-programming.html#asyncio-python-3-5" class="external-link" target="_blank">experimental Python <code>await</code> support</a>, we should declare our function with normal `def` instead of `async def`.
Also, Couchbase recommends not using a single `Bucket` object in multiple "<abbr title="A sequence of code being executed by the program, while at the same time, or at intervals, there can be others being executed too.">thread</abbr>s", so, we can get just get the bucket directly and pass it to our utility functions:
```Python hl_lines="50 51 52 53 54"
{!./src/nosql_databases/tutorial001.py!}
```
## Recap
You can integrate any third party NoSQL database, just using their standard packages.
The same applies to any other external tool, system or API.
### 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*.
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
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`;
You can declare a parameter of type `Response` in your *path operation function*.
And then you can set headers in that *temporal* response object.
```Python hl_lines="2 8 9"
{!./src/response_cookies/tutorial002.py!}
```
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
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){.internal-link target=_blank}.
Then set Cookies in it, and then return it:
```Python hl_lines="10 11 12"
{!./src/response_cookies/tutorial001.py!}
```
!!! tip
Have 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
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">documentation in Starlette</a>.
There are some extra features to handle security apart from the ones covered in the [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}.
!!! 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
The next sections assume you already read the main [Tutorial - User Guide: Security](../../tutorial/security/){.internal-link target=_blank}.
They are all based on the same concepts, but allow some extra functionalities.
If you are just starting, the tutorial [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} that uses SQLAlchemy should be enough.
Feel free to skip this.
If you are starting a project from scratch, you are probably better off with SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), or any other async ORM.
If you already have a code base that uses <a href="http://docs.peewee-orm.com/en/latest/" class="external-link" target="_blank">Peewee ORM</a>, you can check here how to use it with **FastAPI**.
!!! warning "Python 3.7+ required"
You will need Python 3.7 or above to safely use Peewee with FastAPI.
## Peewee for async
Peewee was not designed for async frameworks, or with them in mind.
Peewee has some heavy assumptions about its defaults and about how it should be used.
If you are developing an application with an older non-async framework, and can work with all its defaults, **it can be a great tool**.
But if you need to change some of the defaults, support more than one predefined database, work with an async framework (like FastAPI), etc, you will need to add quite some complex extra code to override those defaults.
Nevertheless, it's possible to do it, and here you'll see exactly what code you have to add to be able to use Peewee with FastAPI.
!!! note "Technical Details"
You can read more about Peewee's stand about async in Python <a href="http://docs.peewee-orm.com/en/latest/peewee/database.html#async-with-gevent" class="external-link" target="_blank">in the docs</a>, <a href="https://github.com/coleifer/peewee/issues/263#issuecomment-517347032" class="external-link" target="_blank">an issue</a>, <a href="https://github.com/coleifer/peewee/pull/2072#issuecomment-563215132" class="external-link" target="_blank">a PR</a>.
## The same app
We are going to create the same application as in the SQLAlchemy tutorial ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}).
Most of the code is actually the same.
So, we are going to focus only on the differences.
## File structure
Let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this:
```
.
└── sql_app
├── __init__.py
├── crud.py
├── database.py
├── main.py
└── schemas.py
```
This is almost the same structure as we had for the SQLAlchemy tutorial.
Now let's see what each file/module does.
## Create the Peewee parts
Let's refer to the file `sql_app/database.py`.
### The standard Peewee code
Let's first check all the normal Peewee code, create a Peewee database:
Have in mind that if you wanted to use a different database, like PostgreSQL, you couldn't just change the string. You would need to use a different Peewee database class.
#### Note
The argument:
```Python
check_same_thread=False
```
is equivalent to the one in the SQLAlchemy tutorial:
```Python
connect_args={"check_same_thread": False}
```
...it is needed only for `SQLite`.
!!! info "Technical Details"
Exactly the same technical details as in [SQL (Relational) Databases](../tutorial/sql-databases.md#note){.internal-link target=_blank} apply.
### Make Peewee async-compatible `PeeweeConnectionState`
The main issue with Peewee and FastAPI is that Peewee relies heavily on <a href="https://docs.python.org/3/library/threading.html#thread-local-data" class="external-link" target="_blank">Python's `threading.local`</a>, and it doesn't have a direct way to override it or let you handle connections/sessions directly (as is done in the SQLAlchemy tutorial).
And `threading.local` is not compatible with the new async features of modern Python.
!!! note "Technical Details"
`threading.local` is used to have a "magic" variable that has a different value for each thread.
This was useful in older frameworks designed to have one single thread per request, no more, no less.
Using this, each request would have its own database connection/session, which is the actual final goal.
But FastAPI, using the new async features, could handle more than one request on the same thread. And at the same time, for a single request, it could run multiple things in different threads (in a threadpool), depending on if you use `async def` or normal `def`. This is what gives all the performance improvements to FastAPI.
But Python 3.7 and above provide a more advanced alternative to `threading.local`, that can also be used in the places where `threading.local` would be used, but is compatible with the new async features.
We are going to use that. It's called <a href="https://docs.python.org/3/library/contextvars.html" class="external-link" target="_blank">`contextvars`</a>.
We are going to override the internal parts of Peewee that use `threading.local` and replace them with `contextvars`, with the corresponding updates.
This might seem a bit complex (and it actually is), you don't really need to completely understand how it works to use it.
This class inherits from a special internal class used by Peewee.
It has all the logic to make Peewee use `contextvars` instead of `threading.local`.
`contextvars` works a bit differently than `threading.local`. But the rest of Peewee's internal code assumes that this class works with `threading.local`.
So, we need to do some extra tricks to make it work as if it was just using `threading.local`. The `__init__`, `__setattr__`, and `__getattr__` implement all the required tricks for this to be used by Peewee without knowing that it is now compatible with FastAPI.
!!! tip
This will just make Peewee behave correctly when used with FastAPI. Not randomly opening or closing connections that are being used, creating errors, etc.
But it doesn't give Peewee async super-powers. You should still use normal `def` functions and not `async def`.
### Use the custom `PeeweeConnectionState` class
Now, overwrite the `._state` internal attribute in the Peewee database `db` object using the new `PeeweeConnectionState`:
It will automatically add an `id` attribute as an integer to be the primary key.
It will chose the name of the tables based on the class names.
For the `Item`, it will create an attribute `owner_id` with the integer ID of the `User`. But we don't declare it anywhere.
## Create the Pydantic models
Now let's check the file `sql_app/schemas.py`.
!!! tip
To avoid confusion between the Peewee *models* and the Pydantic *models*, we will have the file `models.py` with the Peewee models, and the file `schemas.py` with the Pydantic models.
These Pydantic models define more or less a "schema" (a valid data shape).
So this will help us avoiding confusion while using both.
### Create the Pydantic *models* / schemas
Create all the same Pydantic models as in the SQLAlchemy tutorial:
We didn't explicitly specify an `id` attribute in the Peewee models, but Peewee adds one automatically.
We are also adding the magic `owner_id` attribute to `Item`.
### Create a `PeeweeGetterDict` for the Pydantic *models* / schemas
When you access a relationship in a Peewee object, like in `some_user.items`, Peewee doesn't provide a `list` of `Item`.
It provides a special custom object of class `ModelSelect`.
It's possible to create a `list` of its items with `list(some_user.items)`.
But the object itself is not a `list`. And it's also not an actual Python <a href="https://docs.python.org/3/glossary.html#term-generator" class="external-link" target="_blank">generator</a>. Because of this, Pydantic doesn't know by default how to convert it to a `list` of Pydantic *models* / schemas.
But recent versions of Pydantic allow providing a custom class that inherits from `pydantic.utils.GetterDict`, to provide the functionality used when using the `orm_mode = True` to retrieve the values for ORM model attributes.
We are going to create a custom `PeeweeGetterDict` class and use it in all the same Pydantic *models* / schemas that use `orm_mode`:
```Python hl_lines="3 8 9 10 11 12 13 31 49"
{!./src/sql_databases_peewee/sql_app/schemas.py!}
```
Here we are checking if the attribute that is being accessed (e.g. `.items` in `some_user.items`) is an instance of `peewee.ModelSelect`.
And if that's the case, just return a `list` with it.
And then we use it in the Pydantic *models* / schemas that use `orm_mode = True`, with the configuration variable `getter_dict = PeeweeGetterDict`.
!!! tip
We only need to create one `PeeweeGetterDict` class, and we can use it in all the Pydantic *models* / schemas.
## CRUD utils
Now let's see the file `sql_app/crud.py`.
### Create all the CRUD utils
Create all the same CRUD utils as in the SQLAlchemy tutorial, all the code is very similar:
There are some differences with the code for the SQLAlchemy tutorial.
We don't pass a `db` attribute around. Instead we use the models directly. This is because the `db` object is a global object, that includes all the connection logic. That's why we had to do all the `contextvars` updates above.
Aso, when returning several objects, like in `get_users`, we directly call `list`, like in:
```Python
list(models.User.select())
```
This is for the same reason that we had to create a custom `PeeweeGetterDict`. But by returning something that is already a `list` instead of the `peewee.ModelSelect` the `response_model` in the *path operation* with `List[models.User]` (that we'll see later) will work correctly.
## Main **FastAPI** app
And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before.
### Create the database tables
In a very simplistic way create the database tables:
```Python hl_lines="9 10 11"
{!./src/sql_databases_peewee/sql_app/main.py!}
```
### Create a dependency
Create a dependency that will connect the database right at the beginning of a request and disconnect it at the end:
```Python hl_lines="23 24 25 26 27 28 29"
{!./src/sql_databases_peewee/sql_app/main.py!}
```
Here we have an empty `yield` because we are actually not using the database object directly.
It is connecting to the database and storing the connection data in an internal variable that is independent for each request (using the `contextvars` tricks from above).
Because the database connection is potentially I/O blocking, this dependency is created with a normal `def` function.
And then, in each *path operation function* that needs to access the database we add it as a dependency.
But we are not using the value given by this dependency (it actually doesn't give any value, as it has an empty `yield`). So, we don't add it to the *path operation function* but to the *path operation decorator* in the `dependencies` parameter:
```Python hl_lines="32 40 47 59 65 72"
{!./src/sql_databases_peewee/sql_app/main.py!}
```
### Context variable sub-dependency
For all the `contextvars` parts to work, we need to make sure we have an independent value in the `ContextVar` for each request that uses the database, and that value will be used as the database state (connection, transactions, etc) for the whole request.
For that, we need to create another `async` dependency `reset_db_state()` that is used as a sub-dependency in `get_db()`. It will set the value for the context variable (with just a default `dict`) that will be used as the database state for the whole request. And then the dependency `get_db()` will store in it the database state (connection, transactions, etc).
```Python hl_lines="18 19 20"
{!./src/sql_databases_peewee/sql_app/main.py!}
```
For the **next request**, as we will reset that context variable again in the `async` dependency `reset_db_state()` and then create a new connection in the `get_db()` dependency, that new request will have its own database state (connection, transactions, etc).
!!! tip
As FastAPI is an async framework, one request could start being processed, and before finishing, another request could be received and start processing as well, and it all could be processed in the same thread.
But context variables are aware of these async features, so, a Peewee database state set in the `async` dependency `reset_db_state()` will keep its own data throughout the entire request.
And at the same time, the other concurrent request will have its own database state that will be independent for the whole request.
#### Peewee Proxy
If you are using a <a href="http://docs.peewee-orm.com/en/latest/peewee/database.html#dynamically-defining-a-database" class="external-link" target="_blank">Peewee Proxy</a>, the actual database is at `db.obj`.
The same as with SQLAlchemy, we are not doing something like:
```Python
user = await models.User.select().first()
```
...but instead we are using:
```Python
user = models.User.select().first()
```
So, again, we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as:
```Python hl_lines="2"
# Something goes here
def read_users(skip: int = 0, limit: int = 100):
# Something goes here
```
## Testing Peewee with async
This example includes an extra *path operation* that simulates a long processing request with `time.sleep(sleep_time)`.
It will have the database connection open at the beginning and will just wait some seconds before replying back. And each new request will wait one second less.
This will easily let you test that your app with Peewee and FastAPI is behaving correctly with all the stuff about threads.
If you want to check how Peewee would break your app if used without modification, go the the `sql_app/database.py` file and comment the line:
```Python
# db._state = PeeweeConnectionState()
```
And in the file `sql_app/main.py` file, comment the body of the `async` dependency `reset_db_state()` and replace it with a `pass`:
Open your browser at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> and create a couple of users.
Then open 10 tabs at <a href="http://127.0.0.1:8000/docs#/default/read_slow_users_slowusers__get" class="external-link" target="_blank">http://127.0.0.1:8000/docs#/default/read_slow_users_slowusers__get</a> at the same time.
Go to the *path operation* "Get `/slowusers/`" in all of the tabs. Use the "Try it out" button and execute the request in each tab, one right after the other.
The tabs will wait for a bit and then some of them will show `Internal Server Error`.
### What happens
The first tab will make your app create a connection to the database and wait for some seconds before replying back and closing the database connection.
Then, for the request in the next tab, your app will wait for one second less, and so on.
This means that it will end up finishing some of the last tabs' requests earlier than some of the previous ones.
Then one the last requests that wait less seconds will try to open a database connection, but as one of those previous requests for the other tabs will probably be handled in the same thread as the first one, it will have the same database connection that is already open, and Peewee will throw an error and you will see it in the terminal, and the response will have an `Internal Server Error`.
This will probably happen for more than one of those tabs.
If you had multiple clients talking to your app exactly at the same time, this is what could happen.
And as your app starts to handle more and more clients at the same time, the waiting time in a single request needs to be shorter and shorter to trigger the error.
### Fix Peewee with FastAPI
Now go back to the file `sql_app/database.py`, and uncomment the line:
```Python
db._state = PeeweeConnectionState()
```
And in the file `sql_app/main.py` file, uncomment the body of the `async` dependency `reset_db_state()`:
These are very technical details that you probably don't need.
### The problem
Peewee uses <a href="https://docs.python.org/3/library/threading.html#thread-local-data" class="external-link" target="_blank">`threading.local`</a> by default to store it's database "state" data (connection, transactions, etc).
`threading.local` creates a value exclusive to the current thread, but an async framework would run all the code (e.g. for each request) in the same thread, and possibly not in order.
On top of that, an async framework could run some sync code in a threadpool (using `asyncio.run_in_executor`), but belonging to the same request.
This means that, with Peewee's current implementation, multiple tasks could be using the same `threading.local` variable and end up sharing the same connection and data (that they shouldn't), and at the same time, if they execute sync I/O-blocking code in a threadpool (as with normal `def` functions in FastAPI, in *path operations* and dependencies), that code won't have access to the database state variables, even while it's part of the same request and it should be able to get access to the same database state.
### Context variables
Python 3.7 has <a href="https://docs.python.org/3/library/contextvars.html" class="external-link" target="_blank">`contextvars`</a> that can create a local variable very similar to `threading.local`, but also supporting these async features.
There are several things to have in mind.
The `ContextVar` has to be created at the top of the module, like:
To set a value used in the current "context" (e.g. for the current request) use:
```Python
some_var.set("new value")
```
To get a value anywhere inside of the context (e.g. in any part handling the current request) use:
```Python
some_var.get()
```
### Set context variables in the `async` dependency `reset_db_state()`
If some part of the async code sets the value with `some_var.set("updated in function")` (e.g. like the `async` dependency), the rest of the code in it and the code that goes after (including code inside of `async` functions called with `await`) will see that new value.
So, in our case, if we set the Peewee state variable (with a default `dict`) in the `async` dependency, all the rest of the internal code in our app will see this value and will be able to reuse it for the whole request.
And the context variable would be set again for the next request, even if they are concurrent.
### Set database state in the dependency `get_db()`
As `get_db()` is a normal `def` function, **FastAPI** will make it run in a threadpool, with a *copy* of the "context", holding the same value for the context variable (the `dict` with the reset database state). Then it can add database state to that `dict`, like the connection, etc.
But if the value of the context variable (the default `dict`) was set in that normal `def` function, it would create a new value that would stay only in that thread of the threadpool, and the rest of the code (like the *path operation functions*) wouldn't have access to it. In `get_db()` we can only set values in the `dict`, but not the entire `dict` itself.
So, we need to have the `async` dependency `reset_db_state()` to set the `dict` in the context variable. That way, all the code has access to the same `dict` for the database state for a single request.
### Connect and disconnect in the dependency `get_db()`
Then the next question would be, why not just connect and disconnect the database in the `async` dependency itself, instead of in `get_db()`?
The `async` dependency has to be `async` for the context variable to be preserved for the rest of the request, but creating and closing the database connection is potentially blocking, so it could degrade performance if it was there.
So we also need the normal `def` dependency `get_db()`.
There are at least two situations where you could need to create your **FastAPI** application using some specific paths.
But then you need to set them up to be served with a path prefix.
It could happen if you have a:
* **Proxy** server.
* You are "**mounting**" a FastAPI application inside another FastAPI application (or inside another ASGI application, like Starlette).
## Proxy
Having a proxy in this case means that you could declare a path at `/app`, but then, you could need to 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` will actually be served at `/api/v1/app`.
Even though your application "thinks" it is serving at `/app`.
And the Proxy could be re-writing the path "on the fly" to keep your application convinced that it is serving at `/app`.
Up to here, everything would work as normally.
But then, when you open the integrated docs, they 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.
So, it's needed that the frontend looks for the OpenAPI schema at `/api/v1/openapi.json`.
And it's also needed that the returned JSON OpenAPI schema has the defined path at `/api/v1/app` (behind the proxy) instead of `/app`.
---
For these cases, you can declare an `openapi_prefix` parameter in your `FastAPI` application.
See the section below, about "mounting", for an example.
## Mounting a **FastAPI** application
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
You could want to do this if you have several "independent" applications that you want to separate, having their own independent OpenAPI schema and user interfaces.
### Top-level application
First, create the main, top-level, **FastAPI** application, and its *path operations*:
```Python hl_lines="3 6 7 8"
{!./src/sub_applications/tutorial001.py!}
```
### 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".
When creating the sub-application, use the parameter `openapi_prefix`. In this case, with a prefix of `/subapi`:
```Python hl_lines="11 14 15 16"
{!./src/sub_applications/tutorial001.py!}
```
### Mount the sub-application
In your top-level application, `app`, mount the sub-application, `subapi`.
Here you need to make sure you use the same path that you used for the `openapi_prefix`, in this case, `/subapi`:
```Python hl_lines="11 19"
{!./src/sub_applications/tutorial001.py!}
```
## Check the automatic API docs
Now, run `uvicorn`, if your file is at `main.py`, it would be:
```bash
uvicorn main:app --reload
```
And open the docs at <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
You will see the automatic API docs for the main app, including only its own paths:
And then, open the docs for the sub-application, at <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
You will see the automatic API docs for the sub-application, including only its own sub-paths, with their correct prefix:
If you try interacting with any of the two user interfaces, they will work, because the browser will be able to talk to the correct path (or sub-path).
You can use any template engine you want with **FastAPI**.
A common election is Jinja2, the same one used by Flask and other tools.
Starlette has utilities to configure it easily that you can use directly in your **FastAPI** application.
## Install dependencies
Install `jinja2`:
```bash
pip install jinja2
```
If you need to also serve static files (as in this example), install `aiofiles`:
```bash
pip install aiofiles
```
## Using `Jinja2Templates`
* Import `Jinja2Templates` form Starlette.
* Create a `templates` object that you can re-use later.
* Declare a `Request` parameter in the *path operation* that will return a template.
* Use the `templates` you created to render and return a `TemplateResponse`, passing the `request` as one of the key-value pairs in the Jinja2 "context".
```Python hl_lines="4 11 15 16"
{!./src/templates/tutorial001.py!}
```
!!! note
Notice that you have to pass the `request` as part of the key-value pairs in the context for Jinja2. So, you also have to declare it in your *path operation*.
## Writing templates
Then you can write a template at `templates/item.html` with:
```jinja hl_lines="7"
{!./src/templates/templates/item.html!}
```
It will show the `id` taken from the "context" `dict` you passed:
```Python
{"request": request, "id": id}
```
## Templates and static files
And you can also use `url_for()` inside of the template, and use it, for example, with the `StaticFiles` you mounted.
```jinja hl_lines="4"
{!./src/templates/templates/item.html!}
```
In this example, it would link to a CSS file at `static/styles.css` with:
```CSS hl_lines="4"
{!./src/templates/static/styles.css!}
```
And because you are using `StaticFiles`, that CSS file would be served automatically by your **FastAPI** application at the URL `/static/styles.css`.
## More details
For more details, including how to test templates, check <a href="https://www.starlette.io/templates/" class="external-link" target="_blank">Starlette's docs on templates</a>.
Up to now, you have been declaring the parts of the request that you need with their types.
Taking data from:
* The path as parameters.
* Headers.
* Cookies.
* etc.
And by doing so, **FastAPI** is validating that data, converting it and generating documentation for your API automatically.
But there are situations where you might need to access the `Request` object directly.
## Details about the `Request` object
As **FastAPI** is actually **Starlette** underneath, with a layer of several tools on top, you can use Starlette's <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> object directly when you need to.
It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or annotated (with OpenAPI, for the automatic documentation) by FastAPI.
Although any other parameter declared normally (for example, the body with a Pydantic model) would still be validated, converted, annotated, etc.
But there are specific cases where it's useful to get the `Request` object.
## Use the `Request` object directly
Let's imagine you want to get the client's IP address/host inside of your *path operation function*.
For that you need to access the request directly.
### Import the `Request`
First, import the `Request` class from Starlette:
```Python hl_lines="2"
{!./src/using_request_directly/tutorial001.py!}
```
### Declare the `Request` parameter
Then declare a *path operation function* parameter with the type being the `Request` class:
```Python hl_lines="8"
{!./src/using_request_directly/tutorial001.py!}
```
!!! tip
Note that in this case, we are declaring a path parameter besides the request parameter.
So, the path parameter will be extracted, validated, converted to the specified type and annotated with OpenAPI.
The same way, you can declare any other parameter as normally, and additionally, get the `Request` too.
## `Request` documentation
You can read more details about the <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request` object in the official Starlette documentation site</a>.
You can use <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> with **FastAPI**.
## WebSockets client
### In production
In your production system, you probably have a frontend created with a modern framework like React, Vue.js or Angular.
And to communicate using WebSockets with your backend you would probably use your frontend's utilities.
Or you might have a native mobile application that communicates with your WebSocket backend directly, in native code.
Or you might have any other way to communicate with the WebSocket endpoint.
---
But for this example, we'll use a very simple HTML document with some JavaScript, all inside a long string.
This, of course, is not optimal and you wouldn't use it for production.
In production you would have one of the options above.
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
In a WebSocket it doesn't really make sense to raise an `HTTPException`. So it's better to close the WebSocket connection directly.
You can use a closing code from the <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">valid codes defined in the specification</a>.
In the future, there will be a `WebSocketException` that you will be able to `raise` from anywhere, and add exception handlers for it. It depends on the <a href="https://github.com/encode/starlette/pull/527" class="external-link" target="_blank">PR #527</a> in Starlette.
## More info
To learn more about the options, check Starlette's documentation for:
First, you might want to see the basic ways to [help FastAPI and get help](help-fastapi.md){.internal-link target=_blank}.
## Developing
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
### Virtual environment with `venv`
You can create a virtual environment in a directory using Python's `venv` module:
```console
$ python -m venv env
```
That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment.
### Activate the environment
Activate the new environment with:
```console
$source ./env/bin/activate
```
Or in Windows' PowerShell:
```console
$ .\env\Scripts\Activate.ps1
```
Or if you use Bash for Windows (e.g. <a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>):
```console
$source ./env/Scripts/activate
```
To check it worked, use:
```console
$ which pip
some/directory/fastapi/env/bin/pip
```
If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉
Or in Windows PowerShell:
```console
$ Get-Command pip
some/directory/fastapi/env/bin/pip
```
!!! tip
Every time you install a new package with `pip` under that environment, activate the environment again.
This makes sure that if you use a terminal program installed by that package (like `flit`), you use the one from your local environment and not any other that could be installed globally.
### Flit
**FastAPI** uses <a href="https://flit.readthedocs.io/en/latest/index.html" class="external-link" target="_blank">Flit</a> to build, package and publish the project.
After activating the environment as described above, install `flit`:
```console
$ pip install flit
```
Now re-activate the environment to make sure you are using the `flit` you just installed (and not a global one).
And now use `flit` to install the development dependencies:
```console
$ flit install --deps develop --symlink
```
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 local FastAPI source code.
And if you update that local FastAPI source code, as it is installed with `--symlink`, 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.
### Format
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.
For it to sort them correctly, you need to have FastAPI installed locally in your environment, with the command in the section above:
```console
$ flit install --symlink
```
### Format imports
There is another script that formats all the imports and makes sure you don't have unused imports:
```console
$ bash scripts/format-imports.sh
```
As it runs one command after the other and modifies and reverts many files, it takes a bit longer to run, so it might be easier to use `scripts/format.sh` frequently and `scripts/format-imports.sh` only before committing.
## Docs
The documentation uses <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a>.
All the documentation is in Markdown format in the directory `./docs`.
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 making 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.
During local development, there is a script that builds the site and checks for any changes, live-reloading:
```console
$ bash scripts/docs-live.sh
```
It will serve the documentation on `http://0.0.0.0:8008`.
That way, you can edit the documentation/source files and see the changes live.
### Apps and docs at the same time
If you run the examples with, e.g.:
```console
$ uvicorn tutorial001:app --reload
```
as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash.
## 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.
### Tests in your editor
If you want to use the integrated tests in your editor add `./docs/src` to your `PYTHONPATH` variable.
For example, in VS Code you can create a file `.env` with:
Dieses Dokument testet, ob das <abbr title="Large Language Model – Großes Sprachmodell">LLM</abbr>, das die Dokumentation übersetzt, den <abbr title="General Prompt – Allgemeiner Prompt">`general_prompt`</abbr> in `scripts/translate.py` und den sprachspezifischen Prompt in `docs/{language code}/llm-prompt.md` versteht. Der sprachspezifische Prompt wird an `general_prompt` angehängt.
Hier hinzugefügte Tests werden von allen Erstellern sprachspezifischer Prompts gesehen.
So verwenden:
* Einen sprachspezifischen 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 <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLMs keine deterministischen Algorithmen sind</a>).
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 unkorrekt korrekt schreibt, hat man es unkorrekt geschrieben“. Worauf ich antwortete: „Korrekt, aber ‚unkorrekt‘ ist unkorrekterweise nicht ‚„unkorrekt“‘“.
/// 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
$ <font color="#4E9A06">fastapi</font> run <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting server
Searching for package file structure
```
... und noch ein Konsolen-Codebeispiel ...
```console
// Ein Verzeichnis „Code“ erstellen
$ 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:
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 }
* <abbr title="Getting Things Done – Dinge erledigt bekommen">GTD</abbr>
* <abbr title="less than – kleiner als"><code>lt</code></abbr>
### Das abbr gibt eine Erklärung { #the-abbr-gives-an-explanation }
* <abbr title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und in irgendeiner Weise zusammenarbeiten.">Cluster</abbr>
* <abbr title="Eine Methode des Machine Learning, die künstliche neuronale Netze mit zahlreichen versteckten Schichten zwischen Eingabe- und Ausgabeschicht verwendet und so eine umfassende interne Struktur entwickelt">Deep Learning</abbr>
### Das abbr gibt eine vollständige Phrase und eine Erklärung { #the-abbr-gives-a-full-phrase-and-an-explanation }
* <abbr title="Mozilla Developer Network – Mozilla-Entwicklernetzwerk: Dokumentation für Entwickler, geschrieben von den Firefox-Leuten">MDN</abbr>
* <abbr title="Input/Output – Eingabe/Ausgabe: Lesen oder Schreiben auf der Festplatte, Netzwerkkommunikation.">I/O</abbr>.
////
//// 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`.
////
## Ü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 sprachspezifische 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`.
# 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 <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> 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 <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">`dict`</abbr> 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:
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:
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:
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.
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:
## 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.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">OpenAPI Response Object</a>, 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`.
Standardmäßig liefert **FastAPI** die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> 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.
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:
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){.internal-link target=_blank}.
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 <abbr title="Englisch „callable“">„aufrufbar“</abbr> zu machen.
Nicht die Klasse selbst (die bereits aufrufbar ist), sondern eine Instanz dieser Klasse.
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:
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`:
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.
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:
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 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub-Diskussion-Frage</a> 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){.internal-link target=_blank} 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.
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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> 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 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> 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){.internal-link target=_blank} und [Testen](../tutorial/testing.md){.internal-link target=_blank}:
```
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py
```
Die Datei `main.py` hätte als Inhalt:
{* ../../docs_src/async_tests/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/test_main.py *}
## Es ausführen { #run-it }
Sie können Ihre Tests wie gewohnt ausführen mit:
<div class="termy">
```console
$ pytest
---> 100%
```
</div>
## Im Detail { #in-detail }
Der Marker `@pytest.mark.anyio` teilt pytest mit, dass diese Testfunktion asynchron aufgerufen werden soll:
... 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 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a>.
///
## 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 <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDBs MotorClient</a>), 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.
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.
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.
<div class="termy">
```console
$ fastapi run --forwarded-allow-ips="*"
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Weiterleitungen mit HTTPS { #redirects-with-https }
Angenommen, Sie definieren eine *Pfadoperation*`/items/`:
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){.internal-link target=_blank}.
///
### 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:
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 <abbr title="Wurzelpfad">`root_path`</abbr> 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.
Und der Proxy würde das **Pfadpräfix** on-the-fly **„entfernen“**, bevor er den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> 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:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
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`-<abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionarys</abbr> (das ist Teil der ASGI-Spezifikation).
Hier fügen wir ihn, nur zu Demonstrationszwecken, in die Nachricht ein.
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
wäre die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> 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:
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 <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> 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 <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a> verwenden.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Laden Sie Traefik herunter</a>, 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:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
```
</div>
Und jetzt starten Sie Ihre Anwendung mit Uvicorn, indem Sie die Option `--root-path` verwenden:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Die Responses testen { #check-the-responses }
Wenn Sie nun zur URL mit dem Port für Uvicorn gehen: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, 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: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
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 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> 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 <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> testen:
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.
Beachten Sie den automatisch generierten Server mit dem `URL`-Wert `/api/v1`, welcher vom `root_path` stammt.
///
In der Dokumentationsoberfläche unter <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> würde es so aussehen:
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:
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){.internal-link target=_blank}) 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. ✨
Standardmäßig gibt **FastAPI** die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> mittels `JSONResponse` zurück.
Sie können dies überschreiben, indem Sie direkt eine `Response` zurückgeben, wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} 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` deklariert haben), und die Dokumentation wird nicht automatisch generiert (zum Beispiel wird der spezifische „Medientyp“, der im HTTP-Header `Content-Type` angegeben ist, nicht 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.
Und wenn diese `Response` einen JSON-Medientyp (`application/json`) hat, wie es bei `JSONResponse` und `UJSONResponse` der Fall ist, werden die von Ihnen zurückgegebenen Daten automatisch mit jedem Pydantic `response_model` konvertiert (und gefiltert), das Sie im *Pfadoperation-Dekorator* deklariert haben.
/// 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.
Um beispielsweise noch etwas Leistung herauszuholen, können Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> installieren und die Response als `ORJSONResponse` setzen.
Importieren Sie die `Response`-Klasse (Unterklasse), die Sie verwenden möchten, und deklarieren Sie sie im *Pfadoperation-Dekorator*.
Bei umfangreichen Responses ist die direkte Rückgabe einer `Response` wesentlich schneller als ein <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> zurückzugeben.
Das liegt daran, dass FastAPI standardmäßig jedes enthaltene Element überprüft und sicherstellt, dass es als JSON serialisierbar ist, und zwar unter Verwendung desselben [JSON-kompatiblen Encoders](../tutorial/encoder.md){.internal-link target=_blank}, der im Tutorial erläutert wurde. Dadurch können Sie **beliebige Objekte** zurückgeben, zum Beispiel Datenbankmodelle.
Wenn Sie jedoch sicher sind, dass der von Ihnen zurückgegebene Inhalt **mit JSON serialisierbar** ist, können Sie ihn direkt an die Response-Klasse übergeben und die zusätzliche Arbeit vermeiden, die FastAPI hätte, indem es Ihren zurückgegebenen Inhalt durch den `jsonable_encoder` leitet, bevor es ihn an die Response-Klasse übergibt.
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){.internal-link target=_blank} 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:
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 }
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:
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.
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.
### `ORJSONResponse` { #orjsonresponse }
Eine schnelle alternative JSON-Response mit <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, wie Sie oben gelesen haben.
/// info | Info
Dazu muss `orjson` installiert werden, z. B. mit `pip install orjson`.
///
### `UJSONResponse` { #ujsonresponse }
Eine alternative JSON-Response mit <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
/// info | Info
Dazu muss `ujson` installiert werden, z. B. mit `pip install ujson`.
///
/// warning | Achtung
`ujson` ist bei der Behandlung einiger Sonderfälle weniger sorgfältig als Pythons eingebaute Implementierung.
#### Verwendung von `StreamingResponse` mit dateiartigen Objekten { #using-streamingresponse-with-file-like-objects }
Wenn Sie ein dateiartiges (<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a>) Objekt haben (z. B. das von `open()` zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiartige Objekt zu iterieren.
Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und können diese Generatorfunktion an `StreamingResponse` übergeben und zurückgeben.
Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen.
1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält.
2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiartige Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
3. Dieses `yield from` weist die Funktion an, über das Ding namens `file_like` zu iterieren. Und dann für jeden iterierten Teil, diesen Teil so zurückzugeben, als wenn er aus dieser Generatorfunktion (`iterfile`) stammen würde.
Es handelt sich also hier um eine Generatorfunktion, die die „generierende“ Arbeit intern auf etwas anderes überträgt.
Auf diese Weise können wir das Ganze in einen `with`-Block einfügen und so sicherstellen, dass das dateiartige Objekt nach Abschluss geschlossen wird.
/// tip | Tipp
Beachten Sie, dass wir, da wir Standard-`open()` verwenden, welches `async` und `await` nicht unterstützt, hier die Pfadoperation mit normalen `def` deklarieren.
///
### `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.
Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von `Response` erbt und diese verwendet.
Nehmen wir zum Beispiel an, dass Sie <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> verwenden möchten, aber mit einigen benutzerdefinierten Einstellungen, die in der enthaltenen `ORJSONResponse`-Klasse nicht verwendet werden.
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:
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 `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`.
Sie können auch den Medientyp und viele andere Details in OpenAPI mit `responses` deklarieren: [Zusätzliche Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
# Verwendung von Datenklassen { #using-dataclasses }
FastAPI basiert auf **Pydantic**, und ich habe Ihnen gezeigt, wie Sie Pydantic-Modelle verwenden können, um <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> und <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> zu deklarieren.
Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>:
Das ist dank **Pydantic** ebenfalls möglich, da es <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses` intern unterstützt</a>.
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:
Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert.
Auf diese Weise wird deren Schema in der Benutzeroberfläche der API-Dokumentation angezeigt:
<img src="/img/tutorial/dataclasses/image01.png">
## 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:
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 <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> zurück, das `items` enthält, welches eine Liste von Datenklassen ist.
FastAPI ist weiterhin in der Lage, die Daten nach JSON zu <abbr title="Konvertieren der Daten in ein übertragbares Format">serialisieren</abbr>.
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){.internal-link target=_blank}.
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 <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">Pydantic-Dokumentation zu Datenklassen</a>.
## Version { #version }
Dies ist verfügbar seit FastAPI-Version `0.67.0`. 🔖
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, <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> 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-<abbr title="Lebensdauer">**Lifespan**</abbr> 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 <abbr title="Hochfahren">*Startup*</abbr> und <abbr title="Herunterfahren">*Shutdown*</abbr> 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:
Hier simulieren wir den langsamen *Startup*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> 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`.
Ein **Kontextmanager** in Python ist etwas, das Sie in einer `with`-Anweisung verwenden können, zum Beispiel kann `open()` als Kontextmanager verwendet werden:
```Python
withopen("file.txt")asfile:
file.read()
```
In neueren Versionen von Python gibt es auch einen **asynchronen Kontextmanager**. Sie würden ihn mit `async with` verwenden:
```Python
asyncwithlifespan(app):
awaitdo_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.py hl[22] *}
## Alternative Events (<abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr>) { #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 <abbr title="Eventhandler – Ereignisbehandler: Funktion, die beim Eintreten eines bestimmten Ereignisses ausgeführt wird">Eventhandler</abbr> (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.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.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 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan Protokolls</a> und definiert Events namens `startup` und `shutdown`.
/// info | Info
Weitere Informationen zu Starlettes `lifespan`-Handlern finden Sie in <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlettes Lifespan-Dokumentation</a>.
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){.internal-link target=_blank}.
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 (<abbr title="Software Development Kit – Software-Entwicklungspaket">**SDKs**</abbr>) 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 <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, der **viele Programmiersprachen** unterstützt und SDKs aus Ihrer OpenAPI-Spezifikation generieren kann.
Für **TypeScript-Clients** ist <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> eine speziell entwickelte Lösung, die ein optimiertes Erlebnis für das TypeScript-Ökosystem bietet.
Weitere SDK-Generatoren finden Sie auf <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
/// 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){.internal-link target=_blank} ✨ 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. 🙇
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:
Beachten Sie, dass die *Pfadoperationen* die Modelle definieren, die sie für die <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>- und <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-<abbr title="Die eigentlichen Nutzdaten, abzüglich der Metadaten">Payload</abbr> 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.
Dies generiert ein TypeScript-SDK in `./src/client`.
Sie können lernen, wie man <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">`@hey-api/openapi-ts` installiert</a> und über die <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">erzeugte Ausgabe</a> 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:
## 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 (Artikel)** und einen weiteren Abschnitt für **Users (Benutzer)** haben, und diese könnten durch Tags getrennt sein:
... 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:
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**:
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 modifizierten 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:
Nach der Generierung des neuen Clients haben Sie jetzt **saubere Methodennamen**, mit allen **Autovervollständigungen**, **Inline-Fehlerberichten**, usw.:
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. ✨
# Handbuch für fortgeschrittene Benutzer { #advanced-user-guide }
## Zusatzfunktionen { #additional-features }
Das Haupt-[Tutorial – Benutzerhandbuch](../tutorial/index.md){.internal-link target=_blank} 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){.internal-link target=_blank} nutzen.
Und die nächsten Abschnitte setzen voraus, dass Sie es bereits gelesen haben und dass Sie diese Hauptideen kennen.
Im Haupttutorial haben Sie gelesen, wie Sie Ihrer Anwendung [benutzerdefinierte Middleware](../tutorial/middleware.md){.internal-link target=_blank} hinzufügen können.
Und dann auch, wie man [CORS mittels der `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank} handhabt.
In diesem Abschnitt werden wir sehen, wie man andere Middlewares verwendet.
Da **FastAPI** auf Starlette basiert und die <abbr title="Asynchrones Server-Gateway-Interface">ASGI</abbr>-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:
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).
`app.add_middleware()` empfängt eine Middleware-Klasse als erstes Argument und dann alle weiteren Argumente, die an die Middleware übergeben werden sollen.
**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.
Erzwingt, dass alle eingehenden <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> entweder `https` oder `wss` sein müssen.
Alle eingehenden Requests an `http` oder `ws` werden stattdessen an das sichere Schema umgeleitet.
*`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`-<abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> 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.
*`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.
Um mehr über weitere verfügbare Middlewares herauszufinden, besuchen Sie <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Starlettes Middleware-Dokumentation</a> und die <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome List</a>.
Sie könnten eine API mit einer *Pfadoperation* erstellen, die einen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> 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 <abbr title="„Rückruf“">„Callback“</abbr> 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 <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> 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:
Der Query-Parameter `callback_url` verwendet einen Pydantic-<a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>-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:
Der möglicherweise wichtigste Teil des Callbacks besteht jedoch darin, sicherzustellen, dass Ihr API-Benutzer (der externe Entwickler) die *externe API* gemäß den Daten, die *Ihre API* im Requestbody des Callbacks senden wird, korrekt implementiert, 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 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> oder <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a> 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.
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 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI-3-Ausdruck</a> 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 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI-3-Ausdruck</a> 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:
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:
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 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> gehen.
Sie sehen Ihre Dokumentation, einschließlich eines Abschnitts „Callbacks“ für Ihre *Pfadoperation*, der zeigt, wie die *externe API* aussehen sollte:
Es gibt Fälle, in denen Sie Ihren API-**Benutzern** mitteilen möchten, dass Ihre App *deren* App mit einigen Daten aufrufen (einen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> 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 **Web<abbr title="Haken, Einhängepunkt">hook</abbr>** bezeichnet.
## Webhooks-Schritte { #webhooks-steps }
Der Prozess besteht normalerweise darin, dass **Sie in Ihrem Code definieren**, welche Nachricht Sie senden möchten, den **Body des Requests**.
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-<abbr title="Benutzeroberfläche für das Visualisieren und Managen von Daten">Dashboard</abbr>) 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 Request**bodys** 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()`.
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 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> gehen.
Sie werden sehen, dass Ihre Dokumentation die normalen *Pfadoperationen* und jetzt auch einige **Webhooks** enthält:
### 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.
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`:
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 (z. B. Sphinx) können den Rest verwenden.
Sie haben wahrscheinlich gesehen, wie man das `response_model` und den `status_code` für eine *Pfadoperation* deklariert.
Das definiert die Metadaten der Haupt-<abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> 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){.internal-link target=_blank} 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 das <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Operationsobjekt</a> 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){.internal-link target=_blank} 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:
Und wenn Sie die resultierende OpenAPI sehen (unter `/openapi.json` in Ihrer API), sehen Sie Ihre Erweiterung auch als Teil der spezifischen *Pfadoperation*:
Das <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> 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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> mit Ihrem eigenen Code zu lesen und zu validieren, ohne die automatischen Funktionen von FastAPI mit Pydantic zu verwenden, aber Sie könnten den Request trotzdem im OpenAPI-Schema definieren wollen.
In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON <abbr title="von einem einfachen Format, wie Bytes, in Python-Objekte konvertieren">geparst</abbr>, 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.
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 die integrierte Funktionalität von FastAPI 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:
In Pydantic Version 1 hieß die Methode zum Abrufen des JSON-Schemas für ein Modell `Item.schema()`, in Pydantic Version 2 heißt die Methode `Item.model_json_schema()`.
///
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, den 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:
In Pydantic Version 1 war die Methode zum Parsen und Validieren eines Objekts `Item.parse_obj()`, in Pydantic Version 2 heißt die Methode `Item.model_validate()`.
///
/// tip | Tipp
Hier verwenden wir dasselbe Pydantic-Modell wieder.
Aber genauso hätten wir es auch auf andere Weise validieren können.
Sie haben wahrscheinlich schon vorher gelesen, dass Sie einen Default-[Response-Statuscode](../tutorial/response-status-code.md){.internal-link target=_blank} 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* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
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.
## 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* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt setzen.
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){.internal-link target=_blank} beschrieben.
Setzen Sie dann Cookies darin und geben Sie sie dann zurück:
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 <a href="https://www.starlette.dev/responses/#set-cookie" class="external-link" target="_blank">Dokumentation in Starlette</a> an.
# 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.
Standardmäßig konvertiert **FastAPI** diesen Rückgabewert automatisch nach JSON, mithilfe des `jsonable_encoder`, der in [JSON-kompatibler Encoder](../tutorial/encoder.md){.internal-link target=_blank} erläutert wird.
Dann würde es hinter den Kulissen diese JSON-kompatiblen Daten (z. B. ein `dict`) in eine `JSONResponse` einfügen, die zum Senden der <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> an den Client verwendet wird.
Sie können jedoch direkt eine `JSONResponse` von Ihren *Pfadoperationen* zurückgeben.
Das kann beispielsweise nützlich sein, um benutzerdefinierte Header oder Cookies zurückzugeben.
## Eine `Response` zurückgeben { #return-a-response }
Tatsächlich können Sie jede `Response` oder jede Unterklasse davon zurückgeben.
/// tip | Tipp
`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.
## 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:
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 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>-Response zurückgeben.
Sie könnten Ihren XML-Inhalt als String in eine `Response` einfügen und sie zurückgeben:
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){.internal-link target=_blank} 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.
## 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* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
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){.internal-link target=_blank} beschrieben und übergeben Sie die Header als zusätzlichen Parameter:
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 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">mittels des Präfix `X-`</a> 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){.internal-link target=_blank}), unter Verwendung des Parameters `expose_headers`, dokumentiert in <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">Starlettes CORS-Dokumentation</a>.
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:
<img src="/img/tutorial/security/image12.png">
## 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 <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a>, 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.
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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> mit dem Benutzernamen `johndoe` und dem Passwort `love123`.
Dann würde der Python-Code in Ihrer Anwendung etwa so aussehen:
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`.
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 <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> „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:
Neben den in [Tutorial – Benutzerhandbuch: Sicherheit](../../tutorial/security/index.md){.internal-link target=_blank} 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){.internal-link target=_blank} bereits gelesen haben.
Sie basieren alle auf den gleichen Konzepten, ermöglichen jedoch einige zusätzliche Funktionalitäten.
Sie können OAuth2-<abbr title="Geltungsbereiche">Scopes</abbr> 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){.internal-link target=_blank} ändern. Diesmal verwenden wir OAuth2-Scopes:
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 <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">`dict`</abbr> mit jedem Scope als Schlüssel und dessen Beschreibung als Wert:
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:
<img src="/img/tutorial/security/image11.png">
## 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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> 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.
## 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.
`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.
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).
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).
## 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.
## 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.
## 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.
<img src="/img/tutorial/security/image11.png">
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){.internal-link target=_blank} erläutert), Sie könnten auch dort `Security` mit `scopes` verwenden.
# 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){.internal-link target=_blank} 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 <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings Management</a>.
Stellen Sie zunächst sicher, dass Sie Ihre [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellt und aktiviert haben, und installieren Sie dann das Package `pydantic-settings`:
<div class="termy">
```console
$ pip install pydantic-settings
---> 100%
```
</div>
Es ist bereits enthalten, wenn Sie die `all`-Extras installiert haben, mit:
<div class="termy">
```console
$ pip install "fastapi[all]"
---> 100%
```
</div>
/// info | Info
In Pydantic v1 war es im Hauptpackage enthalten. Jetzt wird es als unabhängiges Package verteilt, sodass Sie wählen können, ob Sie es installieren möchten oder nicht, falls Sie die Funktionalität nicht benötigen.
///
### 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()`.
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:
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:
<div class="termy">
```console
$ADMIN_EMAIL="deadpool@example.com"APP_NAME="ChimichangApp" fastapi run main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
/// 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){.internal-link target=_blank} gesehen haben.
Sie könnten beispielsweise eine Datei `config.py` haben mit:
{* ../../docs_src/settings/app01/config.py *}
Und dann verwenden Sie diese in einer Datei `main.py`:
Sie benötigen außerdem eine Datei `__init__.py`, wie in [Größere Anwendungen – mehrere Dateien](../tutorial/bigger-applications.md){.internal-link target=_blank} 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:
### 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:
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 <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support" class="external-link" target="_blank">Pydantic Settings: Dotenv (.env) support</a>.
/// 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 }
Das Attribut `model_config` wird nur für die Pydantic-Konfiguration verwendet. Weitere Informationen finden Sie unter <a href="https://docs.pydantic.dev/latest/concepts/config/" class="external-link" target="_blank">Pydantic: Concepts: Configuration</a>.
Die Klasse `Config` wird nur für die Pydantic-Konfiguration verwendet. Weitere Informationen finden Sie unter <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
///
////
/// info | Info
In Pydantic Version 1 erfolgte die Konfiguration in einer internen Klasse `Config`, in Pydantic Version 2 erfolgt sie in einem Attribut `model_config`. Dieses Attribut akzeptiert ein <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">`dict`</abbr>. Um automatische Codevervollständigung und Inline-Fehlerberichte zu erhalten, können Sie `SettingsConfigDict` importieren und verwenden, um dieses `dict` zu definieren.
///
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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> 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
defget_settings():
returnSettings()
```
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. ✔️
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
defsay_hi(name:str,salutation:str="Ms."):
returnf"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
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 <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python Dokumentation für `@lru_cache`</a>.
## 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.
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*:
Öffnen Sie dann die Dokumentation für die Unteranwendung unter <a href="http://127.0.0.1:8000/subapi/docs" class="external-link" target="_blank">http://127.0.0.1:8000/subapi/docs</a>.
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.
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){.internal-link target=_blank}.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und `jinja2` installieren:
* Erstellen Sie ein `templates`-Objekt, das Sie später wiederverwenden können.
* Deklarieren Sie einen `<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>`-Parameter in der *Pfadoperation*, welcher ein Template zurückgibt.
* Verwenden Sie die von Ihnen erstellten `templates`, um eine `TemplateResponse` zu rendern und zurückzugeben, übergeben Sie den Namen des Templates, das Requestobjekt und ein „Kontext“-<abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> mit Schlüssel-Wert-Paaren, die innerhalb des Jinja2-Templates verwendet werden sollen.
Vor FastAPI 0.108.0 und Starlette 0.29.0 war `name` der erste Parameter.
Außerdem wurde in früheren Versionen das `request`-Objekt als Teil der Schlüssel-Wert-Paare im Kontext für Jinja2 übergeben.
///
/// tip | Tipp
Durch die Deklaration von `response_class=HTMLResponse` kann die Dokumentationsoberfläche erkennen, dass die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> HTML sein wird.
///
/// note | Technische Details
Sie können auch `from starlette.templating import Jinja2Templates` verwenden.
**FastAPI** bietet dasselbe `starlette.templating` auch via `fastapi.templating` an, als Annehmlichkeit für Sie, den Entwickler. Aber die meisten der verfügbaren Responses kommen direkt von Starlette. Das Gleiche gilt für `Request` und `StaticFiles`.
///
## Templates erstellen { #writing-templates }
Dann können Sie unter `templates/item.html` ein Template erstellen, mit z. B. folgendem Inhalt:
Sie können `url_for()` auch innerhalb des Templates verwenden, es nimmt als Argumente dieselben Argumente, die von Ihrer *Pfadoperation-Funktion* verwendet werden.
Der Abschnitt mit:
{% raw %}
```jinja
<a href="{{ url_for('read_item', id=id) }}">
```
{% endraw %}
... generiert also einen Link zu derselben URL, welche von der *Pfadoperation-Funktion* `read_item(id=id)` gehandhabt werden würde.
Mit beispielsweise der ID `42` würde dies Folgendes ergeben:
```html
<a href="/items/42">
```
## Templates und statische Dateien { #templates-and-static-files }
Sie können `url_for()` innerhalb des Templates auch beispielsweise mit den `StaticFiles` verwenden, die Sie mit `name="static"` gemountet haben.
```jinja hl_lines="4"
{!../../docs_src/templates/templates/item.html!}
```
In diesem Beispiel würde das zu einer CSS-Datei unter `static/styles.css` verlinken, mit folgendem Inhalt:
```CSS hl_lines="4"
{!../../docs_src/templates/static/styles.css!}
```
Und da Sie `StaticFiles` verwenden, wird diese CSS-Datei automatisch von Ihrer **FastAPI**-Anwendung unter der URL `/static/styles.css` ausgeliefert.
## Mehr Details { #more-details }
Weitere Informationen, einschließlich, wie man Templates testet, finden Sie in <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">Starlettes Dokumentation zu Templates</a>.
# Testen mit Überschreibungen für Abhängigkeiten { #testing-dependencies-with-overrides }
## Abhängigkeiten beim Testen überschreiben { #overriding-dependencies-during-testing }
Es gibt einige Szenarien, in denen Sie beim Testen möglicherweise eine Abhängigkeit überschreiben möchten.
Sie möchten nicht, dass die ursprüngliche Abhängigkeit ausgeführt wird (und auch keine der möglicherweise vorhandenen Unterabhängigkeiten).
Stattdessen möchten Sie eine andere Abhängigkeit bereitstellen, die nur während Tests (möglicherweise nur bei einigen bestimmten Tests) verwendet wird und einen Wert bereitstellt, der dort verwendet werden kann, wo der Wert der ursprünglichen Abhängigkeit verwendet wurde.
### Anwendungsfälle: Externer Service { #use-cases-external-service }
Ein Beispiel könnte sein, dass Sie einen externen Authentifizierungsanbieter haben, mit dem Sie sich verbinden müssen.
Sie senden ihm ein Token und er gibt einen authentifizierten Benutzer zurück.
Dieser Anbieter berechnet Ihnen möglicherweise Gebühren pro <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>, und der Aufruf könnte etwas länger dauern, als wenn Sie einen vordefinierten <abbr title="Platzhalter, vorgetäuscht, zum Schein">Mock</abbr>-Benutzer für Tests hätten.
Sie möchten den externen Anbieter wahrscheinlich einmal testen, ihn aber nicht unbedingt bei jedem weiteren ausgeführten Test aufrufen.
In diesem Fall können Sie die Abhängigkeit, die diesen Anbieter aufruft, überschreiben und eine benutzerdefinierte Abhängigkeit verwenden, die einen Mock-Benutzer zurückgibt, nur für Ihre Tests.
### Das Attribut `app.dependency_overrides` verwenden { #use-the-app-dependency-overrides-attribute }
Für diese Fälle verfügt Ihre **FastAPI**-Anwendung über das Attribut `app.dependency_overrides`, bei diesem handelt sich um ein einfaches <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">`dict`</abbr>.
Um eine Abhängigkeit für das Testen zu überschreiben, geben Sie als Schlüssel die ursprüngliche Abhängigkeit (eine Funktion) und als Wert Ihre Überschreibung der Abhängigkeit (eine andere Funktion) ein.
Und dann ruft **FastAPI** diese Überschreibung anstelle der ursprünglichen Abhängigkeit auf.
Sie können eine Überschreibung für eine Abhängigkeit festlegen, die an einer beliebigen Stelle in Ihrer **FastAPI**-Anwendung verwendet wird.
Die ursprüngliche Abhängigkeit könnte in einer *Pfadoperation-Funktion*, einem *Pfadoperation-Dekorator* (wenn Sie den Rückgabewert nicht verwenden), einem `.include_router()`-Aufruf, usw. verwendet werden.
FastAPI kann sie in jedem Fall überschreiben.
///
Anschließend können Sie Ihre Überschreibungen zurücksetzen (entfernen), indem Sie `app.dependency_overrides` auf ein leeres `dict` setzen:
```Python
app.dependency_overrides={}
```
/// tip | Tipp
Wenn Sie eine Abhängigkeit nur während einiger Tests überschreiben möchten, können Sie die Überschreibung zu Beginn des Tests (innerhalb der Testfunktion) festlegen und am Ende (am Ende der Testfunktion) zurücksetzen.
Sie können mehr Details unter [„Lifespan in Tests ausführen in der offiziellen Starlette-Dokumentation.“](https://www.starlette.dev/lifespan/#running-lifespan-in-tests) nachlesen.
Für die deprecateten Events <abbr title="Hochfahren">`startup`</abbr> und <abbr title="Herunterfahren">`shutdown`</abbr> können Sie den `TestClient` wie folgt verwenden:
Weitere Informationen finden Sie in Starlettes Dokumentation zum <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">Testen von WebSockets</a>.
# Den Request direkt verwenden { #using-the-request-directly }
Bisher haben Sie die Teile des <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr>, die Sie benötigen, mithilfe von deren Typen deklariert.
Daten nehmend von:
* Dem Pfad als Parameter.
* Headern.
* Cookies.
* usw.
Und indem Sie das tun, validiert **FastAPI** diese Daten, konvertiert sie und generiert automatisch Dokumentation für Ihre API.
Es gibt jedoch Situationen, in denen Sie möglicherweise direkt auf das `Request`-Objekt zugreifen müssen.
## Details zum `Request`-Objekt { #details-about-the-request-object }
Da **FastAPI** unter der Haube eigentlich **Starlette** ist, mit einer Ebene von mehreren Tools darüber, können Sie Starlettes <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a>-Objekt direkt verwenden, wenn Sie es benötigen.
Das bedeutet allerdings auch, dass, wenn Sie Daten direkt vom `Request`-Objekt nehmen (z. B. dessen Body lesen), diese von FastAPI nicht validiert, konvertiert oder dokumentiert werden (mit OpenAPI, für die automatische API-Benutzeroberfläche).
Obwohl jeder andere normal deklarierte Parameter (z. B. der Body, mit einem Pydantic-Modell) dennoch validiert, konvertiert, annotiert, usw. werden würde.
Es gibt jedoch bestimmte Fälle, in denen es nützlich ist, auf das `Request`-Objekt zuzugreifen.
## Das `Request`-Objekt direkt verwenden { #use-the-request-object-directly }
Angenommen, Sie möchten auf die IP-Adresse/den Host des Clients in Ihrer *Pfadoperation-Funktion* zugreifen.
Durch die Deklaration eines *Pfadoperation-Funktionsparameters*, dessen Typ der `Request` ist, weiß **FastAPI**, dass es den `Request` diesem Parameter übergeben soll.
/// tip | Tipp
Beachten Sie, dass wir in diesem Fall einen Pfad-Parameter zusätzlich zum Request-Parameter deklarieren.
Der Pfad-Parameter wird also extrahiert, validiert, in den spezifizierten Typ konvertiert und mit OpenAPI annotiert.
Auf die gleiche Weise können Sie wie gewohnt jeden anderen Parameter deklarieren und zusätzlich auch den `Request` erhalten.
Weitere Details zum <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`-Objekt finden Sie in der offiziellen Starlette-Dokumentation</a>.
/// note | Technische Details
Sie können auch `from starlette.requests import Request` verwenden.
**FastAPI** stellt es direkt zur Verfügung, als Komfort für Sie, den Entwickler. Es kommt aber direkt von Starlette.
Sie können <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> mit **FastAPI** verwenden.
Stellen Sie sicher, dass Sie eine [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und `websockets` installieren (eine Python-Bibliothek, die die Verwendung des „WebSocket“-Protokolls erleichtert):
<div class="termy">
```console
$ pip install websockets
---> 100%
```
</div>
## WebSockets-Client { #websockets-client }
### In Produktion { #in-production }
In Ihrem Produktionssystem haben Sie wahrscheinlich ein Frontend, das mit einem modernen Framework wie React, Vue.js oder Angular erstellt wurde.
Und um über WebSockets mit Ihrem Backend zu kommunizieren, würden Sie wahrscheinlich die Werkzeuge Ihres Frontends verwenden.
Oder Sie verfügen möglicherweise über eine native Mobile-Anwendung, die direkt in nativem Code mit Ihrem WebSocket-Backend kommuniziert.
Oder Sie haben andere Möglichkeiten, mit dem WebSocket-Endpunkt zu kommunizieren.
---
Für dieses Beispiel verwenden wir jedoch ein sehr einfaches HTML-Dokument mit etwas JavaScript, alles in einem langen String.
Das ist natürlich nicht optimal und man würde das nicht in der Produktion machen.
In der Produktion hätten Sie eine der oben genannten Optionen.
Aber es ist der einfachste Weg, sich auf die Serverseite von WebSockets zu konzentrieren und ein funktionierendes Beispiel zu haben:
Da es sich um einen WebSocket handelt, macht es keinen Sinn, eine `HTTPException` auszulösen, stattdessen lösen wir eine `WebSocketException` aus.
Sie können einen „Closing“-Code verwenden, aus den <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">gültigen Codes, die in der Spezifikation definiert sind</a>.
///
### WebSockets mit Abhängigkeiten ausprobieren { #try-the-websockets-with-dependencies }
Wenn Ihre Datei `main.py` heißt, führen Sie Ihre Anwendung mit Folgendem aus:
<div class="termy">
```console
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Öffnen Sie Ihren Browser unter <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
Dort können Sie einstellen:
* Die „Item ID“, die im Pfad verwendet wird.
* Das „Token“, das als Query-Parameter verwendet wird.
/// tip | Tipp
Beachten Sie, dass die Query `token` von einer Abhängigkeit verarbeitet wird.
///
Damit können Sie den WebSocket verbinden und dann Nachrichten senden und empfangen:
<img src="/img/tutorial/websockets/image05.png">
## Verbindungsabbrüche und mehrere Clients handhaben { #handling-disconnections-and-multiple-clients }
Wenn eine WebSocket-Verbindung geschlossen wird, löst `await websocket.receive_text()` eine `WebSocketDisconnect`-Exception aus, die Sie dann wie in folgendem Beispiel abfangen und behandeln können.
* Öffnen Sie die Anwendung mit mehreren Browser-Tabs.
* Schreiben Sie Nachrichten in den Tabs.
* Schließen Sie dann einen der Tabs.
Das wird die Ausnahme `WebSocketDisconnect` auslösen und alle anderen Clients erhalten eine Nachricht wie:
```
Client #1596980209979 left the chat
```
/// tip | Tipp
Die obige Anwendung ist ein minimales und einfaches Beispiel, das zeigt, wie Nachrichten verarbeitet und an mehrere WebSocket-Verbindungen gesendet werden.
Beachten Sie jedoch, dass, da alles nur im Speicher in einer einzigen Liste verwaltet wird, es nur funktioniert, während der Prozess ausgeführt wird, und nur mit einem einzelnen Prozess.
Wenn Sie etwas benötigen, das sich leicht in FastAPI integrieren lässt, aber robuster ist und von Redis, PostgreSQL und anderen unterstützt wird, sehen Sie sich <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a> an.
///
## Mehr Informationen { #more-info }
Weitere Informationen zu Optionen finden Sie in der Dokumentation von Starlette:
# WSGI inkludieren – Flask, Django und andere { #including-wsgi-flask-django-others }
Sie können WSGI-Anwendungen mounten, wie Sie es in [Unteranwendungen – Mounts](sub-applications.md){.internal-link target=_blank}, [Hinter einem Proxy](behind-a-proxy.md){.internal-link target=_blank} gesehen haben.
Dazu können Sie die `WSGIMiddleware` verwenden und damit Ihre WSGI-Anwendung wrappen, zum Beispiel Flask, Django usw.
Jetzt wird jeder <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> unter dem Pfad `/v1/` von der Flask-Anwendung verarbeitet.
Und der Rest wird von **FastAPI** gehandhabt.
Wenn Sie das ausführen und auf <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a> gehen, sehen Sie die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> von Flask:
```txt
Hello, World from Flask!
```
Und wenn Sie auf <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> gehen, sehen Sie die Response von FastAPI:
# Alternativen, Inspiration und Vergleiche { #alternatives-inspiration-and-comparisons }
Was hat **FastAPI** inspiriert, wie es sich im Vergleich zu Alternativen verhält und was es von ihnen gelernt hat.
## Einführung { #intro }
**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).
Es ist das beliebteste Python-Framework und genießt großes Vertrauen. Es wird zum Aufbau von Systemen wie Instagram verwendet.
Es ist relativ eng mit relationalen Datenbanken (wie MySQL oder PostgreSQL) gekoppelt, daher ist es nicht sehr einfach, eine NoSQL-Datenbank (wie Couchbase, MongoDB, Cassandra, usw.) als Hauptspeicherengine zu verwenden.
Es wurde erstellt, um den HTML-Code im Backend zu generieren, nicht um APIs zu erstellen, die von einem modernen Frontend (wie React, Vue.js und Angular) oder von anderen Systemen (wie <abbr title="Internet of Things – Internet der Dinge">IoT</abbr>-Geräten) verwendet werden, um mit ihm zu kommunizieren.
Das Django REST Framework wurde als flexibles Toolkit zum Erstellen von Web-APIs unter Verwendung von Django entwickelt, um dessen API-Möglichkeiten zu verbessern.
Es wird von vielen Unternehmen verwendet, darunter Mozilla, Red Hat und Eventbrite.
Es war eines der ersten Beispiele für **automatische API-Dokumentation**, und dies war insbesondere eine der ersten Ideen, welche „die Suche nach“ **FastAPI** inspirierten.
/// note | Hinweis
Das Django REST Framework wurde von Tom Christie erstellt. Derselbe Schöpfer von Starlette und Uvicorn, auf denen **FastAPI** basiert.
///
/// check | Inspirierte **FastAPI**
Eine automatische API-Dokumentationsoberfläche zu haben.
Flask ist ein „Mikroframework“, es enthält weder Datenbankintegration noch viele der Dinge, die standardmäßig in Django enthalten sind.
Diese Einfachheit und Flexibilität ermöglichen beispielsweise die Verwendung von NoSQL-Datenbanken als Hauptdatenspeichersystem.
Da es sehr einfach ist, ist es relativ intuitiv zu erlernen, obwohl die Dokumentation an einigen Stellen etwas technisch wird.
Es wird auch häufig für andere Anwendungen verwendet, die nicht unbedingt eine Datenbank, Benutzerverwaltung oder eine der vielen in Django enthaltenen Funktionen benötigen. Obwohl viele dieser Funktionen mit Plugins hinzugefügt werden können.
Diese Entkopplung der Teile und die Tatsache, dass es sich um ein „Mikroframework“ handelt, welches so erweitert werden kann, dass es genau das abdeckt, was benötigt wird, war ein Schlüsselmerkmal, das ich beibehalten wollte.
Angesichts der Einfachheit von Flask schien es eine gute Ergänzung zum Erstellen von APIs zu sein. Als Nächstes musste ein „Django REST Framework“ für Flask gefunden werden.
/// check | Inspirierte **FastAPI**
Ein Mikroframework zu sein. Es einfach zu machen, die benötigten Tools und Teile zu kombinieren.
Über ein einfaches und benutzerfreundliches Routingsystem zu verfügen.
**FastAPI** ist eigentlich keine Alternative zu **Requests**. Der Umfang der beiden ist sehr unterschiedlich.
Es wäre tatsächlich üblich, Requests *innerhalb* einer FastAPI-Anwendung zu verwenden.
Dennoch erhielt FastAPI von Requests einiges an Inspiration.
**Requests** ist eine Bibliothek zur *Interaktion* mit APIs (als Client), während **FastAPI** eine Bibliothek zum *Erstellen* von APIs (als Server) ist.
Die beiden stehen mehr oder weniger an entgegengesetzten Enden und ergänzen sich.
Requests hat ein sehr einfaches und intuitives Design, ist sehr einfach zu bedienen und verfügt über sinnvolle Standardeinstellungen. Aber gleichzeitig ist es sehr leistungsstark und anpassbar.
Aus diesem Grund heißt es auf der offiziellen Website:
> Requests ist eines der am häufigsten heruntergeladenen Python-Packages aller Zeiten
Die Art und Weise, wie Sie es verwenden, ist sehr einfach. Um beispielsweise einen `GET`-<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> zu machen, würden Sie schreiben:
Die Hauptfunktion, die ich vom Django REST Framework haben wollte, war die automatische API-Dokumentation.
Dann fand ich heraus, dass es einen Standard namens Swagger gab, zur Dokumentation von APIs unter Verwendung von JSON (oder YAML, einer Erweiterung von JSON).
Und es gab bereits eine Web-Oberfläche für Swagger-APIs. Die Möglichkeit, Swagger-Dokumentation für eine API zu generieren, würde die automatische Nutzung dieser Web-Oberfläche ermöglichen.
Irgendwann wurde Swagger an die Linux Foundation übergeben und in OpenAPI umbenannt.
Aus diesem Grund spricht man bei Version 2.0 häufig von „Swagger“ und ab Version 3 von „OpenAPI“.
/// check | Inspirierte **FastAPI**
Einen offenen Standard für API-Spezifikationen zu übernehmen und zu verwenden, anstelle eines benutzerdefinierten Schemas.
Und Standard-basierte Tools für die Oberfläche zu integrieren:
Diese beiden wurden ausgewählt, weil sie ziemlich beliebt und stabil sind, aber bei einer schnellen Suche könnten Sie Dutzende alternativer Benutzeroberflächen für OpenAPI finden (welche Sie mit **FastAPI** verwenden können).
Es gibt mehrere Flask REST Frameworks, aber nachdem ich die Zeit und Arbeit investiert habe, sie zu untersuchen, habe ich festgestellt, dass viele nicht mehr unterstützt werden oder abgebrochen wurden und dass mehrere fortbestehende Probleme sie unpassend machten.
Eine der von API-Systemen benötigten Hauptfunktionen ist die Daten-<abbr title="Auch „Marshalling“, „Konvertierung“ genannt">„Serialisierung“</abbr>, welche Daten aus dem Code (Python) entnimmt und in etwas umwandelt, was durch das Netzwerk gesendet werden kann. Beispielsweise das Konvertieren eines Objekts, welches Daten aus einer Datenbank enthält, in ein JSON-Objekt. Konvertieren von `datetime`-Objekten in Strings, usw.
Eine weitere wichtige Funktion, benötigt von APIs, ist die Datenvalidierung, welche sicherstellt, dass die Daten unter gegebenen Umständen gültig sind. Zum Beispiel, dass ein Feld ein `int` ist und kein zufälliger String. Das ist besonders nützlich für hereinkommende Daten.
Ohne ein Datenvalidierungssystem müssten Sie alle Prüfungen manuell im Code durchführen.
Für diese Funktionen wurde Marshmallow entwickelt. Es ist eine großartige Bibliothek und ich habe sie schon oft genutzt.
Aber sie wurde erstellt, bevor Typhinweise in Python existierten. Um also ein <abbr title="die Definition, wie Daten geformt sein sollen">Schema</abbr> zu definieren, müssen Sie bestimmte Werkzeuge und Klassen verwenden, die von Marshmallow bereitgestellt werden.
/// check | Inspirierte **FastAPI**
Code zu verwenden, um „Schemas“ zu definieren, welche Datentypen und Validierung automatisch bereitstellen.
Eine weitere wichtige Funktion, die von APIs benötigt wird, ist das <abbr title="Lesen und Konvertieren nach Python-Daten">Parsen</abbr> von Daten aus eingehenden Requests.
Webargs wurde entwickelt, um dieses für mehrere Frameworks, einschließlich Flask, bereitzustellen.
Es verwendet unter der Haube Marshmallow, um die Datenvalidierung durchzuführen. Und es wurde von denselben Entwicklern erstellt.
Es ist ein großartiges Tool und ich habe es auch oft verwendet, bevor ich **FastAPI** hatte.
/// info | Info
Webargs wurde von denselben Marshmallow-Entwicklern erstellt.
///
/// check | Inspirierte **FastAPI**
Eingehende Requestdaten automatisch zu validieren.
Marshmallow und Webargs bieten Validierung, Parsen und Serialisierung als Plugins.
Es fehlt jedoch noch die Dokumentation. Dann wurde APISpec erstellt.
Es ist ein Plugin für viele Frameworks (und es gibt auch ein Plugin für Starlette).
Die Funktionsweise besteht darin, dass Sie die Definition des Schemas im YAML-Format im Docstring jeder Funktion schreiben, die eine Route verarbeitet.
Und es generiert OpenAPI-Schemas.
So funktioniert es in Flask, Starlette, Responder, usw.
Aber dann haben wir wieder das Problem einer Mikrosyntax innerhalb eines Python-Strings (eines großen YAML).
Der Texteditor kann dabei nicht viel helfen. Und wenn wir Parameter oder Marshmallow-Schemas ändern und vergessen, auch den YAML-Docstring zu ändern, wäre das generierte Schema veraltet.
/// info | Info
APISpec wurde von denselben Marshmallow-Entwicklern erstellt.
///
/// check | Inspirierte **FastAPI**
Den offenen Standard für APIs, OpenAPI, zu unterstützen.
Hierbei handelt es sich um ein Flask-Plugin, welches Webargs, Marshmallow und APISpec miteinander verbindet.
Es nutzt die Informationen von Webargs und Marshmallow, um mithilfe von APISpec automatisch OpenAPI-Schemas zu generieren.
Ein großartiges Tool, sehr unterbewertet. Es sollte weitaus populärer als viele andere Flask-Plugins sein. Möglicherweise liegt es daran, dass die Dokumentation zu kompakt und abstrakt ist.
Das löste das Problem, YAML (eine andere Syntax) in Python-Docstrings schreiben zu müssen.
Diese Kombination aus Flask, Flask-apispec mit Marshmallow und Webargs war bis zur Entwicklung von **FastAPI** mein Lieblings-Backend-Stack.
Die Verwendung führte zur Entwicklung mehrerer Flask-Full-Stack-Generatoren. Dies sind die Hauptstacks, die ich (und mehrere externe Teams) bisher verwendet haben:
Dies ist nicht einmal Python, NestJS ist ein von Angular inspiriertes JavaScript (TypeScript) NodeJS Framework.
Es erreicht etwas Ähnliches wie Flask-apispec.
Es verfügt über ein integriertes Dependency Injection System, welches von Angular 2 inspiriert ist. Erfordert ein Vorab-Registrieren der „Injectables“ (wie alle anderen Dependency Injection Systeme, welche ich kenne), sodass der Code ausschweifender wird und es mehr Codeverdoppelung gibt.
Da die Parameter mit TypeScript-Typen beschrieben werden (ähnlich den Python-Typhinweisen), ist die Editorunterstützung ziemlich gut.
Da TypeScript-Daten jedoch nach der Kompilierung nach JavaScript nicht erhalten bleiben, können die Typen nicht gleichzeitig die Validierung, Serialisierung und Dokumentation definieren. Aus diesem Grund und aufgrund einiger Designentscheidungen ist es für die Validierung, Serialisierung und automatische Schemagenerierung erforderlich, an vielen Stellen Dekoratoren hinzuzufügen. Es wird also ziemlich ausführlich.
Es kann nicht sehr gut mit verschachtelten Modellen umgehen. Wenn es sich beim JSON-Body im Request also um ein JSON-Objekt mit inneren Feldern handelt, die wiederum verschachtelte JSON-Objekte sind, kann er nicht richtig dokumentiert und validiert werden.
/// check | Inspirierte **FastAPI**
Python-Typen zu verwenden, um eine hervorragende Editorunterstützung zu erhalten.
Über ein leistungsstarkes Dependency Injection System zu verfügen. Eine Möglichkeit zu finden, Codeverdoppelung zu minimieren.
Es war eines der ersten extrem schnellen Python-Frameworks, welches auf `asyncio` basierte. Es wurde so gestaltet, dass es Flask sehr ähnlich ist.
/// note | Technische Details
Es verwendete <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a> anstelle der standardmäßigen Python-`asyncio`-Schleife. Das hat es so schnell gemacht.
Hat eindeutig Uvicorn und Starlette inspiriert, welche derzeit in offenen Benchmarks schneller als Sanic sind.
///
/// check | Inspirierte **FastAPI**
Einen Weg zu finden, eine hervorragende Performanz zu haben.
Aus diesem Grund basiert **FastAPI** auf Starlette, da dieses das schnellste verfügbare Framework ist (getestet in Benchmarks von Dritten).
Falcon ist ein weiteres leistungsstarkes Python-Framework. Es ist minimalistisch konzipiert und dient als Grundlage für andere Frameworks wie Hug.
Es ist so konzipiert, dass es über Funktionen verfügt, welche zwei Parameter empfangen, einen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">„Request“</abbr> und eine <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">„Response“</abbr>. Dann „lesen“ Sie Teile des Requests und „schreiben“ Teile der Response. Aufgrund dieses Designs ist es nicht möglich, Request-Parameter und -Bodys mit Standard-Python-Typhinweisen als Funktionsparameter zu deklarieren.
Daher müssen Datenvalidierung, Serialisierung und Dokumentation im Code und nicht automatisch erfolgen. Oder sie müssen als Framework oberhalb von Falcon implementiert werden, so wie Hug. Dieselbe Unterscheidung findet auch in anderen Frameworks statt, die vom Design von Falcon inspiriert sind und ein Requestobjekt und ein Responseobjekt als Parameter haben.
/// check | Inspirierte **FastAPI**
Wege zu finden, eine großartige Performanz zu erzielen.
Zusammen mit Hug (da Hug auf Falcon basiert), einen `response`-Parameter in Funktionen zu deklarieren.
Obwohl er in FastAPI optional ist und hauptsächlich zum Festlegen von Headern, Cookies und alternativen Statuscodes verwendet wird.
Ich habe Molten in den ersten Phasen der Entwicklung von **FastAPI** entdeckt. Und es hat ganz ähnliche Ideen:
* Basierend auf Python-Typhinweisen.
* Validierung und Dokumentation aus diesen Typen.
* Dependency Injection System.
Es verwendet keine Datenvalidierungs-, Serialisierungs- und Dokumentationsbibliothek eines Dritten wie Pydantic, sondern verfügt über eine eigene. Daher wären diese Datentyp-Definitionen nicht so einfach wiederverwendbar.
Es erfordert eine etwas ausführlichere Konfiguration. Und da es auf WSGI (anstelle von ASGI) basiert, ist es nicht darauf ausgelegt, die hohe Leistung von Tools wie Uvicorn, Starlette und Sanic zu nutzen.
Das Dependency Injection System erfordert eine Vorab-Registrierung der Abhängigkeiten und die Abhängigkeiten werden basierend auf den deklarierten Typen aufgelöst. Daher ist es nicht möglich, mehr als eine „Komponente“ zu deklarieren, welche einen bestimmten Typ bereitstellt.
Routen werden an einer einzigen Stelle deklariert, indem Funktionen verwendet werden, die an anderen Stellen deklariert wurden (anstatt Dekoratoren zu verwenden, welche direkt über der Funktion platziert werden können, welche den Endpunkt verarbeitet). Dies ähnelt eher der Vorgehensweise von Django als der Vorgehensweise von Flask (und Starlette). Es trennt im Code Dinge, die relativ eng miteinander gekoppelt sind.
/// check | Inspirierte **FastAPI**
Zusätzliche Validierungen für Datentypen zu definieren, mithilfe des „Default“-Werts von Modellattributen. Dies verbessert die Editorunterstützung und war zuvor in Pydantic nicht verfügbar.
Das hat tatsächlich dazu geführt, dass Teile von Pydantic aktualisiert wurden, um denselben Validierungsdeklarationsstil zu unterstützen (diese gesamte Funktionalität ist jetzt bereits in Pydantic verfügbar).
Hug war eines der ersten Frameworks, welches die Deklaration von API-Parametertypen mithilfe von Python-Typhinweisen implementierte. Das war eine großartige Idee, die andere Tools dazu inspirierte, dasselbe zu tun.
Es verwendete benutzerdefinierte Typen in seinen Deklarationen anstelle von Standard-Python-Typen, es war aber dennoch ein großer Fortschritt.
Außerdem war es eines der ersten Frameworks, welches ein benutzerdefiniertes Schema generierte, welches die gesamte API in JSON deklarierte.
Es basierte nicht auf einem Standard wie OpenAPI und JSON Schema. Daher wäre es nicht einfach, es in andere Tools wie Swagger UI zu integrieren. Aber, nochmal, es war eine sehr innovative Idee.
Es verfügt über eine interessante, ungewöhnliche Funktion: Mit demselben Framework ist es möglich, APIs und auch CLIs zu erstellen.
Da es auf dem bisherigen Standard für synchrone Python-Webframeworks (WSGI) basiert, kann es nicht mit Websockets und anderen Dingen umgehen, verfügt aber dennoch über eine hohe Performanz.
/// info | Info
Hug wurde von Timothy Crosley erstellt, demselben Schöpfer von <a href="https://github.com/timothycrosley/isort" class="external-link" target="_blank">`isort`</a>, einem großartigen Tool zum automatischen Sortieren von Importen in Python-Dateien.
///
/// check | Ideen, die **FastAPI** inspiriert haben
Hug inspirierte Teile von APIStar und war eines der Tools, die ich am vielversprechendsten fand, neben APIStar.
Hug hat dazu beigetragen, **FastAPI** dazu zu inspirieren, Python-Typhinweise zum Deklarieren von Parametern zu verwenden und ein Schema zu generieren, das die API automatisch definiert.
Hug inspirierte **FastAPI** dazu, einen `response`-Parameter in Funktionen zu deklarieren, um Header und Cookies zu setzen.
Kurz bevor ich mich entschied, **FastAPI** zu erstellen, fand ich den **APIStar**-Server. Er hatte fast alles, was ich suchte, und ein tolles Design.
Er war eine der ersten Implementierungen eines Frameworks, die ich je gesehen hatte (vor NestJS und Molten), welches Python-Typhinweise zur Deklaration von Parametern und Requests verwendeten. Ich habe ihn mehr oder weniger zeitgleich mit Hug gefunden. Aber APIStar nutzte den OpenAPI-Standard.
Er verfügte an mehreren Stellen über automatische Datenvalidierung, Datenserialisierung und OpenAPI-Schemagenerierung, basierend auf denselben Typhinweisen.
Body-Schemadefinitionen verwendeten nicht die gleichen Python-Typhinweise wie Pydantic, er war Marshmallow etwas ähnlicher, sodass die Editorunterstützung nicht so gut war, aber dennoch war APIStar die beste verfügbare Option.
Er hatte zu dieser Zeit die besten Leistungsbenchmarks (nur übertroffen von Starlette).
Anfangs gab es keine Web-Oberfläche für die automatische API-Dokumentation, aber ich wusste, dass ich Swagger UI hinzufügen konnte.
Er verfügte über ein Dependency Injection System. Es erforderte eine Vorab-Registrierung der Komponenten, wie auch bei anderen oben besprochenen Tools. Aber dennoch, es war ein tolles Feature.
Ich konnte ihn nie in einem vollständigen Projekt verwenden, da er keine Sicherheitsintegration hatte, sodass ich nicht alle Funktionen, die ich hatte, durch die auf Flask-apispec basierenden Full-Stack-Generatoren ersetzen konnte. Ich hatte in meinem Projekte-Backlog den Eintrag, einen Pull Request zu erstellen, welcher diese Funktionalität hinzufügte.
Doch dann verlagerte sich der Schwerpunkt des Projekts.
Es handelte sich nicht länger um ein API-Webframework, da sich der Entwickler auf Starlette konzentrieren musste.
Jetzt handelt es sich bei APIStar um eine Reihe von Tools zur Validierung von OpenAPI-Spezifikationen, nicht um ein Webframework.
/// info | Info
APIStar wurde von Tom Christie erstellt. Derselbe, welcher Folgendes erstellt hat:
* Django REST Framework
* Starlette (auf welchem **FastAPI** basiert)
* Uvicorn (verwendet von Starlette und **FastAPI**)
///
/// check | Inspirierte **FastAPI**
Zu existieren.
Die Idee, mehrere Dinge (Datenvalidierung, Serialisierung und Dokumentation) mit denselben Python-Typen zu deklarieren, welche gleichzeitig eine hervorragende Editorunterstützung bieten, hielt ich für eine brillante Idee.
Und nach einer langen Suche nach einem ähnlichen Framework und dem Testen vieler verschiedener Alternativen, war APIStar die beste verfügbare Option.
Dann hörte APIStar auf, als Server zu existieren, und Starlette wurde geschaffen, welches eine neue, bessere Grundlage für ein solches System bildete. Das war die finale Inspiration für die Entwicklung von **FastAPI**.
Ich betrachte **FastAPI** als einen „spirituellen Nachfolger“ von APIStar, welcher die Funktionen, das Typsystem und andere Teile verbessert und erweitert, basierend auf den Erkenntnissen aus all diesen früheren Tools.
Pydantic ist eine Bibliothek zum Definieren von Datenvalidierung, Serialisierung und Dokumentation (unter Verwendung von JSON Schema) basierend auf Python-Typhinweisen.
Das macht es äußerst intuitiv.
Es ist vergleichbar mit Marshmallow. Obwohl es in Benchmarks schneller als Marshmallow ist. Und da es auf den gleichen Python-Typhinweisen basiert, ist die Editorunterstützung großartig.
/// check | **FastAPI** verwendet es, um
Die gesamte Datenvalidierung, Datenserialisierung und automatische Modelldokumentation (basierend auf JSON Schema) zu erledigen.
**FastAPI** nimmt dann, abgesehen von all den anderen Dingen, die es tut, dieses JSON-Schema und fügt es in OpenAPI ein.
Starlette ist ein leichtgewichtiges <abbr title="Der neue Standard für die Erstellung asynchroner Python-Webanwendungen">ASGI</abbr>-Framework/Toolkit, welches sich ideal für die Erstellung hochperformanter asynchroner Dienste eignet.
Es ist sehr einfach und intuitiv. Es ist so konzipiert, dass es leicht erweiterbar ist und über modulare Komponenten verfügt.
Starlette ist derzeit das schnellste getestete Python-Framework. Nur übertroffen von Uvicorn, welches kein Framework, sondern ein Server ist.
Starlette bietet alle grundlegenden Funktionen eines Web-Microframeworks.
Es bietet jedoch keine automatische Datenvalidierung, Serialisierung oder Dokumentation.
Das ist eines der wichtigsten Dinge, welche **FastAPI** hinzufügt, alles basierend auf Python-Typhinweisen (mit Pydantic). Das, plus, das Dependency Injection System, Sicherheitswerkzeuge, OpenAPI-Schemagenerierung, usw.
/// note | Technische Details
ASGI ist ein neuer „Standard“, welcher von Mitgliedern des Django-Kernteams entwickelt wird. Es handelt sich immer noch nicht um einen „Python-Standard“ (ein PEP), obwohl sie gerade dabei sind, das zu tun.
Dennoch wird es bereits von mehreren Tools als „Standard“ verwendet. Das verbessert die Interoperabilität erheblich, da Sie Uvicorn mit jeden anderen ASGI-Server (wie Daphne oder Hypercorn) tauschen oder ASGI-kompatible Tools wie `python-socketio` hinzufügen können.
///
/// check | **FastAPI** verwendet es, um
Alle Kern-Webaspekte zu handhaben. Und fügt Funktionen obenauf.
Die Klasse `FastAPI` selbst erbt direkt von der Klasse `Starlette`.
Alles, was Sie also mit Starlette machen können, können Sie direkt mit **FastAPI** machen, da es sich im Grunde um Starlette auf Steroiden handelt.
Uvicorn ist ein blitzschneller ASGI-Server, der auf uvloop und httptools basiert.
Es handelt sich nicht um ein Webframework, sondern um einen Server. Beispielsweise werden keine Tools für das Routing von Pfaden bereitgestellt. Das ist etwas, was ein Framework wie Starlette (oder **FastAPI**) zusätzlich bieten würde.
Es ist der empfohlene Server für Starlette und **FastAPI**.
/// check | **FastAPI** empfiehlt es als
Hauptwebserver zum Ausführen von **FastAPI**-Anwendungen.
Sie können auch die Kommandozeilenoption `--workers` verwenden, um einen asynchronen Multiprozess-Server zu erhalten.
Weitere Details finden Sie im Abschnitt [Deployment](deployment/index.md){.internal-link target=_blank}.
///
## Benchmarks und Geschwindigkeit { #benchmarks-and-speed }
Um den Unterschied zwischen Uvicorn, Starlette und FastAPI zu verstehen, zu vergleichen und zu sehen, lesen Sie den Abschnitt über [Benchmarks](benchmarks.md){.internal-link target=_blank}.
# Nebenläufigkeit und async / await { #concurrency-and-async-await }
Details zur `async def`-Syntax für *Pfadoperation-Funktionen* und Hintergrundinformationen zu asynchronem Code, Nebenläufigkeit und Parallelität.
## In Eile? { #in-a-hurry }
<abbr title="too long; didn't read – Zu lang; nicht gelesen"><strong>TL;DR:</strong></abbr>
Wenn Sie Bibliotheken von Dritten verwenden, die mit `await` aufgerufen werden müssen, wie zum Beispiel:
```Python
results=awaitsome_library()
```
Dann deklarieren Sie Ihre *Pfadoperation-Funktionen* mit `async def`, wie in:
```Python hl_lines="2"
@app.get('/')
async def read_results():
results = await some_library()
return results
```
/// note | Hinweis
Sie können `await` nur innerhalb von Funktionen verwenden, die mit `async def` erstellt wurden.
///
---
Wenn Sie eine Bibliothek eines Dritten verwenden, die mit etwas kommuniziert (einer Datenbank, einer API, dem Dateisystem, usw.) und welche die Verwendung von `await` nicht unterstützt (dies ist derzeit bei den meisten Datenbankbibliotheken der Fall), dann deklarieren Sie Ihre *Pfadoperation-Funktionen* ganz normal nur mit `def`, wie in:
```Python hl_lines="2"
@app.get('/')
def results():
results = some_library()
return results
```
---
Wenn Ihre Anwendung (irgendwie) nicht mit etwas anderem kommunizieren und auf dessen Antwort warten muss, verwenden Sie `async def`, auch wenn Sie `await` im Inneren nicht verwenden müssen.
---
Wenn Sie sich unsicher sind, verwenden Sie einfach `def`.
---
**Hinweis**: Sie können `def` und `async def` in Ihren *Pfadoperation-Funktionen* beliebig mischen, so wie Sie es benötigen, und jede einzelne Funktion in der für Sie besten Variante erstellen. FastAPI wird damit das Richtige tun.
Wie dem auch sei, in jedem der oben genannten Fälle wird FastAPI immer noch asynchron arbeiten und extrem schnell sein.
Wenn Sie jedoch den oben genannten Schritten folgen, können einige Performanz-Optimierungen vorgenommen werden.
## Technische Details { #technical-details }
Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async` und `await`**.
Nehmen wir obigen Satz in den folgenden Abschnitten Schritt für Schritt unter die Lupe:
* **Asynchroner Code**
* **`async` und `await`**
* **Coroutinen**
## Asynchroner Code { #asynchronous-code }
Asynchroner Code bedeutet lediglich, dass die Sprache 💬 eine Möglichkeit hat, dem Computer / Programm 🤖 mitzuteilen, dass es 🤖 an einem bestimmten Punkt im Code darauf warten muss, dass *etwas anderes* irgendwo anders fertig wird. Nehmen wir an, *etwas anderes* ist hier „Langsam-Datei“ 📝.
Während der Zeit, die „Langsam-Datei“ 📝 benötigt, kann das System also andere Aufgaben erledigen.
Dann kommt der Computer / das Programm 🤖 bei jeder Gelegenheit zurück, weil es entweder wieder wartet oder wann immer es 🤖 die ganze Arbeit erledigt hat, die zu diesem Zeitpunkt zu tun war. Und es 🤖 wird nachschauen, ob eine der Aufgaben, auf die es gewartet hat, fertig ist.
Dann nimmt es 🤖 die erste erledigte Aufgabe (sagen wir, unsere „Langsam-Datei“ 📝) und bearbeitet sie weiter.
Das „Warten auf etwas anderes“ bezieht sich normalerweise auf <abbr title="Input and Output – Eingabe und Ausgabe">I/O</abbr>-Operationen, die relativ „langsam“ sind (im Vergleich zur Geschwindigkeit des Prozessors und des Arbeitsspeichers), wie etwa das Warten darauf, dass:
* die Daten des Clients über das Netzwerk empfangen wurden
* die von Ihrem Programm gesendeten Daten vom Client über das Netzwerk empfangen wurden
* der Inhalt einer Datei vom System von der Festplatte gelesen und an Ihr Programm übergeben wurde
* der Inhalt, den Ihr Programm dem System übergeben hat, auf die Festplatte geschrieben wurde
* eine Remote-API-Operation beendet wurde
* Eine Datenbankoperation abgeschlossen wurde
* eine Datenbankabfrage die Ergebnisse zurückgegeben hat
* usw.
Da die Ausführungszeit hier hauptsächlich durch das Warten auf <abbr title="Input and Output – Eingabe und Ausgabe">I/O</abbr>-Operationen verbraucht wird, nennt man dies auch „I/O-lastige“ („I/O bound“) Operationen.
„Asynchron“, sagt man, weil der Computer / das Programm nicht mit einer langsamen Aufgabe „synchronisiert“ werden muss und nicht auf den genauen Moment warten muss, in dem die Aufgabe beendet ist, ohne dabei etwas zu tun, um schließlich das Ergebnis der Aufgabe zu übernehmen und die Arbeit fortsetzen zu können.
Da es sich stattdessen um ein „asynchrones“ System handelt, kann die Aufgabe nach Abschluss ein wenig (einige Mikrosekunden) in der Schlange warten, bis der Computer / das Programm seine anderen Dinge erledigt hat und zurückkommt, um die Ergebnisse entgegenzunehmen und mit ihnen weiterzuarbeiten.
Für „synchron“ (im Gegensatz zu „asynchron“) wird auch oft der Begriff „sequentiell“ verwendet, da der Computer / das Programm alle Schritte in einer Sequenz („der Reihe nach“) ausführt, bevor es zu einer anderen Aufgabe wechselt, auch wenn diese Schritte mit Warten verbunden sind.
### Nebenläufigkeit und Hamburger { #concurrency-and-burgers }
Diese oben beschriebene Idee von **asynchronem** Code wird manchmal auch **„Nebenläufigkeit“** genannt. Sie unterscheidet sich von **„Parallelität“**.
**Nebenläufigkeit** und **Parallelität** beziehen sich beide auf „verschiedene Dinge, die mehr oder weniger gleichzeitig passieren“.
Aber die Details zwischen *Nebenläufigkeit* und *Parallelität* sind ziemlich unterschiedlich.
Um den Unterschied zu erkennen, stellen Sie sich die folgende Geschichte über Hamburger vor:
Der Kassierer sagt etwas zum Koch in der Küche, damit dieser weiß, dass er Ihre Burger zubereiten muss (obwohl er gerade die für die vorherigen Kunden zubereitet).
Während Sie warten, suchen Sie sich mit Ihrem Schwarm einen Tisch aus, Sie sitzen da und reden lange mit Ihrem Schwarm (da Ihre Burger sehr aufwändig sind und die Zubereitung einige Zeit dauert).
Während Sie mit Ihrem Schwarm am Tisch sitzen und auf die Burger warten, können Sie die Zeit damit verbringen, zu bewundern, wie großartig, süß und klug Ihr Schwarm ist ✨😍✨.
Während Sie warten und mit Ihrem Schwarm sprechen, überprüfen Sie von Zeit zu Zeit die auf dem Zähler angezeigte Nummer, um zu sehen, ob Sie bereits an der Reihe sind.
Dann, irgendwann, sind Sie endlich an der Reihe. Sie gehen zur Theke, holen sich die Burger und kommen zurück an den Tisch.
Die wunderschönen Illustrationen stammen von <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
Stellen Sie sich vor, Sie wären der Computer / das Programm 🤖 in dieser Geschichte.
Während Sie an der Schlange stehen, sind Sie einfach untätig 😴, warten darauf, dass Sie an die Reihe kommen, und tun nichts sehr „Produktives“. Aber die Schlange ist schnell abgearbeitet, weil der Kassierer nur die Bestellungen entgegennimmt (und nicht zubereitet), also ist das vertretbar.
Wenn Sie dann an der Reihe sind, erledigen Sie tatsächliche „produktive“ Arbeit, Sie gehen das Menü durch, entscheiden sich, was Sie möchten, bekunden Ihre und die Wahl Ihres Schwarms, bezahlen, prüfen, ob Sie die richtige Menge Geld oder die richtige Karte geben, prüfen, ob die Rechnung korrekt ist, prüfen, dass die Bestellung die richtigen Artikel enthält, usw.
Aber dann, auch wenn Sie Ihre Burger noch nicht haben, ist Ihre Interaktion mit dem Kassierer erst mal „auf Pause“ ⏸, weil Sie warten müssen 🕙, bis Ihre Burger fertig sind.
Aber wenn Sie sich von der Theke entfernt haben und mit der Nummer für die Bestellung an einem Tisch sitzen, können Sie Ihre Aufmerksamkeit auf Ihren Schwarm lenken und an dieser Aufgabe „arbeiten“ ⏯ 🤓. Sie machen wieder etwas sehr „Produktives“ und flirten mit Ihrem Schwarm 😍.
Dann sagt der Kassierer 💁 „Ich bin mit dem Burger fertig“, indem er Ihre Nummer auf dem Display über der Theke anzeigt, aber Sie springen nicht sofort wie verrückt auf, wenn das Display auf Ihre Nummer springt. Sie wissen, dass niemand Ihnen Ihre Burger wegnimmt, denn Sie haben die Nummer Ihrer Bestellung, und andere Leute haben andere Nummern.
Also warten Sie darauf, dass Ihr Schwarm ihre Geschichte zu Ende erzählt (die aktuelle Arbeit ⏯ / bearbeitete Aufgabe beendet 🤓), lächeln sanft und sagen, dass Sie die Burger holen ⏸.
Dann gehen Sie zur Theke 🔀, zur ursprünglichen Aufgabe, die nun erledigt ist ⏯, nehmen die Burger auf, sagen Danke, und bringen sie zum Tisch. Damit ist dieser Schritt / diese Aufgabe der Interaktion mit der Theke abgeschlossen ⏹. Das wiederum schafft eine neue Aufgabe, „Burger essen“ 🔀 ⏯, aber die vorherige Aufgabe „Burger holen“ ist erledigt ⏹.
### Parallele Hamburger { #parallel-burgers }
Stellen wir uns jetzt vor, dass es sich hierbei nicht um „nebenläufige Hamburger“, sondern um „parallele Hamburger“ handelt.
Sie gehen los mit Ihrem Schwarm, um paralleles Fast Food zu bekommen.
Sie stehen in der Schlange, während mehrere (sagen wir acht) Kassierer, die gleichzeitig Köche sind, die Bestellungen der Leute vor Ihnen entgegennehmen.
Alle vor Ihnen warten darauf, dass ihre Burger fertig sind, bevor sie die Theke verlassen, denn jeder der 8 Kassierer geht los und bereitet den Burger sofort zu, bevor er die nächste Bestellung entgegennimmt.
Da Sie und Ihr Schwarm damit beschäftigt sind, niemanden vor sich zu lassen, der Ihre Burger nimmt, wenn sie ankommen, können Sie Ihrem Schwarm keine Aufmerksamkeit schenken. 😞
Das ist „synchrone“ Arbeit, Sie sind mit dem Kassierer/Koch „synchronisiert“ 👨🍳. Sie müssen warten 🕙 und genau in dem Moment da sein, in dem der Kassierer/Koch 👨🍳 die Burger zubereitet hat und Ihnen gibt, sonst könnte jemand anderes sie nehmen.
Es wurde nicht viel geredet oder geflirtet, da die meiste Zeit mit Warten 🕙 vor der Theke verbracht wurde. 😞
/// info | Info
Die wunderschönen Illustrationen stammen von <a href="https://www.instagram.com/ketrinadrawsalot" class="external-link" target="_blank">Ketrina Thompson</a>. 🎨
///
---
In diesem Szenario der parallelen Hamburger sind Sie ein Computersystem / Programm 🤖 mit zwei Prozessoren (Sie und Ihr Schwarm), die beide warten 🕙 und ihre Aufmerksamkeit darauf verwenden, „lange Zeit vor der Theke zu warten“ 🕙.
Der Fast-Food-Laden verfügt über 8 Prozessoren (Kassierer/Köche). Während der nebenläufige Burger-Laden nur zwei hatte (einen Kassierer und einen Koch).
Dennoch ist das schlussendliche Benutzererlebnis nicht das Beste. 😞
---
Dies wäre die parallele äquivalente Geschichte für Hamburger. 🍔
Für ein „realeres“ Beispiel hierfür, stellen Sie sich eine Bank vor.
Bis vor kurzem hatten die meisten Banken mehrere Kassierer 👨💼👨💼👨💼👨💼 und eine große Warteschlange 🕙🕙🕙🕙🕙🕙🕙🕙.
Alle Kassierer erledigen die ganze Arbeit mit einem Kunden nach dem anderen 👨💼⏯.
Und man muss lange in der Schlange warten 🕙 sonst kommt man nicht an die Reihe.
Sie würden Ihren Schwarm 😍 wahrscheinlich nicht mitnehmen wollen, um Besorgungen bei der Bank zu erledigen 🏦.
In diesem Szenario „Fastfood-Burger mit Ihrem Schwarm“ ist es viel sinnvoller, ein nebenläufiges System zu haben ⏸🔀⏯, da viel gewartet wird 🕙.
Das ist auch bei den meisten Webanwendungen der Fall.
Viele, viele Benutzer, aber Ihr Server wartet 🕙 darauf, dass deren nicht so gute Internetverbindungen die <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> übermitteln.
Und dann wieder warten 🕙, bis die <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Responses</abbr> zurückkommen.
Dieses „Warten“ 🕙 wird in Mikrosekunden gemessen, aber zusammenfassend lässt sich sagen, dass am Ende eine Menge gewartet wird.
Deshalb ist es sehr sinnvoll, asynchronen ⏸🔀⏯ Code für Web-APIs zu verwenden.
Diese Art der Asynchronität hat NodeJS populär gemacht (auch wenn NodeJS nicht parallel ist) und darin liegt die Stärke von Go als Programmiersprache.
Und das ist das gleiche Leistungsniveau, das Sie mit **FastAPI** erhalten.
Und da Sie Parallelität und Asynchronität gleichzeitig haben können, erzielen Sie eine höhere Performanz als die meisten getesteten NodeJS-Frameworks und sind mit Go auf Augenhöhe, einer kompilierten Sprache, die näher an C liegt <a href="https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=query&l=zijmkf-1" class="external-link" target="_blank">(alles dank Starlette)</a>.
### Ist Nebenläufigkeit besser als Parallelität? { #is-concurrency-better-than-parallelism }
Nein! Das ist nicht die Moral der Geschichte.
Nebenläufigkeit unterscheidet sich von Parallelität. Und sie ist besser bei **bestimmten** Szenarien, die viel Warten erfordern. Aus diesem Grund ist sie im Allgemeinen viel besser als Parallelität für die Entwicklung von Webanwendungen. Aber das stimmt nicht für alle Anwendungen.
Um die Dinge auszugleichen, stellen Sie sich die folgende Kurzgeschichte vor:
> Sie müssen ein großes, schmutziges Haus aufräumen.
*Yup, das ist die ganze Geschichte*.
---
Es gibt kein Warten 🕙, nur viel Arbeit an mehreren Stellen im Haus.
Sie könnten wie im Hamburger-Beispiel hin- und herspringen, zuerst das Wohnzimmer, dann die Küche, aber da Sie auf nichts warten 🕙, sondern nur putzen und putzen, hätte das Hin- und Herspringen keine Auswirkungen.
Es würde mit oder ohne Hin- und Herspringen (Nebenläufigkeit) die gleiche Zeit in Anspruch nehmen, um fertig zu werden, und Sie hätten die gleiche Menge an Arbeit erledigt.
Aber wenn Sie in diesem Fall die acht Ex-Kassierer/Köche/jetzt Reinigungskräfte mitbringen würden und jeder von ihnen (plus Sie) würde einen Bereich des Hauses reinigen, könnten Sie die ganze Arbeit **parallel** erledigen, und würden mit dieser zusätzlichen Hilfe viel schneller fertig werden.
In diesem Szenario wäre jede einzelne Reinigungskraft (einschließlich Ihnen) ein Prozessor, der seinen Teil der Arbeit erledigt.
Und da die meiste Ausführungszeit durch tatsächliche Arbeit (anstatt durch Warten) in Anspruch genommen wird und die Arbeit in einem Computer von einer <abbr title="Central Processing Unit – Zentrale Recheneinheit">CPU</abbr> erledigt wird, werden diese Probleme als „CPU-lastig“ („CPU bound“) bezeichnet.
---
Typische Beispiele für CPU-lastige Vorgänge sind Dinge, die komplexe mathematische Berechnungen erfordern.
Zum Beispiel:
* **Audio-** oder **Bildbearbeitung**.
* **Computer Vision**: Ein Bild besteht aus Millionen von Pixeln, jedes Pixel hat 3 Werte / Farben, die Verarbeitung erfordert normalerweise, Berechnungen mit diesen Pixeln durchzuführen, alles zur gleichen Zeit.
* **Maschinelles Lernen**: Normalerweise sind viele „Matrix“- und „Vektor“-Multiplikationen erforderlich. Stellen Sie sich eine riesige Tabelle mit Zahlen vor, in der Sie alle Zahlen gleichzeitig multiplizieren.
* **Deep Learning**: Dies ist ein Teilgebiet des maschinellen Lernens, daher gilt das Gleiche. Es ist nur so, dass es nicht eine einzige Tabelle mit Zahlen zum Multiplizieren gibt, sondern eine riesige Menge davon, und in vielen Fällen verwendet man einen speziellen Prozessor, um diese Modelle zu erstellen und / oder zu verwenden.
Mit **FastAPI** können Sie die Vorteile der Nebenläufigkeit nutzen, die in der Webentwicklung weit verbreitet ist (derselbe Hauptvorteil von NodeJS).
Sie können aber auch die Vorteile von Parallelität und Multiprocessing (mehrere Prozesse werden parallel ausgeführt) für **CPU-lastige** Workloads wie in Systemen für maschinelles Lernen nutzen.
Dies und die einfache Tatsache, dass Python die Hauptsprache für **Data Science**, maschinelles Lernen und insbesondere Deep Learning ist, machen FastAPI zu einem sehr passenden Werkzeug für Web-APIs und Anwendungen für Data Science / maschinelles Lernen (neben vielen anderen).
Wie Sie diese Parallelität in der Produktion erreichen, erfahren Sie im Abschnitt über [Deployment](deployment/index.md){.internal-link target=_blank}.
## `async` und `await` { #async-and-await }
Moderne Versionen von Python verfügen über eine sehr intuitive Möglichkeit, asynchronen Code zu schreiben. Dadurch sieht es wie normaler „sequentieller“ Code aus und übernimmt im richtigen Moment das „Warten“ für Sie.
Wenn es einen Vorgang gibt, der erfordert, dass gewartet wird, bevor die Ergebnisse zurückgegeben werden, und der diese neue Python-Funktionalität unterstützt, können Sie ihn wie folgt schreiben:
```Python
burgers = await get_burgers(2)
```
Der Schlüssel hier ist das `await`. Es teilt Python mit, dass es warten ⏸ muss, bis `get_burgers(2)` seine Aufgabe erledigt hat 🕙, bevor die Ergebnisse in `burgers` gespeichert werden. Damit weiß Python, dass es in der Zwischenzeit etwas anderes tun kann 🔀 ⏯ (z. B. einen weiteren Request empfangen).
Damit `await` funktioniert, muss es sich in einer Funktion befinden, die diese Asynchronität unterstützt. Dazu deklarieren Sie sie einfach mit `async def`:
```Python hl_lines="1"
async def get_burgers(number: int):
# Mache hier etwas Asynchrones, um die Burger zu erstellen
return burgers
```
... statt mit `def`:
```Python hl_lines="2"
# Dies ist nicht asynchron
def get_sequential_burgers(number: int):
# Mache hier etwas Sequentielles, um die Burger zu erstellen
return burgers
```
Mit `async def` weiß Python, dass es innerhalb dieser Funktion auf `await`-Ausdrücke achten muss und dass es die Ausführung dieser Funktion „anhalten“ ⏸ und etwas anderes tun kann 🔀, bevor es zurückkommt.
Wenn Sie eine `async def`-Funktion aufrufen möchten, müssen Sie sie „erwarten“ („await“). Das folgende wird also nicht funktionieren:
```Python
# Das funktioniert nicht, weil get_burgers definiert wurde mit: async def
burgers = get_burgers(2)
```
---
Wenn Sie also eine Bibliothek verwenden, die Ihnen sagt, dass Sie sie mit `await` aufrufen können, müssen Sie die *Pfadoperation-Funktionen*, die diese Bibliothek verwenden, mittels `async def` erstellen, wie in:
```Python hl_lines="2-3"
@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers
```
### Weitere technische Details { #more-technical-details }
Ihnen ist wahrscheinlich aufgefallen, dass `await` nur innerhalb von Funktionen verwendet werden kann, die mit `async def` definiert sind.
Gleichzeitig müssen aber mit `async def` definierte Funktionen „erwartet“ („awaited“) werden. Daher können Funktionen mit `async def` nur innerhalb von Funktionen aufgerufen werden, die auch mit `async def` definiert sind.
Daraus resultiert das Ei-und-Huhn-Problem: Wie ruft man die erste `async` Funktion auf?
Wenn Sie mit **FastAPI** arbeiten, müssen Sie sich darüber keine Sorgen machen, da diese „erste“ Funktion Ihre *Pfadoperation-Funktion* sein wird und FastAPI weiß, was zu tun ist.
Wenn Sie jedoch `async` / `await` ohne FastAPI verwenden möchten, können Sie dies auch tun.
### Schreiben Sie Ihren eigenen asynchronen Code { #write-your-own-async-code }
Starlette (und **FastAPI**) basieren auf <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a>, was bedeutet, dass es sowohl kompatibel mit der Python-Standardbibliothek <a href="https://docs.python.org/3/library/asyncio-task.html" class="external-link" target="_blank">asyncio</a> als auch mit <a href="https://trio.readthedocs.io/en/stable/" class="external-link" target="_blank">Trio</a> ist.
Insbesondere können Sie <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> direkt verwenden für Ihre fortgeschrittenen nebenläufigen Anwendungsfälle, die fortgeschrittenere Muster in Ihrem eigenen Code erfordern.
Und auch wenn Sie FastAPI nicht verwenden würden, könnten Sie Ihre eigenen asynchronen Anwendungen mit <a href="https://anyio.readthedocs.io/en/stable/" class="external-link" target="_blank">AnyIO</a> schreiben, um hochkompatibel zu sein und dessen Vorteile zu nutzen (z. B. *strukturierte Nebenläufigkeit*).
Ich habe eine weitere Bibliothek auf Basis von AnyIO erstellt, als dünne Schicht obendrauf, um die Typannotationen etwas zu verbessern und bessere **Autovervollständigung**, **Inline-Fehler** usw. zu erhalten. Sie hat auch eine freundliche Einführung und ein Tutorial, um Ihnen zu helfen, **Ihren eigenen asynchronen Code zu verstehen** und zu schreiben: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. Sie ist insbesondere nützlich, wenn Sie **asynchronen Code mit regulärem** (blockierendem/synchronem) Code kombinieren müssen.
### Andere Formen von asynchronem Code { #other-forms-of-asynchronous-code }
Diese Art der Verwendung von `async` und `await` ist in der Sprache relativ neu.
Aber sie erleichtert die Arbeit mit asynchronem Code erheblich.
Die gleiche Syntax (oder fast identisch) wurde kürzlich auch in moderne Versionen von JavaScript (im Browser und in NodeJS) aufgenommen.
Davor war der Umgang mit asynchronem Code jedoch deutlich komplexer und schwieriger.
In früheren Versionen von Python hätten Sie Threads oder <a href="https://www.gevent.org/" class="external-link" target="_blank">Gevent</a> verwenden können. Der Code ist jedoch viel komplexer zu verstehen, zu debuggen und nachzuvollziehen.
In früheren Versionen von NodeJS / Browser JavaScript hätten Sie „Callbacks“ verwendet. Was zur „Callback-Hölle“ führt.
## Coroutinen { #coroutines }
**Coroutine** ist nur ein schicker Begriff für dasjenige, was von einer `async def`-Funktion zurückgegeben wird. Python weiß, dass es so etwas wie eine Funktion ist, die es starten kann und die irgendwann endet, aber auch dass sie pausiert ⏸ werden kann, wann immer darin ein `await` steht.
Aber all diese Funktionalität der Verwendung von asynchronem Code mit `async` und `await` wird oft als Verwendung von „Coroutinen“ zusammengefasst. Es ist vergleichbar mit dem Hauptmerkmal von Go, den „Goroutinen“.
## Fazit { #conclusion }
Sehen wir uns den gleichen Satz von oben noch mal an:
> Moderne Versionen von Python unterstützen **„asynchronen Code“** unter Verwendung sogenannter **„Coroutinen“** mithilfe der Syntax **`async` und `await`**.
Das sollte jetzt mehr Sinn ergeben. ✨
All das ist es, was FastAPI (via Starlette) befeuert und es eine so beeindruckende Performanz haben lässt.
## Sehr technische Details { #very-technical-details }
/// warning | Achtung
Das folgende können Sie wahrscheinlich überspringen.
Dies sind sehr technische Details darüber, wie **FastAPI** unter der Haube funktioniert.
Wenn Sie über gute technische Kenntnisse verfügen (Coroutinen, Threads, Blocking, usw.) und neugierig sind, wie FastAPI mit `async def`s im Vergleich zu normalen `def`s umgeht, fahren Sie fort.
Wenn Sie eine *Pfadoperation-Funktion* mit normalem `def` anstelle von `async def` deklarieren, wird sie in einem externen Threadpool ausgeführt, der dann `await`et wird, anstatt direkt aufgerufen zu werden (da dies den Server blockieren würde).
Wenn Sie von einem anderen asynchronen Framework kommen, das nicht auf die oben beschriebene Weise funktioniert, und Sie es gewohnt sind, triviale, nur-berechnende *Pfadoperation-Funktionen* mit einfachem `def` zu definieren, um einen geringfügigen Geschwindigkeitsgewinn (etwa 100 Nanosekunden) zu erzielen, beachten Sie bitte, dass der Effekt in **FastAPI** genau gegenteilig wäre. In solchen Fällen ist es besser, `async def` zu verwenden, es sei denn, Ihre *Pfadoperation-Funktionen* verwenden Code, der blockierende <abbr title="Input/Output – Eingabe/Ausgabe: Lesen oder Schreiben von/auf Festplatte, Netzwerkkommunikation.">I/O</abbr>-Operationen durchführt.
Dennoch besteht in beiden Fällen eine gute Chance, dass **FastAPI** [immer noch schneller](index.md#performance){.internal-link target=_blank} als Ihr bisheriges Framework (oder zumindest damit vergleichbar) ist.
### Abhängigkeiten { #dependencies }
Das Gleiche gilt für [Abhängigkeiten](tutorial/dependencies/index.md){.internal-link target=_blank}. Wenn eine Abhängigkeit eine normale `def`-Funktion anstelle einer `async def` ist, wird sie im externen Threadpool ausgeführt.
### Unterabhängigkeiten { #sub-dependencies }
Sie können mehrere Abhängigkeiten und [Unterabhängigkeiten](tutorial/dependencies/sub-dependencies.md){.internal-link target=_blank} haben, die einander bedingen (als Parameter der Funktionsdefinitionen), einige davon könnten erstellt werden mit `async def` und einige mit normalem `def`. Es würde immer noch funktionieren, und diejenigen, die mit normalem `def` erstellt wurden, würden in einem externen Thread (vom Threadpool stammend) aufgerufen werden, anstatt `await`et zu werden.
### Andere Hilfsfunktionen { #other-utility-functions }
Jede andere Hilfsfunktion, die Sie direkt aufrufen, kann mit normalem `def` oder `async def` erstellt werden, und FastAPI beeinflusst nicht die Art und Weise, wie Sie sie aufrufen.
Dies steht im Gegensatz zu den Funktionen, die FastAPI für Sie aufruft: *Pfadoperation-Funktionen* und Abhängigkeiten.
Wenn Ihre Hilfsfunktion eine normale Funktion mit `def` ist, wird sie direkt aufgerufen (so wie Sie es in Ihrem Code schreiben), nicht in einem Threadpool. Wenn die Funktion mit `async def` erstellt wurde, sollten Sie sie `await`en, wenn Sie sie in Ihrem Code aufrufen.
---
Nochmal, es handelt sich hier um sehr technische Details, die Ihnen helfen, falls Sie danach gesucht haben.
Andernfalls liegen Sie richtig, wenn Sie sich an die Richtlinien aus dem obigen Abschnitt halten: <a href="#in-a-hurry">In Eile?</a>.
Unabhängige TechEmpower-Benchmarks zeigen **FastAPI**-Anwendungen, die unter Uvicorn ausgeführt werden, als <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" class="external-link" target="_blank">eines der schnellsten verfügbaren Python-Frameworks</a>, nur unterhalb von Starlette und Uvicorn selbst (die intern von FastAPI verwendet werden).
Aber bei der Betrachtung von Benchmarks und Vergleichen sollten Sie Folgendes beachten.
## Benchmarks und Geschwindigkeit { #benchmarks-and-speed }
Wenn Sie die Benchmarks ansehen, ist es üblich, dass mehrere Tools unterschiedlichen Typs als gleichwertig verglichen werden.
Insbesondere dass Uvicorn, Starlette und FastAPI zusammen verglichen werden (neben vielen anderen Tools).
Je einfacher das Problem, das durch das Tool gelöst wird, desto besser wird die Performanz sein. Und die meisten Benchmarks testen nicht die zusätzlichen Funktionen, die das Tool bietet.
Die Hierarchie ist wie folgt:
* **Uvicorn**: ein ASGI-Server
* **Starlette**: (verwendet Uvicorn) ein Web-Mikroframework
* **FastAPI**: (verwendet Starlette) ein API-Mikroframework mit mehreren zusätzlichen Funktionen zum Erstellen von APIs, mit Datenvalidierung, usw.
* **Uvicorn**:
* Wird die beste Performanz haben, da außer dem Server selbst nicht viel zusätzlicher Code vorhanden ist.
* Sie würden eine Anwendung nicht direkt in Uvicorn schreiben. Das würde bedeuten, dass Ihr Code zumindest mehr oder weniger den gesamten von Starlette (oder **FastAPI**) bereitgestellten Code enthalten müsste. Und wenn Sie das täten, hätte Ihre endgültige Anwendung den gleichen Overhead wie bei der Verwendung eines Frameworks und der Minimierung Ihres Anwendungscodes und der Fehler.
* Wenn Sie Uvicorn vergleichen, vergleichen Sie es mit Anwendungsservern wie Daphne, Hypercorn, uWSGI, usw.
* **Starlette**:
* Wird nach Uvicorn die nächstbeste Performanz erbringen. Tatsächlich verwendet Starlette intern Uvicorn, um zu laufen. Daher kann es wahrscheinlich nur „langsamer“ als Uvicorn werden, weil mehr Code ausgeführt werden muss.
* Aber es bietet Ihnen die Werkzeuge, um einfache Webanwendungen zu erstellen, mit Routing basierend auf Pfaden, usw.
* Wenn Sie Starlette vergleichen, vergleichen Sie es mit Webframeworks (oder Mikroframeworks) wie Sanic, Flask, Django, usw.
* **FastAPI**:
* So wie Starlette Uvicorn verwendet und nicht schneller als dieses sein kann, verwendet **FastAPI** Starlette, sodass es nicht schneller als dieses sein kann.
* FastAPI bietet zusätzliche Funktionen auf Basis von Starlette. Funktionen, die Sie beim Erstellen von APIs fast immer benötigen, wie Datenvalidierung und Serialisierung. Und wenn Sie es verwenden, erhalten Sie kostenlose automatische Dokumentation (die automatische Dokumentation verursacht nicht einmal zusätzlichen Overhead für laufende Anwendungen, sie wird beim Starten generiert).
* Wenn Sie FastAPI nicht verwenden und stattdessen Starlette direkt (oder ein anderes Tool wie Sanic, Flask, Responder, usw.) verwenden würden, müssten Sie die gesamte Datenvalidierung und Serialisierung selbst implementieren. Ihre finale Anwendung hätte also immer noch den gleichen Overhead, als ob sie mit FastAPI erstellt worden wäre. Und in vielen Fällen ist diese Datenvalidierung und Serialisierung der größte Teil des in Anwendungen geschriebenen Codes.
* Durch die Verwendung von FastAPI sparen Sie also Entwicklungszeit, Fehler und Codezeilen und würden wahrscheinlich die gleiche Performanz (oder eine bessere) erzielen, die Sie hätten, wenn Sie es nicht verwenden würden (da Sie alles in Ihrem Code implementieren müssten).
* Wenn Sie FastAPI vergleichen, vergleichen Sie es mit einem Webanwendungs-Framework (oder einer Reihe von Tools), das Datenvalidierung, Serialisierung und Dokumentation bereitstellt, wie Flask-apispec, NestJS, Molten, usw. – Frameworks mit integrierter automatischer Datenvalidierung, Serialisierung und Dokumentation.
# FastAPI bei Cloudanbietern deployen { #deploy-fastapi-on-cloud-providers }
Sie können praktisch **jeden Cloudanbieter** verwenden, um Ihre FastAPI-Anwendung bereitzustellen.
In den meisten Fällen bieten die großen Cloudanbieter Anleitungen zum Deployment von FastAPI an.
## FastAPI Cloud { #fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** wurde 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 Finanzierungsgeber für die *FastAPI and friends* Open-Source-Projekte. ✨
Bei dem Deployment – der Bereitstellung – einer **FastAPI**-Anwendung, oder eigentlich jeder Art von Web-API, gibt es mehrere Konzepte, die Sie wahrscheinlich interessieren, und mithilfe der Sie die **am besten geeignete** Methode zum **Deployment Ihrer Anwendung** finden können.
Einige wichtige Konzepte sind:
* Sicherheit – HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Arbeitsspeicher
* Schritte vor dem Start
Wir werden sehen, wie diese sich auf das **Deployment** auswirken.
Letztendlich besteht das ultimative Ziel darin, **Ihre API-Clients** auf **sichere** Weise zu versorgen, um **Unterbrechungen** zu vermeiden und die **Rechenressourcen** (z. B. entfernte Server/virtuelle Maschinen) so effizient wie möglich zu nutzen. 🚀
Ich erzähle Ihnen hier etwas mehr über diese **Konzepte**, was Ihnen hoffentlich die **Intuition** gibt, die Sie benötigen, um zu entscheiden, wie Sie Ihre API in sehr unterschiedlichen Umgebungen deployen, möglicherweise sogar in **zukünftigen**, die jetzt noch nicht existieren.
Durch die Berücksichtigung dieser Konzepte können Sie die beste Variante des Deployments **Ihrer eigenen APIs****evaluieren und konzipieren**.
In den nächsten Kapiteln werde ich Ihnen mehr **konkrete Rezepte** für das Deployment von FastAPI-Anwendungen geben.
Aber schauen wir uns zunächst einmal diese grundlegenden **konzeptionellen Ideen** an. Diese Konzepte gelten auch für jede andere Art von Web-API. 💡
## Sicherheit – HTTPS { #security-https }
Im [vorherigen Kapitel über HTTPS](https.md){.internal-link target=_blank} haben wir erfahren, wie HTTPS Verschlüsselung für Ihre API bereitstellt.
Wir haben auch gesehen, dass HTTPS normalerweise von einer Komponente **außerhalb** Ihres Anwendungsservers bereitgestellt wird, einem **TLS-Terminierungsproxy**.
Und es muss etwas geben, das für die **Erneuerung der HTTPS-Zertifikate** zuständig ist, es könnte sich um dieselbe Komponente handeln oder um etwas anderes.
### Beispieltools für HTTPS { #example-tools-for-https }
Einige der Tools, die Sie als TLS-Terminierungsproxy verwenden können, sind:
* Traefik
* Handhabt automatisch Zertifikat-Erneuerungen ✨
* Caddy
* Handhabt automatisch Zertifikat-Erneuerungen ✨
* Nginx
* Mit einer externen Komponente wie Certbot für Zertifikat-Erneuerungen
* HAProxy
* Mit einer externen Komponente wie Certbot für Zertifikat-Erneuerungen
* Kubernetes mit einem Ingress Controller wie Nginx
* Mit einer externen Komponente wie cert-manager für Zertifikat-Erneuerungen
* Es wird intern von einem Cloud-Anbieter als Teil seiner Dienste verwaltet (siehe unten 👇)
Eine andere Möglichkeit besteht darin, dass Sie einen **Cloud-Dienst** verwenden, der den größten Teil der Arbeit übernimmt, einschließlich der Einrichtung von HTTPS. Er könnte einige Einschränkungen haben oder Ihnen mehr in Rechnung stellen, usw. In diesem Fall müssten Sie jedoch nicht selbst einen TLS-Terminierungsproxy einrichten.
In den nächsten Kapiteln zeige ich Ihnen einige konkrete Beispiele.
---
Die nächsten zu berücksichtigenden Konzepte drehen sich dann um das Programm, das Ihre eigentliche API ausführt (z. B. Uvicorn).
## Programm und Prozess { #program-and-process }
Wir werden viel über den laufenden „**Prozess**“ sprechen, daher ist es nützlich, Klarheit darüber zu haben, was das bedeutet und was der Unterschied zum Wort „**Programm**“ ist.
### Was ist ein Programm { #what-is-a-program }
Das Wort **Programm** wird häufig zur Beschreibung vieler Dinge verwendet:
* Der **Code**, den Sie schreiben, die **Python-Dateien**.
* Die **Datei**, die vom Betriebssystem **ausgeführt** werden kann, zum Beispiel: `python`, `python.exe` oder `uvicorn`.
* Ein bestimmtes Programm, während es auf dem Betriebssystem **läuft**, die CPU nutzt und Dinge im Arbeitsspeicher ablegt. Dies wird auch als **Prozess** bezeichnet.
### Was ist ein Prozess { #what-is-a-process }
Das Wort **Prozess** wird normalerweise spezifischer verwendet und bezieht sich nur auf das, was im Betriebssystem ausgeführt wird (wie im letzten Punkt oben):
* Ein bestimmtes Programm, während es auf dem Betriebssystem **ausgeführt** wird.
* Dies bezieht sich weder auf die Datei noch auf den Code, sondern **speziell** auf das, was vom Betriebssystem **ausgeführt** und verwaltet wird.
* Jedes Programm, jeder Code **kann nur dann Dinge tun**, wenn er **ausgeführt** wird, wenn also ein **Prozess läuft**.
* Der Prozess kann von Ihnen oder vom Betriebssystem **terminiert** („beendet“, „gekillt“) werden. An diesem Punkt hört es auf zu laufen/ausgeführt zu werden und kann **keine Dinge mehr tun**.
* Hinter jeder Anwendung, die Sie auf Ihrem Computer ausführen, steckt ein Prozess, jedes laufende Programm, jedes Fenster usw. Und normalerweise laufen viele Prozesse **gleichzeitig**, während ein Computer eingeschaltet ist.
* Es können **mehrere Prozesse** desselben **Programms** gleichzeitig ausgeführt werden.
Wenn Sie sich den „Task-Manager“ oder „Systemmonitor“ (oder ähnliche Tools) in Ihrem Betriebssystem ansehen, können Sie viele dieser laufenden Prozesse sehen.
Und Sie werden beispielsweise wahrscheinlich feststellen, dass mehrere Prozesse dasselbe Browserprogramm ausführen (Firefox, Chrome, Edge, usw.). Normalerweise führen diese einen Prozess pro Browsertab sowie einige andere zusätzliche Prozesse aus.
Nachdem wir nun den Unterschied zwischen den Begriffen **Prozess** und **Programm** kennen, sprechen wir weiter über das Deployment.
## Beim Hochfahren ausführen { #running-on-startup }
Wenn Sie eine Web-API erstellen, möchten Sie in den meisten Fällen, dass diese **immer läuft**, ununterbrochen, damit Ihre Clients immer darauf zugreifen können. Es sei denn natürlich, Sie haben einen bestimmten Grund, warum Sie möchten, dass diese nur in bestimmten Situationen ausgeführt wird. Meistens möchten Sie jedoch, dass sie ständig ausgeführt wird und **verfügbar** ist.
### Auf einem entfernten Server { #in-a-remote-server }
Wenn Sie einen entfernten Server (einen Cloud-Server, eine virtuelle Maschine, usw.) einrichten, können Sie am einfachsten `fastapi run` (welches Uvicorn verwendet) oder etwas Ähnliches manuell ausführen, genau wie bei der lokalen Entwicklung.
Und es wird funktionieren und **während der Entwicklung** nützlich sein.
Wenn Ihre Verbindung zum Server jedoch unterbrochen wird, wird der **laufende Prozess** wahrscheinlich abstürzen.
Und wenn der Server neu gestartet wird (z. B. nach Updates oder Migrationen vom Cloud-Anbieter), werden Sie das wahrscheinlich **nicht bemerken**. Und deshalb wissen Sie nicht einmal, dass Sie den Prozess manuell neu starten müssen. Ihre API bleibt also einfach tot. 😱
### Beim Hochfahren automatisch ausführen { #run-automatically-on-startup }
Im Allgemeinen möchten Sie wahrscheinlich, dass das Serverprogramm (z. B. Uvicorn) beim Hochfahren des Servers automatisch gestartet wird und kein **menschliches Eingreifen** erforderlich ist, sodass immer ein Prozess mit Ihrer API ausgeführt wird (z. B. Uvicorn, welches Ihre FastAPI-Anwendung ausführt).
### Separates Programm { #separate-program }
Um dies zu erreichen, haben Sie normalerweise ein **separates Programm**, welches sicherstellt, dass Ihre Anwendung beim Hochfahren ausgeführt wird. Und in vielen Fällen würde es auch sicherstellen, dass auch andere Komponenten oder Anwendungen ausgeführt werden, beispielsweise eine Datenbank.
### Beispieltools zur Ausführung beim Hochfahren { #example-tools-to-run-at-startup }
Einige Beispiele für Tools, die diese Aufgabe übernehmen können, sind:
* Docker
* Kubernetes
* Docker Compose
* Docker im Schwarm-Modus
* Systemd
* Supervisor
* Es wird intern von einem Cloud-Anbieter im Rahmen seiner Dienste verwaltet
* Andere ...
In den nächsten Kapiteln werde ich Ihnen konkretere Beispiele geben.
## Neustart { #restarts }
Ähnlich wie Sie sicherstellen möchten, dass Ihre Anwendung beim Hochfahren ausgeführt wird, möchten Sie wahrscheinlich auch sicherstellen, dass diese nach Fehlern **neu gestartet** wird.
### Wir machen Fehler { #we-make-mistakes }
Wir, als Menschen, machen ständig **Fehler**. Software hat fast *immer***Bugs**, die an verschiedenen Stellen versteckt sind. 🐛
Und wir als Entwickler verbessern den Code ständig, wenn wir diese Bugs finden und neue Funktionen implementieren (und möglicherweise auch neue Bugs hinzufügen 😅).
### Kleine Fehler automatisch handhaben { #small-errors-automatically-handled }
Wenn beim Erstellen von Web-APIs mit FastAPI ein Fehler in unserem Code auftritt, wird FastAPI ihn normalerweise dem einzelnen <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> zurückgeben, der den Fehler ausgelöst hat. 🛡
Der Client erhält für diesen Request einen **500 Internal Server Error**, aber die Anwendung arbeitet bei den nächsten Requests weiter, anstatt einfach komplett abzustürzen.
Dennoch kann es vorkommen, dass wir Code schreiben, der **die gesamte Anwendung zum Absturz bringt** und so zum Absturz von Uvicorn und Python führt. 💥
Und dennoch möchten Sie wahrscheinlich nicht, dass die Anwendung tot bleibt, weil an einer Stelle ein Fehler aufgetreten ist. Sie möchten wahrscheinlich, dass sie zumindest für die *Pfadoperationen*, die nicht fehlerhaft sind, **weiterläuft**.
### Neustart nach Absturz { #restart-after-crash }
Aber in den Fällen mit wirklich schwerwiegenden Fehlern, die den laufenden **Prozess** zum Absturz bringen, benötigen Sie eine externe Komponente, die den Prozess **neu startet**, zumindest ein paar Mal ...
/// tip | Tipp
... Obwohl es wahrscheinlich keinen Sinn macht, sie immer wieder neu zu starten, wenn die gesamte Anwendung einfach **sofort abstürzt**. Aber in diesen Fällen werden Sie es wahrscheinlich während der Entwicklung oder zumindest direkt nach dem Deployment bemerken.
Konzentrieren wir uns also auf die Hauptfälle, in denen die Anwendung in bestimmten Fällen **in der Zukunft** völlig abstürzen könnte und es dann dennoch sinnvoll ist, sie neu zu starten.
///
Sie möchten wahrscheinlich, dass eine **externe Komponente** für den Neustart Ihrer Anwendung verantwortlich ist, da zu diesem Zeitpunkt dieselbe Anwendung mit Uvicorn und Python bereits abgestürzt ist und es daher nichts im selben Code derselben Anwendung gibt, was etwas dagegen tun kann.
### Beispieltools zum automatischen Neustart { #example-tools-to-restart-automatically }
In den meisten Fällen wird dasselbe Tool, das zum **Ausführen des Programms beim Hochfahren** verwendet wird, auch für automatische **Neustarts** verwendet.
Dies könnte zum Beispiel erledigt werden durch:
* Docker
* Kubernetes
* Docker Compose
* Docker im Schwarm-Modus
* Systemd
* Supervisor
* Intern von einem Cloud-Anbieter im Rahmen seiner Dienste
* Andere ...
## Replikation – Prozesse und Arbeitsspeicher { #replication-processes-and-memory }
Wenn Sie eine FastAPI-Anwendung verwenden und ein Serverprogramm wie den `fastapi`-Befehl, der Uvicorn ausführt, kann **ein einzelner Prozess** an mehrere Clients gleichzeitig ausliefern.
In vielen Fällen möchten Sie jedoch mehrere Workerprozesse gleichzeitig ausführen.
### Mehrere Prozesse – Worker { #multiple-processes-workers }
Wenn Sie mehr Clients haben, als ein einzelner Prozess verarbeiten kann (z. B. wenn die virtuelle Maschine nicht sehr groß ist) und die CPU des Servers **mehrere Kerne** hat, dann könnten **mehrere Prozesse** gleichzeitig mit derselben Anwendung laufen und alle Requests unter sich verteilen.
Wenn Sie mit **mehreren Prozessen** dasselbe API-Programm ausführen, werden diese üblicherweise als <abbr title="Arbeiter">**Worker**</abbr> bezeichnet.
### Workerprozesse und Ports { #worker-processes-and-ports }
Erinnern Sie sich aus der Dokumentation [Über HTTPS](https.md){.internal-link target=_blank}, dass nur ein Prozess auf einer Kombination aus Port und IP-Adresse auf einem Server lauschen kann?
Das ist immer noch wahr.
Um also **mehrere Prozesse** gleichzeitig zu haben, muss es einen **einzelnen Prozess geben, der einen Port überwacht**, welcher dann die Kommunikation auf irgendeine Weise an jeden Workerprozess überträgt.
### Arbeitsspeicher pro Prozess { #memory-per-process }
Wenn das Programm nun Dinge in den Arbeitsspeicher lädt, zum Beispiel ein Modell für maschinelles Lernen in einer Variablen oder den Inhalt einer großen Datei in einer Variablen, verbraucht das alles **einen Teil des Arbeitsspeichers (RAM – Random Access Memory)** des Servers.
Und mehrere Prozesse teilen sich normalerweise keinen Speicher. Das bedeutet, dass jeder laufende Prozess seine eigenen Dinge, eigenen Variablen und eigenen Speicher hat. Und wenn Sie in Ihrem Code viel Speicher verbrauchen, verbraucht **jeder Prozess** die gleiche Menge Speicher.
### Serverspeicher { #server-memory }
Wenn Ihr Code beispielsweise ein Machine-Learning-Modell mit **1 GB Größe** lädt und Sie einen Prozess mit Ihrer API ausführen, verbraucht dieser mindestens 1 GB RAM. Und wenn Sie **4 Prozesse** (4 Worker) starten, verbraucht jeder 1 GB RAM. Insgesamt verbraucht Ihre API also **4 GB RAM**.
Und wenn Ihr entfernter Server oder Ihre virtuelle Maschine nur über 3 GB RAM verfügt, führt der Versuch, mehr als 4 GB RAM zu laden, zu Problemen. 🚨
### Mehrere Prozesse – Ein Beispiel { #multiple-processes-an-example }
Im folgenden Beispiel gibt es einen **Manager-Prozess**, welcher zwei **Workerprozesse** startet und steuert.
Dieser Manager-Prozess wäre wahrscheinlich derjenige, welcher der IP am **Port** lauscht. Und er würde die gesamte Kommunikation an die Workerprozesse weiterleiten.
Diese Workerprozesse würden Ihre Anwendung ausführen, sie würden die Hauptberechnungen durchführen, um einen **<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>** entgegenzunehmen und eine **<abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>** zurückzugeben, und sie würden alles, was Sie in Variablen einfügen, in den RAM laden.
Und natürlich würden auf derselben Maschine neben Ihrer Anwendung wahrscheinlich **andere Prozesse** laufen.
Ein interessantes Detail ist dabei, dass der Prozentsatz der von jedem Prozess verwendeten **CPU** im Laufe der Zeit stark **variieren** kann, der **Arbeitsspeicher (RAM)** jedoch normalerweise mehr oder weniger **stabil** bleibt.
Wenn Sie eine API haben, die jedes Mal eine vergleichbare Menge an Berechnungen durchführt, und Sie viele Clients haben, dann wird die **CPU-Auslastung** wahrscheinlich *ebenfalls stabil sein* (anstatt ständig schnell zu steigen und zu fallen).
### Beispiele für Replikation-Tools und -Strategien { #examples-of-replication-tools-and-strategies }
Es gibt mehrere Ansätze, um dies zu erreichen, und ich werde Ihnen in den nächsten Kapiteln mehr über bestimmte Strategien erzählen, beispielsweise wenn es um Docker und Container geht.
Die wichtigste zu berücksichtigende Einschränkung besteht darin, dass es eine **einzelne** Komponente geben muss, welche die **öffentliche IP** auf dem **Port** verwaltet. Und dann muss diese irgendwie die Kommunikation **weiterleiten**, an die replizierten **Prozesse/Worker**.
Hier sind einige mögliche Kombinationen und Strategien:
* **Uvicorn** mit `--workers`
* Ein Uvicorn-**Prozessmanager** würde der **IP** am **Port** lauschen, und er würde **mehrere Uvicorn-Workerprozesse** starten.
* **Kubernetes** und andere verteilte **Containersysteme**
* Etwas in der **Kubernetes**-Ebene würde die **IP** und den **Port** abhören. Die Replikation hätte **mehrere Container**, in jedem wird jeweils **ein Uvicorn-Prozess** ausgeführt.
* **Cloud-Dienste**, welche das für Sie erledigen
* Der Cloud-Dienst wird wahrscheinlich **die Replikation für Sie übernehmen**. Er würde Sie möglicherweise **einen auszuführenden Prozess** oder ein **zu verwendendes Container-Image** definieren lassen, in jedem Fall wäre es höchstwahrscheinlich **ein einzelner Uvicorn-Prozess**, und der Cloud-Dienst wäre auch verantwortlich für die Replikation.
/// tip | Tipp
Machen Sie sich keine Sorgen, wenn einige dieser Punkte zu **Containern**, Docker oder Kubernetes noch nicht viel Sinn ergeben.
Ich werde Ihnen in einem zukünftigen Kapitel mehr über Container-Images, Docker, Kubernetes, usw. erzählen: [FastAPI in Containern – Docker](docker.md){.internal-link target=_blank}.
///
## Schritte vor dem Start { #previous-steps-before-starting }
Es gibt viele Fälle, in denen Sie, **bevor Sie Ihre Anwendung starten**, einige Schritte ausführen möchten.
Beispielsweise möchten Sie möglicherweise **Datenbankmigrationen** ausführen.
In den meisten Fällen möchten Sie diese Schritte jedoch nur **einmal** ausführen.
Sie möchten also einen **einzelnen Prozess** haben, um diese **Vorab-Schritte** auszuführen, bevor Sie die Anwendung starten.
Und Sie müssen sicherstellen, dass es sich um einen einzelnen Prozess handelt, der die Vorab-Schritte ausführt, *auch* wenn Sie anschließend **mehrere Prozesse** (mehrere Worker) für die Anwendung selbst starten. Wenn diese Schritte von **mehreren Prozessen** ausgeführt würden, würden diese die Arbeit **verdoppeln**, indem sie sie **parallel** ausführen, und wenn es sich bei den Schritten um etwas Delikates wie eine Datenbankmigration handelt, könnte das miteinander Konflikte verursachen.
Natürlich gibt es Fälle, in denen es kein Problem darstellt, die Vorab-Schritte mehrmals auszuführen. In diesem Fall ist die Handhabung viel einfacher.
/// tip | Tipp
Bedenken Sie außerdem, dass Sie, abhängig von Ihrer Einrichtung, in manchen Fällen **gar keine Vorab-Schritte** benötigen, bevor Sie die Anwendung starten.
In diesem Fall müssen Sie sich darüber keine Sorgen machen. 🤷
///
### Beispiele für Strategien für Vorab-Schritte { #examples-of-previous-steps-strategies }
Es hängt **stark** davon ab, wie Sie **Ihr System deployen**, und hängt wahrscheinlich mit der Art und Weise zusammen, wie Sie Programme starten, Neustarts durchführen, usw.
Hier sind einige mögliche Ideen:
* Ein „Init-Container“ in Kubernetes, der vor Ihrem Anwendungs-Container ausgeführt wird
* Ein Bash-Skript, das die Vorab-Schritte ausführt und dann Ihre Anwendung startet
* Sie benötigen immer noch eine Möglichkeit, *dieses* Bash-Skript zu starten/neu zu starten, Fehler zu erkennen, usw.
/// tip | Tipp
Konkretere Beispiele hierfür mit Containern gebe ich Ihnen in einem späteren Kapitel: [FastAPI in Containern – Docker](docker.md){.internal-link target=_blank}.
///
## Ressourcennutzung { #resource-utilization }
Ihr(e) Server ist (sind) eine **Ressource**, welche Sie mit Ihren Programmen, der Rechenzeit auf den CPUs und dem verfügbaren RAM-Speicher verbrauchen oder **nutzen** können.
Wie viele Systemressourcen möchten Sie verbrauchen/nutzen? Sie mögen „nicht viel“ denken, aber in Wirklichkeit möchten Sie tatsächlich **so viel wie möglich ohne Absturz** verwenden.
Wenn Sie für drei Server bezahlen, aber nur wenig von deren RAM und CPU nutzen, **verschwenden Sie wahrscheinlich Geld** 💸 und wahrscheinlich **Strom für den Server** 🌎, usw.
In diesem Fall könnte es besser sein, nur zwei Server zu haben und einen höheren Prozentsatz von deren Ressourcen zu nutzen (CPU, Arbeitsspeicher, Festplatte, Netzwerkbandbreite, usw.).
Wenn Sie andererseits über zwei Server verfügen und **100 % ihrer CPU und ihres RAM** nutzen, wird irgendwann ein Prozess nach mehr Speicher fragen und der Server muss die Festplatte als „Speicher“ verwenden (was tausendmal langsamer sein kann) oder er könnte sogar **abstürzen**. Oder ein Prozess muss möglicherweise einige Berechnungen durchführen und müsste warten, bis die CPU wieder frei ist.
In diesem Fall wäre es besser, **einen zusätzlichen Server** zu besorgen und einige Prozesse darauf auszuführen, damit alle über **genug RAM und CPU-Zeit** verfügen.
Es besteht auch die Möglichkeit, dass es aus irgendeinem Grund zu **Spitzen** in der Nutzung Ihrer API kommt. Vielleicht ist diese viral gegangen, oder vielleicht haben andere Dienste oder Bots damit begonnen, sie zu nutzen. Und vielleicht möchten Sie in solchen Fällen über zusätzliche Ressourcen verfügen, um auf der sicheren Seite zu sein.
Sie können eine **beliebige Zahl** festlegen, um beispielsweise eine Ressourcenauslastung zwischen **50 % und 90 %** anzustreben. Der Punkt ist, dass dies wahrscheinlich die wichtigen Dinge sind, die Sie messen und verwenden sollten, um Ihre Deployments zu optimieren.
Sie können einfache Tools wie `htop` verwenden, um die in Ihrem Server verwendete CPU und den RAM oder die von jedem Prozess verwendete Menge anzuzeigen. Oder Sie können komplexere Überwachungstools verwenden, die möglicherweise auf mehrere Server usw. verteilt sind.
## Zusammenfassung { #recap }
Sie haben hier einige der wichtigsten Konzepte gelesen, die Sie wahrscheinlich berücksichtigen müssen, wenn Sie entscheiden, wie Sie Ihre Anwendung deployen:
* Sicherheit – HTTPS
* Beim Hochfahren ausführen
* Neustarts
* Replikation (die Anzahl der laufenden Prozesse)
* Arbeitsspeicher
* Schritte vor dem Start
Das Verständnis dieser Ideen und deren Anwendung sollte Ihnen die nötige Intuition vermitteln, um bei der Konfiguration und Optimierung Ihrer Deployments Entscheidungen zu treffen. 🤓
In den nächsten Abschnitten gebe ich Ihnen konkretere Beispiele für mögliche Strategien, die Sie verfolgen können. 🚀
# FastAPI in Containern – Docker { #fastapi-in-containers-docker }
Beim Deployment von FastAPI-Anwendungen besteht ein gängiger Ansatz darin, ein **Linux-Containerimage** zu erstellen. Normalerweise erfolgt dies mit <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a>. Sie können dieses Containerimage dann auf eine von mehreren möglichen Arten deployen.
Die Verwendung von Linux-Containern bietet mehrere Vorteile, darunter **Sicherheit**, **Replizierbarkeit**, **Einfachheit** und andere.
/// tip | Tipp
Sie haben es eilig und kennen sich bereits aus? Springen Sie zum [`Dockerfile` unten 👇](#build-a-docker-image-for-fastapi).
///
<details>
<summary>Dockerfile-Vorschau 👀</summary>
```Dockerfile
FROMpython:3.9
WORKDIR/code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD["fastapi","run","app/main.py","--port","80"]
# Wenn Sie hinter einem Proxy wie Nginx oder Traefik sind, fügen Sie --proxy-headers hinzu
Container (hauptsächlich Linux-Container) sind eine sehr **leichtgewichtige** Möglichkeit, Anwendungen einschließlich aller ihrer Abhängigkeiten und erforderlichen Dateien zu verpacken und sie gleichzeitig von anderen Containern (anderen Anwendungen oder Komponenten) im selben System isoliert zu halten.
Linux-Container werden mit demselben Linux-Kernel des Hosts (Maschine, virtuellen Maschine, Cloud-Servers, usw.) ausgeführt. Das bedeutet einfach, dass sie sehr leichtgewichtig sind (im Vergleich zu vollständigen virtuellen Maschinen, die ein gesamtes Betriebssystem emulieren).
Auf diese Weise verbrauchen Container **wenig Ressourcen**, eine Menge vergleichbar mit der direkten Ausführung der Prozesse (eine virtuelle Maschine würde viel mehr verbrauchen).
Container verfügen außerdem über ihre eigenen **isoliert** laufenden Prozesse (üblicherweise nur einen Prozess), über ihr eigenes Dateisystem und ihr eigenes Netzwerk, was Deployment, Sicherheit, Entwicklung usw. vereinfacht.
## Was ist ein Containerimage { #what-is-a-container-image }
Ein **Container** wird von einem **Containerimage** ausgeführt.
Ein Containerimage ist eine **statische** Version aller Dateien, Umgebungsvariablen und des Standardbefehls/-programms, welche in einem Container vorhanden sein sollten. **Statisch** bedeutet hier, dass das Container-**Image** nicht läuft, nicht ausgeführt wird, sondern nur die gepackten Dateien und Metadaten enthält.
Im Gegensatz zu einem „**Containerimage**“, bei dem es sich um den gespeicherten statischen Inhalt handelt, bezieht sich ein „**Container**“ normalerweise auf die laufende Instanz, das Ding, das **ausgeführt** wird.
Wenn der **Container** gestartet und ausgeführt wird (gestartet von einem **Containerimage**), kann er Dateien, Umgebungsvariablen usw. erstellen oder ändern. Diese Änderungen sind nur in diesem Container vorhanden, nicht im zugrunde liegenden Containerimage (werden nicht auf der Festplatte gespeichert).
Ein Containerimage ist vergleichbar mit der **Programmdatei** und ihrem Inhalt, z. B. `python` und eine Datei `main.py`.
Und der **Container** selbst (im Gegensatz zum **Containerimage**) ist die tatsächlich laufende Instanz des Images, vergleichbar mit einem **Prozess**. Tatsächlich läuft ein Container nur, wenn er einen **laufenden Prozess** hat (und normalerweise ist es nur ein einzelner Prozess). Der Container stoppt, wenn kein Prozess darin ausgeführt wird.
## Containerimages { #container-images }
Docker ist eines der wichtigsten Tools zum Erstellen und Verwalten von **Containerimages** und **Containern**.
Und es gibt einen öffentlichen <a href="https://hub.docker.com/" class="external-link" target="_blank">Docker <abbr title="Umschlagplatz">Hub</abbr></a> mit vorgefertigten **offiziellen Containerimages** für viele Tools, Umgebungen, Datenbanken und Anwendungen.
Beispielsweise gibt es ein offizielles <a href="https://hub.docker.com/_/python" class="external-link" target="_blank">Python-Image</a>.
Und es gibt viele andere Images für verschiedene Dinge wie Datenbanken, zum Beispiel für:
Durch die Verwendung eines vorgefertigten Containerimages ist es sehr einfach, verschiedene Tools zu **kombinieren** und zu verwenden. Zum Beispiel, um eine neue Datenbank auszuprobieren. In den meisten Fällen können Sie die **offiziellen Images** verwenden und diese einfach mit Umgebungsvariablen konfigurieren.
Auf diese Weise können Sie in vielen Fällen etwas über Container und Docker lernen und dieses Wissen mit vielen verschiedenen Tools und Komponenten wiederverwenden.
Sie würden also **mehrere Container** mit unterschiedlichen Dingen ausführen, wie einer Datenbank, einer Python-Anwendung, einem Webserver mit einer React-Frontend-Anwendung, und diese über ihr internes Netzwerk miteinander verbinden.
In alle Containerverwaltungssysteme (wie Docker oder Kubernetes) sind diese Netzwerkfunktionen integriert.
## Container und Prozesse { #containers-and-processes }
Ein **Containerimage** enthält normalerweise in seinen Metadaten das Standardprogramm oder den Standardbefehl, der ausgeführt werden soll, wenn der **Container** gestartet wird, sowie die Parameter, die an dieses Programm übergeben werden sollen. Sehr ähnlich zu dem, was wäre, wenn es über die Befehlszeile gestartet werden würde.
Wenn ein **Container** gestartet wird, führt er diesen Befehl/dieses Programm aus (Sie können ihn jedoch überschreiben und einen anderen Befehl/ein anderes Programm ausführen lassen).
Ein Container läuft, solange der **Hauptprozess** (Befehl oder Programm) läuft.
Ein Container hat normalerweise einen **einzelnen Prozess**, aber es ist auch möglich, Unterprozesse vom Hauptprozess aus zu starten, und auf diese Weise haben Sie **mehrere Prozesse** im selben Container.
Es ist jedoch nicht möglich, einen laufenden Container, ohne **mindestens einen laufenden Prozess** zu haben. Wenn der Hauptprozess stoppt, stoppt der Container.
## Ein Docker-Image für FastAPI erstellen { #build-a-docker-image-for-fastapi }
Okay, wollen wir jetzt etwas bauen! 🚀
Ich zeige Ihnen, wie Sie ein **Docker-Image** für FastAPI **von Grund auf** erstellen, basierend auf dem **offiziellen Python**-Image.
Das ist, was Sie in **den meisten Fällen** tun möchten, zum Beispiel:
* Bei Verwendung von **Kubernetes** oder ähnlichen Tools
* Beim Betrieb auf einem **Raspberry Pi**
* Bei Verwendung eines Cloud-Dienstes, der ein Containerimage für Sie ausführt, usw.
### Paketanforderungen { #package-requirements }
Normalerweise befinden sich die **Paketanforderungen** für Ihre Anwendung in einer Datei.
Dies hängt hauptsächlich von dem Tool ab, mit dem Sie diese Anforderungen **installieren**.
Die gebräuchlichste Methode besteht darin, eine Datei `requirements.txt` mit den Namen der Packages und deren Versionen zu erstellen, eine pro Zeile.
Sie würden natürlich die gleichen Ideen verwenden, die Sie in [Über FastAPI-Versionen](versions.md){.internal-link target=_blank} gelesen haben, um die Versionsbereiche festzulegen.
Ihre `requirements.txt` könnte beispielsweise so aussehen:
```
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
```
Und normalerweise würden Sie diese Paketabhängigkeiten mit `pip` installieren, zum Beispiel:
<div class="termy">
```console
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
```
</div>
/// 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
fromtypingimportUnion
fromfastapiimportFastAPI
app=FastAPI()
@app.get("/")
defread_root():
return{"Hello":"World"}
@app.get("/items/{item_id}")
defread_item(item_id:int,q:Union[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.9
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
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 <a href="https://docs.docker.com/reference/dockerfile/#cmd" class="external-link" target="_blank">`CMD`</a> Docker-Anweisung kann in zwei Formen geschrieben werden:
Achten Sie darauf, stets die **exec** form zu verwenden, um sicherzustellen, dass FastAPI ordnungsgemäß heruntergefahren wird und [Lifespan-Events](../advanced/events.md){.internal-link target=_blank} ausgelöst werden.
Sie können mehr darüber in der <a href="https://docs.docker.com/reference/dockerfile/#shell-and-exec-form" class="external-link" target="_blank">Docker-Dokumentation für Shell und Exec Form lesen</a>.
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: <a href="https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop" class="external-link" target="_blank">Warum benötigen meine Dienste 10 Sekunden, um neu erstellt oder gestoppt zu werden?</a>.
#### 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.
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:
<div class="termy">
```console
$ docker build -t myimage .
---> 100%
```
</div>
/// 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:
<div class="termy">
```console
$ docker run -d --name mycontainer -p 80:80 myimage
```
</div>
## Es testen { #check-it }
Sie sollten es in der URL Ihres Docker-Containers überprüfen können, zum Beispiel: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> oder <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (oder gleichwertig, unter Verwendung Ihres Docker-Hosts).
Jetzt können Sie auf <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> oder <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> gehen (oder ähnlich, unter Verwendung Ihres Docker-Hosts).
Sie sehen die automatische interaktive API-Dokumentation (bereitgestellt von <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
## Alternative API-Dokumentation { #alternative-api-docs }
Sie können auch auf <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> oder <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> gehen (oder ähnlich, unter Verwendung Ihres Docker-Hosts).
Sie sehen die alternative automatische Dokumentation (bereitgestellt von <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
## 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.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["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){.internal-link target=_blank} 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 <a href="https://traefik.io/" class="external-link" target="_blank">Traefik</a>, 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 <abbr title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und auf irgendeine Weise zusammenarbeiten.">Cluster</abbr> 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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> 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 <abbr title="Lastverteiler">**Load Balancer**</abbr> 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.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
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 <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Init-Container</a>.
///
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: <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>. Dieses ist jedoch jetzt veraltet. ⛔️
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 <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> verwenden, um Ihr Projekt zu installieren und zu verwalten, können Sie deren <a href="https://docs.astral.sh/uv/guides/integration/docker/" class="external-link" target="_blank">uv-Docker-Leitfaden</a> 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). 😎
Sie können Ihre FastAPI-App in der <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a> 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:
<div class="termy">
```console
$ fastapi login
You are logged in to FastAPI Cloud 🚀
```
</div>
## Deployen { #deploy }
Stellen Sie Ihre App jetzt mit **einem einzigen Befehl** bereit:
<div class="termy">
```console
$ fastapi deploy
Deploying to FastAPI Cloud...
✅ Deployment successful!
🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev
```
</div>
Das war’s! Jetzt können Sie Ihre App unter dieser URL aufrufen. ✨
## Über FastAPI Cloud { #about-fastapi-cloud }
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** 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. 🤓
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 <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a> 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 **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Server Name Indication – Servernamensanzeige">SNI</abbr></a>**.
* 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-<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr>**, 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 **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS-Terminierungsproxy</a>** 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 **<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** 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 <abbr title="Sie ändert sich nicht">feste</abbr> **ö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.
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 **<abbr title="TLS-Handschlag">TLS-Handshake</abbr>** 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).
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).
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.
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 <abbr title="Redirect – Umleitung">Redirects</abbr> 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){.internal-link target=_blank}
///
## 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. 🔒
Das Deployment einer **FastAPI**-Anwendung ist relativ einfach.
## Was bedeutet Deployment { #what-does-deployment-mean }
<abbr title="Bereitstellen der Anwendung">**Deployment**</abbr> bedeutet, die notwendigen Schritte durchzuführen, um die Anwendung **für die Endbenutzer 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.
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, <a href="https://fastapicloud.com" class="external-link" target="_blank">**FastAPI Cloud**</a> 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. ✨
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>2306215</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C
to quit<b>)</b>
```
</div>
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 <abbr title="Asynchrones Server-Gateway-Interface">ASGI</abbr> 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:
* <a href="https://www.uvicorn.dev/" class="external-link" target="_blank">Uvicorn</a>: ein hochperformanter ASGI-Server.
* <a href="https://hypercorn.readthedocs.io/" class="external-link" target="_blank">Hypercorn</a>: ein ASGI-Server, der unter anderem kompatibel mit HTTP/2 und Trio ist.
* <a href="https://github.com/django/daphne" class="external-link" target="_blank">Daphne</a>: der für Django Channels entwickelte ASGI-Server.
* <a href="https://github.com/emmett-framework/granian" class="external-link" target="_blank">Granian</a>: Ein Rust HTTP-Server für Python-Anwendungen.
* <a href="https://unit.nginx.org/howto/fastapi/" class="external-link" target="_blank">NGINX Unit</a>: 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){.internal-link target=_blank} erstellen, sie aktivieren und dann die Serveranwendung installieren.
Zum Beispiel, um Uvicorn zu installieren:
<div class="termy">
```console
$ pip install "uvicorn[standard]"
---> 100%
```
</div>
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-Performanz-Schub 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:
<div class="termy">
```console
$ uvicorn main:app --host 0.0.0.0 --port 80
<span style="color: green;">INFO</span>: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```
</div>
/// 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
frommainimportapp
```
///
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. 🚀
# 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 <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> bearbeiten zu können.
Wie Sie im vorherigen Kapitel über [Deployment-Konzepte](concepts.md){.internal-link target=_blank} 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){.internal-link target=_blank}.
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:
<div class="termy">
```console
$ <font color="#4E9A06">fastapi</font> run --workers 4 <u style="text-decoration-style:solid">main.py</u>
<span style="background-color:#009485"><font color="#D3D7CF"> FastAPI </font></span> Starting production server 🚀
Searching for package file structure from directories with
<font color="#3465A4">__init__.py</font> files
Importing from <font color="#75507B">/home/user/code/</font><font color="#AD7FA8">awesomeapp</font>
<span style="background-color:#007166"><font color="#D3D7CF"> app </font></span> Using import string: <font color="#3465A4">main:app</font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Server started at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font>
<span style="background-color:#007166"><font color="#D3D7CF"> server </font></span> Documentation at <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000/docs</u></font>
Logs:
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Uvicorn running on <font color="#729FCF"><u style="text-decoration-style:solid">http://0.0.0.0:8000</u></font> <b>(</b>Press CTRL+C to
quit<b>)</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started parent process <b>[</b><font color="#34E2E2"><b>27365</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27368</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27369</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27370</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Started server process <b>[</b><font color="#34E2E2"><b>27367</b></font><b>]</b>
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Waiting for application startup.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
<span style="background-color:#007166"><font color="#D3D7CF"> INFO </font></span> Application startup complete.
```
</div>
////
//// tab | `uvicorn`
Wenn Sie den `uvicorn`-Befehl direkt verwenden möchten:
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){.internal-link target=_blank} 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. ✨
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.